Merge upstream main for opt-in Jev dispatch - #27
Merged
Merged
Conversation
* fix(bin): derive passed PR state from PR record A completed no-mistakes run with outcome=passed does not prove the associated pull request merged or closed. A parked gate can be approved on other evidence, so the old crew-state label could report an open PR as merged and make teardown look safe when unlanded work still exists. For passed runs, derive the crew-state detail from the run or task PR identity, accept a matching merge-poll retirement receipt as local merged evidence, and otherwise perform a bounded forge read. If the identity is absent or unreadable, report the run as passed with unknown PR state instead of inventing a merged claim. Fixes kunchenguid#4607 * no-mistakes(review): Add bounded GitLab merge-request state reads * no-mistakes(review): Preserve network-free inactive crew-state scans * no-mistakes(document): Document PR record readers in shared library
kunchenguid#4627) * fix: restore published contribution follow-up (Fixes kunchenguid#4469) * fix(review): Fix contribution freshness and merge actor routing * fix(review): Restore issue triage and scope contribution follow-up * fix(test): test: assert one wake per contribution signal * fix(document): Document contribution follow-up * fix: restore truthful terminal delivery evidence * fix(review): Disclose unsupported contributions and deduplicate watcher wakes * fix(review): Preserve unmeasured unsupported contributions across Bearings * fix(review): Deduplicate shared contribution wakes and isolate diagnostics * fix(ci): Captain, fixed the CI failure by updating the PR-security fake GitHub interface to support the contribution observer’s API reads. Verified with shellcheck, git diff --check, the full contribution suite, and a focused merged-poll retirement reproduction. The full PR-security script was not allowed to complete locally after its expanded observer path made it substantially slower
…nguid#4658) * fix(bin): make a remote-reply document gap self-clearing and re-attemptable A remote mate's undelivered document raised a keyed `blocked` decision that nothing could ever resolve, and any `data/*.md` substring in any mirrored line was an unconditional fetch instruction. A mate announcing a report it had not written yet therefore manufactured a permanent, factually false blocker, and its own explanation of the false alarm manufactured more. The reader has no permanence vocabulary: a report still being written refuses exactly like a path that will never exist. So an undelivered document is now a durable, re-attemptable obligation under `state/remote-replies/<id>.pending-docs`, re-attempted on the next delta and on the channel's own quiet poll, and retired with a matching `resolved` line naming the local copy once it arrives. The cursor still advances and no delta stalls on one bad pointer. Only a structured `report=data/....md` pointer now offers a document, so a path merely mentioned in prose - including one under another home's mirror tree, which is provably not that mate's to serve - is never fetched. Offers are deduplicated across the whole delta, the escalation names each missing document once and carries the reader's own reason instead of discarding it, and a strictly increasing notice ordinal keeps a later escalation from being swallowed as duplicate bytes. A mirrored line still lands once whichever pointer form it was first written under. * no-mistakes(review): Require structured pointer token boundaries * no-mistakes(review): Unify boundary-safe pointer extraction and rewriting * fix(bin): identify a mirrored line independently of its delivery state Two defects in the boundary-safe pointer work. The at-most-once check compared only the all-remote and all-local renderings of a line, so it could not recognize a mixed one. A line offering two documents where only the first was deliverable mirrored as local-plus-remote; once the second arrived, a cursor-loss whole-log recapture rendered the same line all-local, matched neither alternate, and mirrored a second time. A line's identity is now the canonical form every boundary-valid pointer would take once delivered, derived by the same parser that does extraction and rewriting, so it no longer depends on which documents happened to be deliverable at the time. The pointer map was passed to awk through the process environment. A delta may carry up to the configured 1 MiB bound, and an expanded map of delivered pointers can exceed the platform's exec argument limit, so awk would fail to start; because no caller checked, the empty result would have been appended as blank lines while the cursor advanced past dropped status content. The map now travels in a file, and every call site checks the exit status and stops the ingest rather than committing a delta it could not render. Both passes now run once per stream instead of twice per line. * no-mistakes(review): Abort ingest when document pointer extraction fails * no-mistakes(review): Exclude structured cross-home pointers from document transfer * fix(bin): fail open on an undeliverable remote document instead of tracking it Narrow the remote-reply document fix to the scope the diagnosis actually requires, as decided after measuring a simpler alternative. A document the reader cannot deliver now fails open. The mate's line is mirrored with its own pointer, the cursor advances, and one unkeyed note carries the reader's reason. A note never enters the open-decision fold, so it cannot stand open the way the original keyed block did - which removes the never-clearing false blocker by construction rather than by resolving it. That makes the durable self-clearing obligation unnecessary, so it goes: the per-mate pending-documents record, its notice ordinal and resolved announcements, and the poll-side retry. Canonical line identity goes too, and with it a way to silently drop a genuine status line; mirroring is back to at-most-once on exact bytes. The cross-home exclusion goes as well: under fail-open a cross-home report= either fails harmlessly or is a nested remote report this mate genuinely holds, which is now relayed again. Kept: fetching only on a structured report= pointer, the boundary-correct parser, the file-based rewrite map, and checked extraction and rewrite exit status. The parser now scans behind a sentinel byte so a rejected candidate can no longer give the text right after it a false leading boundary. The reported incident is covered end to end: a report path announced in prose before it exists raises no decision, and the report still arrives through the ledger publisher's structured offer once written. * no-mistakes(review): Preserve source-line identity across remote reply replays * no-mistakes(document): Document remote reply transfer and replay semantics * no-mistakes(lint): Fix staging truncation lint checks
* Preserve substantive Calm mid-turn text * no-mistakes(review): Distinguish newline-preserved replies from short narration * no-mistakes(document): Document Calm mid-turn preservation boundaries * no-mistakes(ci): Fixed the flaky contribution watcher test by increasing its bounded checkpoint from 5 to 15 seconds, allowing diagnostics to surface under slower CI load. Verified with `bash tests/fm-contributions.test.sh` and `git diff --check`
…#4656) * fix(bin): re-record PR poll identity after a volume device renumber (Fixes kunchenguid#4260) A volume remount can renumber the state filesystem's st_dev while every inode and byte stays the same; APFS does this across a reboot. A poll registration records its sidecar and check as device:inode, so every poll armed before the remount failed strict validation and the watcher refused all of them as unauthenticated state checks until each was re-armed by hand. There are two device comparisons. fm_pr_private_file_valid compares a live file's device with the state directory's device read in the same invocation: it refuses a file that is not on the state directory's own filesystem and already survives a renumber, so it is unchanged. The registration's recorded identity versus the live identity (from kunchenguid#556, reused by the kunchenguid#932 retirement receipt) binds the registration to the exact files published in its own transaction; its device part is what breaks. When strict capture fails, the watcher now proves the device is the only difference: every other artifact check passes (template bytes, both hashes, private mode, single link, live device, metadata), both recorded identities name one device, and each recorded inode equals its live inode. Only then, under the task's control lock, does it rewrite the two identity lines, repeating the whole proof and comparing the registration's file identity and bytes just before the rename, and then capture strictly again. A swapped, altered, re-moded, relinked, split-device, or foreign-device artifact still fails a proof and is still refused, and a pending retirement receipt blocks the rewrite. Reproduction: on macOS a poll armed on an APFS disk image that was detached and re-attached behind another image moved st_dev 16777239 -> 16777243 with inodes, bytes, mode, and link count unchanged; the real watcher refused it on main and reports its merge with this change. The portable regression test rewrites a real registration's recorded device and drives the watcher. Not changed here: the status presentation cursor keys rows by its own device:inode identity in bin/fm-classify-lib.sh, a different helper that needs its own fix; a retirement receipt left by a reboot between its publication and removal still names the old device and stays refused; custom check trust binds only a content hash and is unaffected. * fix(review): Serialize PR poll publication writers * fix(review): Bound PR poll publication lock scope
…llow-up to kunchenguid#4627) (kunchenguid#4661) A budget that expires partway through an observation no longer records an error or prints the unavailable wake; the URL keeps its prior record and is observed first next poll. forge() flags budget exhaustion at the point it refuses, or when a read is killed at the budget's own deadline, so a genuine forge failure still records the error and wakes. Each distinct URL is now observed once per poll and applied to every owning task.
…kunchenguid#4680) * fix(bin): clear parent pending-replies on local secondmate retirement Local secondmate teardown left resolved parent pending-reply records behind after home removal (seen after papa-hdds / pxmx retirement). Refuse non-forced retirement while any reply for that id is still unresolved, and delete every matching record plus its delivery confirmation after a successful local or remote retirement, matching the remote cleanup path. * no-mistakes(document): Align secondmate retirement docs with pending-reply cleanup * no-mistakes(review): Lokale Pending-replies-Sicherheitsprüfung vor Home-Entfernung * no-mistakes(review): Pending-replies-corr_id auf 16-Hex absichern * no-mistakes(review): Pending-replies Basename und corr_id abgleichen * no-mistakes(document): Clarify forced retirement pending-reply cleanup --------- Co-authored-by: ladwein <ladwein@firstmate.bost8.thelad.loc>
kunchenguid#4677) * fix(bin): accept Orca's composite worktree id at teardown Teardown refused every Orca-backed task because the endpoint validator checked orca_worktree_id with the simple-atom rule meant for tmux-style window names, which rejects any character outside [A-Za-z0-9._@%+-]. Orca returns that id as `<orca id>::<absolute worktree path>`, so the colon and slashes in every real value made validation fail and finished Orca tasks could never be cleaned up. Validate the field as the composite it is: both halves of the first `::` split present, the path half absolute, and no embedded newline, carriage return, or tab. The terminal field keeps the atom check, which is correct for it, and no other backend's validation changes. The existing Orca fixtures recorded ids like `wt-teardown`, a shape Orca never returns, which is why the suite passed a check the real value fails. They now carry the composite form, so the tests exercise the real value. * no-mistakes(document): name Orca's repo id in the composite worktree id * no-mistakes(document): list teardown endpoint safety suite in Orca regression entry points
* feat(bin): add opt-in typed dispatch resolution through typesafe.ai Add bin/fm-dispatch-resolve.sh, which resolves one concrete crewmate or scout profile from a written brief with typesafe.ai's System One model: one Choice question over the rules' `when` texts, then the confidence floor, the rule's `approval` and `floor`, each profile's `provider` and `floor`, one quota-axi snapshot, and the spendPriority argmax all in code. It is off unless TYPESAFE_API_KEY is in the environment or the home's gitignored .env; off means one stderr line, exit 0, and no network call, so firstmate dispatches exactly as before. The key reaches curl on a file descriptor, never argv. Extract fmx_env_get into bin/fm-env-lib.sh as the one .env accessor and the harness-to-provider table into bin/fm-quota-axi-lib.sh so the new tool and bin/fm-quota-choose.sh share one owner each. Bootstrap validates the four new optional dispatch fields. Document the schema, the operator contract, the AGENTS.md intake step, and the live and benchmark evidence. * no-mistakes(review): Harden typed dispatch resolution and quota bounds * no-mistakes(review): Validate dispatch floors and ranking evidence * no-mistakes(review): Tighten dispatch response and floor evidence * no-mistakes(review): Neutralize none matching and resolve defaults locally * no-mistakes(review): Preserve providerless profiles outside typed resolution * no-mistakes(review): Validate response usage and reject duplicate profiles * no-mistakes(review): Escalate unverifiable floors and validate probabilities * no-mistakes(review): Validate probability mass and unknown profile floors * no-mistakes(review): Simplify resolver interface and preserve fallback routing * no-mistakes(review): Fix constants and rank partial quota evidence * no-mistakes(review): Add authoritative provider mapping and enforce explicit providers * no-mistakes(review): Declare provider for documented Pi profile * no-mistakes(review): Validate provider identifiers and support Gemini dispatch * no-mistakes(review): Strictly anchor provider identifiers * no-mistakes(review): Validate selectors and preserve fallback candidate evidence * no-mistakes(review): Gate typed validation and harden resolver evidence * no-mistakes(review): Preserve opt-in routing and harden candidate evidence * no-mistakes(review): Prioritize known exhaustion over quota uncertainty * no-mistakes(review): Isolate API secrets and preserve no-key diagnostics * no-mistakes(review): Fallback safely when dispatch rules are absent * no-mistakes(review): Prioritize quota vetoes and isolate bootstrap secrets * no-mistakes(document): Document typed dispatch safety and fallback behavior
…n decisions aren't lost (kunchenguid#3753) * test: reproduce buried status declarations in shared readers * fix: share status event reads and preserve open blockers * fix: retain terminal scout and ship status declarations * no-mistakes(review): Fix status chronology, legacy completions, and reader performance * no-mistakes(review): Share terminal decision reconciliation across fleet snapshots * no-mistakes(review): Unify terminal supersession across cached folds and consumers * no-mistakes(review): Filter per-key status history while preserving terminal chronology * no-mistakes(test): Preserve parent lock ownership in Bash 3.2 subshells * no-mistakes(review): Anchor legacy status tokens so prose cannot hide pauses * no-mistakes(document): Document latest-event status read and kind-scoped fold cursor * no-mistakes(lint): Quote literal done in test for-lists for SC1010 * ci: expect 19 snapshot/fleet-view tests This branch adds a fleet-snapshot regression, so the stock macOS Bash lane's hardcoded guard of 18 'ok - ' lines fails on the new count. Bump the guard and its message to 19. * no-mistakes(review): Restore multiline child outcome reporting * no-mistakes(review): Select ledger terminal events through bounded shared reader * no-mistakes(review): Report newest open decision instead of preferring blocked * no-mistakes(review): Require colon before ship/scout terminal supersession in fold * no-mistakes(review): Gate socket-down override on latest event; drop lock matrix * no-mistakes(review): Fold only colon-bearing or keyed lines as decision transitions * no-mistakes(review): Pre-select candidate lines before per-key closing-verb fold * no-mistakes(test): Update fleet-view expectations to newest-open-decision rule * no-mistakes(document): Align status-read docs with fold-resolved crew state * no-mistakes(document): Correct status-reader contracts in classify-lib and crew-state headers * no-mistakes(ci): Greptile P1 (bin/fm-crew-state.sh:729, "Stale socket blocker survives") was a real defect introduced by commit b7c2183 on this branch, and is fixed. Root cause: the daemon-socket-down override took its verb check from `last_status_line "$LOG"` but its evidence and emitted detail from `$LOG_LINE` (status_current_line = the fold's newest still-open decision). Those are different lines whenever a later recognized `blocked:` event is one the decision fold declines. Reproduced by sourcing bin/fm-classify-lib.sh on `blocked: no-mistakes daemon socket is missing` followed by `blocked [key=pending-reply-t3]: still waiting on the answer` (reserved-namespace key whose note does not speak that vocabulary, so _fm_decision_key_transition_allowed rejects it): open set still holds the socket blocker, last_status_line returns the newer line, its verb is blocked, so the gate passed and the stale daemon-down evidence overrode a healthy attributed run. Fix (bin/fm-crew-state.sh): capture LOG_LATEST=$(last_status_line "$LOG") once and read verb, socket-down evidence, and the emitted note all off that same line, so the override fires only while the socket-down declaration is itself the log's latest recognized event — preserving the narrow override the prior round's user instruction asked for. Comment updated to state that contract. No new machinery; the two-line conflation was removed rather than papered over. Regression: extended tests/fm-crew-state.test.sh:test_socket_refusal_override_expires_when_the_crew_moves_on with the reproduced sequence, asserting the run-step reading (state: working, source: run-step) and absence of the override detail. It fails before the fix ("not ok - a later unfolded blocked event also hands the reading back to the run (missing: 'state: working')") and passes after. Verified locally: tests/fm-crew-state.test.sh, tests/fm-fleet-snapshot-view.test.sh, tests/fm-classify-decision-key.test.sh, tests/fm-watch-triage.test.sh, tests/fm-captain-hold-lifecycle.test.sh all pass; bin/fm-lint.sh (shellcheck 0.11.0 + actionlint) exits 0. Changes left uncommitted in the worktree * test: fold terminal-cleanup snapshot coverage into the completed-scout case Keep the ship/scout/secondmate supersession assertions without adding a nineteenth top-level fleet-view test, so CI can stay at the upstream suite count. * no-mistakes(document): Clarify socket-down override expiry in architecture doc * ci: retrigger flaky contribution check
…nchenguid#4689) * fix(spawn): launch codex crewmates with codex's hook layer disabled A freshly launched Codex worker never reached its instructions. Codex stopped it on an interactive "Hooks need review" modal whose selection sits on "Review hooks", which is neither trusting nor declining. Firstmate's key plane carries only Enter, Escape and Ctrl-C with no arrow navigation, so the selection cannot be moved, and pre-accepting the prompt by writing Codex's own trust store would record an operator consent that was never given. The hooks are the machine's own ~/.codex/hooks.json plus any project's .codex/hooks.json. A crewmate needs neither: its turn-end signal is the -c notify= program on the same launch, and Firstmate's project hooks are primary-session infrastructure that stands down in a child worktree. Crewmate and scout launches now pass --disable hooks. That is the opposite of --dangerously-bypass-hook-trust, which RUNS the untrusted hooks; disabling the feature runs none of them and leaves the operator's ~/.codex untouched. An unknown feature name is a hard Codex error, so a release that drops the flag fails the launch loudly instead of silently restoring the modal. A secondmate is a primary in its own home and keeps the project hooks its turn-end guard and session-start digest ride on. Verified on codex-cli 0.151.0: the modal is gone and the turn-end notification still lands. This unblocks the second review that every finished pull request is supposed to get. Fixes kunchenguid#4673 * no-mistakes(review): Fix contradictory hook count in Codex verification record
Integrate upstream fa93097 while retaining the fork's Vault, lifecycle, Orchestra, lock, and repair behavior. Keep lifecycle filtering in contribution-call presentation with explicit suppression counts and per-home identity, without changing contribution storage, polling, or coverage totals. Declare the new Bearings surface and refresh fork-only test timing hints from green CI measurements.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merge upstream Firstmate through
fa93097162d16f70a070044b8ccece037a38e3e6into the fork to make opt-in Jev dispatch resolution available while preserving the fork's integrations.The fork parent is
bd00258d1c3c9db5ca99793d63517e9e47efe296.This brings in 11 upstream commits while retaining the 25 fork-only commits and their history.
Use a merge commit when landing so future synchronizations retain upstream ancestry.
The upstream tip contains
69d660ad6167271daf09e8c5521581c03cb9a4f8, the merged typed dispatch implementation, includingbin/fm-dispatch-resolve.sh,tests/fm-dispatch-resolve.test.sh, anddocs/verification/dispatch-resolve.md.The preservation precedent is the previous fork synchronization.
No real API key, running-home configuration, benchmark, or authenticated Jev request is involved in this change.
Fork-only commit classification
Classification is against the exact upstream tip above, based on
git log upstream/main..origin/main, the patches, the previous synchronization's preservation record, and the remaining fork-versus-upstream diff.No fork-only commit is an exact already-upstreamed patch (
git cherryfinds no equivalent patches).The Pi renderer change below is superseded in behavior; its separate cleanup safeguard remains.
4c8d97bdcmux failed-list guardfm_backend_cmux_surface_exists.e31fd313review-diff branch resolutionfm/<id>.8d304c6cHerdr capability probe259be8e0Claude session identity105ff038hosted Codex lockscodex:<thread-id>ownership and conservative reclamation alongside numeric owners.ae77b98dlegacy Herdr binding repairfm-work-landed-lib.shreachability predicates.045e5382tasks-axi compatibility gatesd0f820d7Bitwarden migration tooling30705e93Bitwarden rotation boundary1af5ea04Automic Vault worker authentication3294f656Vault development-only boundaryc3b6535bproject lifecycle registrye353d9bdsole Vault settings160c812bVault settings invariant documentation20240d73Vault verification documentation71522695Vault filtering regression663a5f68Herdr/Vault verification consolidation99839a98Bearings versioned contract--contract; declare the new upstream contribution surface so the contract remains accurate.cdd5462aPi renderer baseline0a14719cOrchestra live-board refreshf50a7997lifecycle across Bearings projections4c06f118prior upstream integration merge67b3e863previous pipeline integration fixes46d09085changed-test asset mappingbd00258dprior synchronization landing mergeExact conflict inventory
bin/fm-bearings-snapshot.shbin/fm-fleet-snapshot.shbin/fm-test-run.shAGENTS.mdmerged automatically without manual edits.The required
fm-ensure-agents-md.sh .helper reported it unchanged.The upstream resolver, its fixture suite, and its verification record remain byte-identical to the fetched upstream tip.
Three integration corrections supplement the conflict resolutions:
lifecycle_suppressedand an explicit omission line disclose suppressed calls. Secondmate summaries carry their owning project identity, and older summaries without enough identity retain their calls rather than guessing from the parent registry. This is a fork presentation adaptation, with no change to upstream contribution storage or polling.contributionsobject in the fork's machine-readable Bearings contract. The executable snapshot previously emitted an undeclared surface; the existing contract regression guards this boundary.Enable the resolver after landing
The operator contract is docs/configuration.md, Typed dispatch resolution (.env TYPESAFE_API_KEY).
After the running home has adopted this merge, the owner can privately add a nonempty
TYPESAFE_API_KEY=<owner-supplied key>line to/Users/rega1011/Projects/firstmate/.env, retaining existing entries, and set that file to owner-only read/write mode withchmod 600 /Users/rega1011/Projects/firstmate/.env.Mode 600 is the recommended secret-file permission, not an activation condition enforced by the resolver.
Alternatively, supply the variable in the firstmate process environment; a nonempty environment value takes precedence.
No additional enable flag is needed.
The resolver reads the home-local key on each invocation, and updated
AGENTS.mddirects firstmate to invoke it immediately after writing a task brief.The key is not propagated automatically to other homes by this merge.
Without a key, the tool prints
dispatch-resolve: offon stderr, emits nothing on stdout, exits 0, and makes no request.Only
clearemits a spawn profile; off, ambiguous, escalation, and error outcomes return to the existing intake.An existing malformed configuration remains an actionable exit 2.
The tool does not replace firstmate's catalog/authentication, reasoning-class, completion-runway, or approval judgment.
Existing crew-dispatch.json migration checklist
docs/configuration.md, Crew dispatch profiles owns the schema.
There is no schema-version bump and single-profile objects remain supported.
These changes apply when typed resolution is opted in; without a key the new declarations remain inert.
provider, in both ruleuseand top-leveldefaultpi,pi-signed,omp,opencode,gemini, androvo; omission is an error before any request. The single-provider defaults areclaude -> claude,codex -> codex,grok -> grok,kimi -> kimi,cursor -> cursor,agy -> agy, andmuse -> meta. Explicit IDs must match^[a-z0-9]+(-[a-z0-9]+)*\z. Do not infer a multi-provider model's family from its harness.approval"captain". Declare it for rules requiring approval so the tool escalates instead of emitting a profile. Approval prose inwhenorwhyalone is not an executable gate.floorprovider, nonemptyscope, and numericmin_percentfrom 0 through 100. A known shortfall falls back todefault; missing/unknown evidence escalates.floorscopeand numericmin_percentfrom 0 through 100. It must not containprovider; it uses the profile provider. A known shortfall excludes the candidate, and unknown evidence makes it unrankable.useordefaultarray, duplicate(harness, model, effort)tuples are errors, even if provider or floor declarations differ.when, nonemptyuse, valid verified harnesses, and model/effort strings supported by that harness/model. Ifselectis present it must be"quota-balanced"; no new selection modes are added. Typed mode additionally recognizesgemini, with explicit provider.rulesyieldsescalate: no rules to matchwithout a model/quota request; ordinary intake selects the default. A missing optional file behaves the same, while malformed/unreadable files and broken symlinks error.Natural-language match conditions and exemptions stay in
when.The model sees those conditions, the brief, and the project name; it does not see
why,use, quota, or approval declarations.The live rules file was not inspected or changed during this synchronization.
Post-merge verification
Before relying on typed dispatch, firstmate will exercise authenticated resolution in the running home with the owner's real key after merge, including a clear concrete harness/model/effort result and the never-auto-dispatch guards for low confidence, required approval, and quota restrictions.
This synchronization deliberately leaves those authenticated scenarios untested: it cannot read or use the real key.
The upstream fixture suite covers those guards, and direct isolated CLI checks demonstrated the absent-key and no-rules fallbacks.
Firstmate accepted no-mistakes finding
test-2with that verification boundary; no live credential test was attempted to clear the gate.Noted upstream follow-ups
local-secondmate-pending-cleanup-after-home-removal(bin/fm-teardown.sh:3409, severity error): local secondmate retirement removes its home before deleting pending-reply records, so a subsequent cleanup failure can retain a route to a missing home. The review proposed transactional staging with restore-on-failure. Firstmate confirmed this ordering is byte-equivalent upstream behavior, not introduced by this merge; the fix is deferred because changing upstream behavior is outside this synchronization. The completed review was accepted without applying that finding.Validation
CI=true bin/fm-lint.shpassed with ShellCheck 0.11.0 and actionlint 1.7.12; final local lint, stock Bash syntax, documentation audience/link validation, shard coverage, andgit diff --checkpassed.fm-session-start.sh --help,fm-spawn.sh --help, andfm-dispatch-resolve.sh --helppassed.dispatch-resolve: offon stderr.fm-pi-branch-extension.test.shin serial shard 3 at the sequence-bound acknowledgement case (no durable outcome for claimed wake rows). Merge CI must independently pass; neither baseline is represented as a complete green run..agents/skills/quota-array-dispatch/SKILL.md: it now points to the resolver-owned gates and limits rather than claiming that the resolver implements every skill gate. Final head isa9c053ffc423015b8178419120f42f0db331b490; all hosted CI jobs passed in run 35198243559, and no-mistakes returnedchecks-passed.The hosted portable timing aggregate records 204 scripts, zero failures, and 34 explicit optional-platform or opt-in skips.
The resolver, contribution, Bearings, and Pi branch extension suites all passed without gate skips; the Pi branch extension test that failed on the upstream parent passed on this merge.
The separate required Herdr lane, both parallel lanes, all five serial lanes, lint, invariants, coverage, macOS Bash compatibility, and timing aggregation are green.
The generated pipeline report follows unchanged, including its original risk assessment.
The decisions in "Noted upstream follow-ups" and "Post-merge verification" above explain the accepted findings and their scope.
Intent
Instruction of 2026-09-17: "i'd like to implement Jev for model selection as described by kunchenguid in this X post: https://x.com/kunchenguid/status/2100477276945367454?s=20 I already have an API key for Jev, can you help me set this up?"
Context needed to read that ask: the capability described is upstream kunchenguid/firstmate pull request 4692, "feat(bin): add opt-in typed dispatch resolution", merged to upstream main on 2026-09-17. It adds bin/fm-dispatch-resolve.sh, which takes a written task brief, asks typesafe.ai's System One model (Jev) one Choice question over the natural-language rules in config/crew-dispatch.json, then applies a confidence floor, approval and quota gates, and one quota-axi spendPriority ranking to print the concrete harness, model and effort for the spawn. It is opt-in: it activates only when TYPESAFE_API_KEY is present in the environment or in the firstmate home's gitignored .env, and firstmate routes exactly as today when the key is absent or the result is not clear. This fork (origin = https://github.com/rega10/firstmate, the running Firstmate) is 11 commits behind upstream main and 25 ahead, so the capability arrives by bringing the fork up to date with upstream main while keeping the fork's own integrations working. The API key itself is the fork owner's to place and is not part of this task.
What Changed
TYPESAFE_API_KEY, with confidence, approval, quota, and spend-priority gates plus fallback to existing routing.Risk Assessment
🚨 High: The update is otherwise well-bounded, but the new cleanup ordering introduces a destructive, source-verifiable failure path that can leave local secondmate retirement permanently inconsistent.
Testing
Inspected the target diff and credential availability, captured direct CLI evidence for absent-key and no-rules fallback behavior, and ran the focused resolver, bootstrap, and spawn-profile suites successfully. No UI surface was involved, so CLI transcripts are the appropriate reviewer-visible evidence. A real Jev call could not be exercised without TYPESAFE_API_KEY.
Evidence: Live no-key fallback transcript
Source: Live no-key fallback transcript
exit=0; stdout empty; stderr reports typed dispatch is off because TYPESAFE_API_KEY is absent.Evidence: Live no-rules fallback transcript
Source: Live no-rules fallback transcript
exit=0; status=escalate; reason=no rules to match.Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
⏭️ **Rebase** - skipped
Step was skipped.
bin/fm-teardown.sh:3409- Local secondmate retirement removes the home before deleting its pending-reply records. If that later cleanup fails, for example becausestate/pending-repliesis readable but not writable, teardown exits while preserving the registry route and metadata even though the home is already gone; a rerun then cannot pass home validation, leaving an unrecoverable half-retired route. Fixing this safely requires authorization for transactional staging: stage the matching records before home removal, restore them if removal fails, and commit their deletion only after removal succeeds.Credential presence check for the process and worktree without printing secret valuesenv -u TYPESAFE_API_KEY FM_HOME=<isolated-home> bin/fm-dispatch-resolve.shTYPESAFE_API_KEY=<nonempty test value> FM_HOME=<isolated-home-with-no-rules> bin/fm-dispatch-resolve.sh <brief> --project demobash tests/fm-dispatch-resolve.test.shbash tests/fm-bootstrap.test.shbash tests/fm-spawn-dispatch-profile.test.shgit status --shortconfirmed testing left the worktree clean✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.