Skip to content

feat: sync latest upstream while preserving fork features - #11

Open
bramdokman wants to merge 356 commits into
mainfrom
fm/fm-upstream-sync
Open

bramdokman wants to merge 356 commits into
mainfrom
fm/fm-upstream-sync

Conversation

@bramdokman

Copy link
Copy Markdown
Owner

Intent

Sync this fork of firstmate (bramdokman/firstmate, origin) with the latest upstream (kunchenguid/firstmate, upstream) by producing ONE branch whose tip is a TRUE MERGE COMMIT of upstream's latest main into our main, with zero loss of fork features.

The captain's words: "make sure firstmate is updated to the latest version from kun ... sync upstream with our own version ... but make sure the quota display and syncing is kept intact."

WHY A TRUE MERGE: fork PR #9 (bdef528) was SQUASH-merged, so it imported upstream content with no upstream ancestry and left the recorded merge-base three months stale (114 phantom conflicts). This branch restores real ancestry so future syncs are ordinary merges. Merge commit 9edd13d has parents bdef528 (origin/main) and af1f2ea (upstream/main), resolved using fc3684a as the effective base, which yields 15 real conflicts. The resulting tree differs from upstream/main in exactly 18 paths.

RESOLUTION POLICY, which is the acceptance criterion for this change: upstream wins everywhere, EXCEPT where a fork feature that upstream lacks is ported onto upstream's version of the file, including that feature's tests, documentation lines, and any registry, CI, shellcheck or documentation-audience list that must enumerate a fork-only script.

Fork features and their required outcome:

THE CAPTAIN'S EXPLICIT CONDITION - quota display and syncing must stay intact:

  • bin/fm-spawn.sh must keep accepting a RAW LAUNCH COMMAND prefixed with CLAUDE_CONFIG_DIR=/.claude-acctN or CODEX_HOME=/.codex-acctN, deriving the harness by skipping leading VAR=value words, with the placeholders MODELFLAG EFFORTFLAG OPINPUT BRIEF TURNEND all still substituted.
  • The local config/crew-dispatch.json must stay valid under the merged validator.
  • bin/fm-quota-axi-lib.sh and the quota-array-dispatch skill must keep working together.
  • Nothing may close, rename or reuse non-firstmate Herdr panes, write into ~/.local/bin, edit systemd units or shell rc files.

REVIEW FOCUS AND EXCLUSIONS, which bound what this change is accountable for:
The diff is overwhelmingly upstream code that upstream already reviewed and shipped. Review must focus on the conflict resolutions and the fork-feature ports listed above. Upstream code must NOT be rewritten. A pre-existing defect that reproduces identically on plain upstream/main is not a defect of this change. Generalizations, consistency sweeps and extra hardening the captain did not ask for are follow-up work, not scope.

A finding that would change upstream behaviour beyond what a fork port requires must be escalated as an ask-user finding rather than applied.

The merge commit's upstream parent is load-bearing and must survive: this branch must not be squashed, rebased, or otherwise linearized in a way that drops the upstream parent. Fix-round commits stacked on top of the merge commit are fine. It will be landed with a merge commit, never a squash.

One follow-up commit on top of the merge (a25e2c7) makes the fork-ported endpoint-abort arm in bin/fm-spawn.sh report the outcome that actually happened instead of unconditionally claiming the endpoint was closed, and registers measured serial duration hints for the two fork-only test suites in bin/fm-test-run.sh.

What Changed

  • Merge the latest upstream main with full ancestry, bringing in the current runtime, harness, supervision, Calm mode, voice relay, and fleet-management capabilities.
  • Port fork-specific delivery evidence, merged-issue verification, landed-work teardown handling, and pre-launch task bootstrapping onto the upstream implementations.
  • Preserve quota-aware raw launch commands, model and effort placeholder substitution, crew-dispatch validation, and quota-array dispatch behavior.

Risk Assessment

✅ Low: The focused fork-feature ports and fixer follow-up are consistent with the stated intent, the prior brief-fallback defect is correctly resolved, and the required true merge topology and upstream-verbatim exclusions remain intact.

Testing

The prior payload established live passes for the issue-closure help and a real merged GitHub PR. Its other reported passes were non-live regression or repository checks and therefore remain untested under the live-validation contract; the dedicated host quota E2E was also untested because its explicit opt-in was unavailable.

  • Live validation: ⚠️ inconclusive - 2 of 11 scenarios driven live against the product
Scenario Result Live Evidence
A user reading issue-closure help sees the same brief-fallback policy the command implements ✅ pass live fm-issue-closure-help.log shows the executable help names PR-body and GitHub references as primary and the task brief as fallback.
A stale task-brief reference cannot supplement an explicit PR-body closing reference ⏸️ untested no The prior payload cited an isolated behavioral regression transcript and explicitly recorded live=false; it did not establish this result against the live product.
Issue closure verification handles a real merged GitHub PR whose referenced issue is closed ✅ pass live fm-issue-closure-live.log records merged upstream PR 4627, its real closing reference to issue 4469, and a silent successful product exit.
Issue closure verification reports actionable failures without blocking teardown ⏸️ untested no The prior payload cited executable regression coverage but explicitly recorded live=false; it did not establish this behavior against the live product.
Direct-PR and promoted worker delivery contracts preserve evidence requirements ⏸️ untested no The prior payload cited a delivery-contract regression transcript but explicitly recorded live=false; it did not establish this result against the live product.
Fresh ordinary worker launches bootstrap their project environment and refuse failed bootstrap ⏸️ untested no The prior payload cited a task-bootstrap regression transcript but explicitly recorded live=false; it did not establish this result through a live worker launch.
Quota-account-prefixed raw launch commands retain harness and profile routing ⏸️ untested no The prior payload described spawn commands exercised in isolation and explicitly recorded live=false; it did not establish routing against the live product.
Teardown accepts already-landed or absent endpoints while preserving unrelated runtime targets ⏸️ untested no The prior payload cited an isolated teardown regression transcript and explicitly recorded live=false; it did not establish endpoint safety against the live product.
Fleet Bearings completes at realistic backlog size without losing bounded output ⏸️ untested no The prior payload cited a regression transcript using a large backlog but explicitly recorded live=false; it did not establish this result against the live product.
The branch preserves true upstream ancestry and leaves explicitly superseded implementations upstream-verbatim ⏸️ untested no The prior payload cited repository ancestry and file-comparison checks and explicitly recorded live=false; it did not establish a live product result.
Quota array dispatch works against the host's real account and quota tooling ⏸️ untested no The dedicated live E2E requires explicit FM_QUOTA_ARRAY_DISPATCH_LIVE_E2E=1 opt-in and access to host quota/account tooling; provide that opt-in in an authorized isolated quota environment to drive…
Evidence: Issue-closure help output

Source: Issue-closure help output

$ bin/fm-issue-closure.sh --help
Best-effort post-merge verification that the issues a merged GitHub PR was
meant to close are actually closed, reporting - never auto-closing - any
GitHub issue left open despite a closing reference.

GitHub silently ignores some closing keywords even when the PR body carries a
verified one, so the work lands on the default branch and the issue stays open
with no error or signal. This re-derives the candidate issues the PR was meant
to close, checks each candidate's state, and prints a clear report naming the
PR and every still-open issue. It never closes anything: closing an issue on
inferred evidence is an outward-facing human decision, and a wrong auto-close
is worse than an open issue.

It is best-effort and never blocks its caller: it always exits 0. A lookup
failure (network down, gh error, missing issue) is reported on stderr and the
merge is left unaffected. GitLab merge requests are out of scope and exit
silently; the measured failure mode is GitHub-specific.

Candidate issue numbers are unioned and deduplicated, scoped to the PR's own
repository, from the PR body and GitHub's references:
  - the PR body, parsed for GitHub's closing-keyword grammar
    (close[sd]|fix(es|ed)?|resolve[ds]), optionally followed by a colon, then
    #N, owner/repo#N, or a full issue URL. GitHub may have silently ignored
    exactly these keywords.
  - GitHub's closingIssuesReferences for the PR, which captures references
    GitHub linked from commit messages even when the PR body is bare.
  - the task brief (--brief <path>), parsed with the same grammar, as a fallback
    for work whose PR body carried no parseable reference at all.
Issues and pull requests share one number space, so a candidate that resolves
to a pull request is skipped silently: it is not an issue to verify.
Usage: fm-issue-closure.sh <pr-url> [--brief <brief-path>]
Evidence: Issue-closure behavioral regression transcript

Source: Issue-closure behavioral regression transcript

$ tests/fm-issue-closure.test.sh
ok - a merged PR that left its issue open produces a report naming the PR and issue
ok - a merged PR whose issue did close produces no noise
ok - a merged PR with no closing reference produces no noise
ok - a PR that is not merged is not verified
ok - failure of the issue lookup itself is reported and does not break the post-merge path
ok - a merged PR closing several issues reports only the ones still open
ok - a per-issue lookup failure is warned about and does not stop the rest of the check
ok - GitHub's closingIssuesReferences supply candidates the body lacks
ok - the task brief supplies candidates the PR body lacks
ok - the task brief is only a fallback when the PR body has no reference
ok - a brief without a closing-keyword reference produces no noise
ok - a GitLab merge request is out of scope and stays silent
ok - prose containing keyword substrings is not mistaken for a closing reference
ok - cross-repository closing references are not resolved against this PR's repo
ok - owner/repo#N and full issue-URL closing references are recognized
ok - the colon keyword form 'Fixes: #N' is recognized without loosening the word boundary
ok - a closing reference to a PR number is not reported as an open issue
ok - a malformed PR URL is reported and exits zero
ok - a discrepancy report is never an error exit (never blocks teardown)
Evidence: Real GitHub issue-closure verification

Source: Real GitHub issue-closure verification

$ gh pr view https://github.com/kunchenguid/firstmate/pull/4627 --json state,body,closingIssuesReferences
{"body":"## Intent\n\nUpstream issue https://github.com/kunchenguid/firstmate/issues/4469, \"Bug: nothing tells the captain whether a published contribution still needs them, so the outward surface is tracked by hand\", is triaged `ready-for-pr` with `contract-class=restore`, still reproduces on `main`, and has no competing work in flight.\n\nThe reported problem is that a published contribution has to be chased by hand to learn whether it still needs its owner. The only fact that matters for each one is whether anything is left to do on it, and the same signal is wanted for filed issues reaching `ready-for-pr` and for incoming comments, because it is what planning runs on.\n\nThe issue asks for three pieces:\n\n1. Bind a recorded maintainer verdict to the version it judged, and present it as stale as soon as that version moves.\n2. Classify every published contribution by required actor - captain, fleet, maintainer, nobody - from evidence already available. Surface only the captain set; state the others as counts.\n3. Treat an incoming outward signal as a wake: a new maintainer comment or review on a contribution the fleet owns, and a triage label reaching `ready-for-pr` on an issue overlapping filed work.\n\nThe principle the rest depends on, stated in the issue itself: silence must be proven, not merely empty. \"Nothing needs your hand\" has to be a statement measured over a known set, otherwise it is indistinguishable from \"nothing was looked at\".\n\nThis is a `restore` change. It re-establishes an honesty already promised by the existing durable records, wake path, and Captain's Call surface, and introduces no new captain-facing surface. All three pieces ship together, so the title carries `Fixes` because this PR does close the issue.\n\n## What Changed\n\n- Add a durable observer for task-owned GitHub pull requests and linked issues, including exact-head maintainer verdicts, freshness checks, actor classification, and measured coverage.\n- Feed contribution coverage into fleet and Bearings snapshots, surfacing captain-owned follow-up while reporting other actors, stale verdicts, and unmeasured coverage as counts.\n- Arm the authenticated contribution check with PR registration and emit deduplicated wakes for maintainer feedback and linked issues reaching `ready-for-pr`.\n\nFixes #4469\n\n## Risk Assessment\n\n✅ Low: The change is bounded to contribution observation, classification, and durable wake routing; the reviewed paths preserve the required unknown and unmeasured-coverage boundaries.\n\n## Testing\n\nThe focused executable regression and isolated real Firstmate CLI homes exercised contribution classification, verdict freshness, away/yolo authority projection, unsupported-forge containment, and durable wake deduplication; direct JSON/queue artifacts record the observable results. This shell CLI has no rendered GUI surface. PR-title verification (`Fixes #4469`) remains correctly deferred to the publication phase, per the recorded decision.\n\n- Live validation: ✅ go - 8 of 8 scenarios driven live against the product\n\n| Scenario | Result | Live | Evidence |\n| --- | --- | --- | --- |\n| Bearings classifies known published contributions by required actor and never treats unchecked coverage as proven silence | ✅ pass | live | fm-contributions-targeted-regression.txt |\n| A replaced PR head makes its earlier verdict stale, while a verdict recorded on the observed replacement can be reassessed | ✅ pass | live | fm-contributions-cli-head-coverage.json; fm-contributions-targeted-regression.txt |\n| An unavailable current PR observation remains unverified rather than becoming fresh or stale | ✅ pass | live | fm-contributions-cli-head-coverage.json |\n| A green merge-authorized yolo delivery during away posture is fleet work in both same-home and secondmate-home summaries | ✅ pass | live | fm-contributions-targeted-regression.txt |\n| Unsupported-forge work, including held and expired-child cases, remains disclosed as unmeasured rather than captain or fleet work | ✅ pass | live | fm-contributions-cli-head-coverage.json; fm-contributions-targeted-regression.txt |\n| A new maintainer comment or review becomes one durable follow-up wake and a repeat observation creates none | ✅ pass | live | fm-contributions-cli-wakes.json; fm-contributions-targeted-regression.txt |\n| A filed issue reaching ready-for-pr becomes one durable planning wake and a repeat observation creates none | ✅ pass | live | fm-contributions-cli-wakes.json; fm-contributions-targeted-regression.txt |\n| One signal shared by two task records creates one durable wake while preserving separate acknowledgements, and watcher diagnostics do not duplicate that wake | ✅ pass | live | fm-contributions-targeted-regression.txt |\n\n<details>\n<summary>Evidence: Focused contribution regression transcript</summary>\n\nSource: [Focused contribution regression transcript](https://github.com/mremond/firstmate/blob/68065d03f6310b0d88123aa6ccde8baa8ecda2c9/.no-mistakes/evidence/fm/fm-4469-suivi-contributions/fm-contributions-targeted-regression.txt)\n\n`` `text\nok - only required-captain contributions are rows; other actors are counted\nok - replaced-head verdict is stale and cannot create a captain requirement\nok - unchecked ownership is disclosed and cannot prove silence\nok - newest check with no verdict is distinct from passing and pending\nok - new maintainer comment wakes once and stays pending until acknowledged\nok - new maintainer review wakes once and stays pending until acknowledged\nok - new maintainer inline wakes once and stays pending until acknowledged\nok - ready-for-pr on a filed issue becomes a planning wake\nok - a fresh open issue remains measured maintainer triage\nok - an absent check lane remains missing across repeated observations\nok - mixed freshness retains measured captain work and discloses the gap\nok - malformed durable evidence cannot prove silence\nok - a transient ready-for-pr label wakes and its exact acknowledgement survives replay\nok - recorded judgment keeps its exact head and is stale immediately on a published replacement\nok - a current forge observation refreshes a verdict after a replacement\nok - an unavailable current head leaves verdict freshness unknown\nok - away yolo delivery is fleet work without granting merge authority\nok - cross-home away yolo delivery is fleet work\nok - retired ownership persists and unsupported forge remains visibly unmeasured\nok - unsupported forge coverage is disclosed without inventing fleet work\nok - held unsupported forge coverage remains unmeasured\nok - shared contribution signal wakes once while retaining both acknowledgements\nok - watcher keeps observer diagnostics separate from contribution wakes\nok - expired child unsupported-forge coverage remains unmeasured\nok - watcher surfaces one newly durable contribution signal without re-ringing it\nok - parent consumes measured child coverage and refuses expired child silence\nok - unreadable pending signals refuse an empty-inbox claim\n`` `\n</details>\n<details>\n<summary>Evidence: Head freshness and unsupported-forge CLI output</summary>\n\nSource: [Head freshness and unsupported-forge CLI output](https://github.com/mremond/firstmate/blob/68065d03f6310b0d88123aa6ccde8baa8ecda2c9/.no-mistakes/evidence/fm/fm-4469-suivi-contributions/fm-contributions-cli-head-coverage.json)\n\n`` `text\n{\n  \"replaced_head\": {\n    \"scope\": \"owned contributions per home\",\n    \"known\": 1,\n    \"checked\": 1,\n    \"counts\": {\n      \"captain\": 0,\n      \"fleet\": 1,\n      \"maintainer\": 0,\n      \"nobody\": 0\n    },\n    \"complete\": true,\n    \"proven_clear\": true,\n    \"unmeasured_homes\": 0,\n    \"unreadable_records\": 0,\n    \"unmeasured\": 0,\n    \"stale_verdicts\": 1,\n    \"missing_verdicts\": 0,\n    \"captain_omitted\": 0,\n    \"captain\": []\n  },\n  \"unavailable_current_head\": {\n    \"checked\": 0,\n    \"stale_verdicts\": 0,\n    \"rows\": [\n      {\n        \"url\": \"https://github.com/o/r/pull/17\",\n 

... [645 bytes truncated] ...

    \"nobody\": 0\n    },\n    \"complete\": false,\n    \"proven_clear\": false,\n    \"unmeasured_homes\": 0,\n    \"unreadable_records\": 0,\n    \"unmeasured\": 1,\n    \"stale_verdicts\": 0,\n    \"missing_verdicts\": 0,\n    \"captain_omitted\": 0,\n    \"captain\": []\n  }\n}\n`` `\n</details>\n<details>\n<summary>Evidence: Exactly-once contribution wake CLI output</summary>\n\nSource: [Exactly-once contribution wake CLI output](https://github.com/mremond/firstmate/blob/68065d03f6310b0d88123aa6ccde8baa8ecda2c9/.no-mistakes/evidence/fm/fm-4469-suivi-contributions/fm-contributions-cli-wakes.json)\n\n`` `text\n{\n  \"maintainer_comment\": {\n    \"pending\": [\n      {\n        \"token\": \"comment:12:2026-09-16T08:01:00Z:\",\n        \"type\": \"comment\",\n        \"source\": \"https://github.com/o/r/pull/8#issuecomment-12\",\n        \"head\": null,\n        \"author\": \"maintainer\",\n        \"body\": \"Please clarify the contract\",\n        \"task\": \"delivery\",\n        \"url\": \"https://github.com/o/r/pull/8\"\n      }\n    ],\n    \"durable_wake_rows_after_new_signal\": 1,\n    \"durable_wake_rows_after_repeat\": 1\n  },\n  \"ready_for_pr_label\": {\n    \"pending\": [\n      {\n        \"token\": \"ready-for-pr:2026-09-16T08:00:00Z\",\n        \"type\": \"ready-for-pr\",\n        \"source\": \"https://github.com/o/r/issues/9\",\n        \"head\": null,\n        \"body\": \"filed issue reached ready-for-pr\",\n        \"task\": \"filed\",\n        \"url\": \"https://github.com/o/r/issues/9\"\n      }\n    ],\n    \"durable_wake_rows_after_new_signal\": 1,\n    \"durable_wake_rows_after_repeat\": 1\n  }\n}\n`` `\n</details>\n\n## Pipeline\n\nUpdates from [git push no-mistakes](https://github.com/kunchenguid/no-mistakes)\n\n<!-- no-mistakes-pipeline-attestation\:v1 {\"head_sha\":\"97e8fe0dc59c452ca80aa31f53861f457d779111\",\"steps\":[{\"step\":\"intent\",\"status\":\"completed\"},{\"step\":\"rebase\",\"status\":\"completed\"},{\"step\":\"review\",\"status\":\"completed\"},{\"step\":\"test\",\"status\":\"completed\"},{\"step\":\"document\",\"status\":\"completed\"},{\"step\":\"lint\",\"status\":\"completed\"},{\"step\":\"push\",\"status\":\"completed\"},{\"step\":\"pr\",\"status\":\"running\"},{\"step\":\"ci\",\"status\":\"pending\"}]} -->\n\n<details>\n<summary>✅ **intent** - passed</summary>\n\n✅ No issues found.\n</details>\n\n<details>\n<summary>🔧 **Rebase** - 1 issue found → auto-fixed ✅</summary>\n\n- ⚠️ `bin/fm-crew-state.sh` - merge conflict rebasing onto origin/main\n\n🔧 Fix applied.\n✅ Re-checked - no issues remain.\n</details>\n\n<details>\n<summary>🔧 **Review** - 2 issues found → auto-fixed (3) ✅</summary>\n\n- 🚨 `bin/fm-contributions.sh:179` - Intent requires “Classify every published contribution” and maintainer-signal wakes, but the existing supported GitLab MR path remains excluded: `fm-pr-check.sh` accepts GitLab MRs, while `observe` rejects every non-GitHub URL. A recorded GitLab MR therefore reaches `poll`, records an unavailable observation, and is always reported as fleet/unmeasured with no comment or review wake. Either include the established GitLab provider in this restore or explicitly authorize that containment.\n- 🚨 `bin/fm-watch.sh:2201` - One incoming signal produces multiple wakes. `poll` first persists its event-keyed durable wake, then prints the pending count on every later poll; the watcher appends this generic check wake and re-rings it each interval until acknowledgement. A single maintainer comment thus immediately creates two queue rows and continues re-announcing. Suppress the aggregate watcher wake for already-durable pending signals, while retaining real observer-failure reporting.\n\n🔧 Fix applied.\n1 error still open:\n\n- 🚨 `bin/fm-bearings-snapshot.sh:604` - This contradicts the accepted containment: “unsupported forge ... must be reported as unmeasured coverage” and “must not be classified as fleet work.” A GitLab MR with a live hold takes the earlier captain branch, never `unmeasured`; moreover, an expired secondmate summary rewrites every known contribution—including an unmeasured GitLab MR—to `fleet:.known`. Thus a parent immediately reports a child containing only an unsupported MR as fleet work (its `valid_until` is 0), rather than unmeasured coverage. Make unmeasured classification take precedence and preserve it when degrading stale child summaries.\n\n🔧 Fix applied.\n2 errors still open:\n\n- 🚨 `bin/fm-contributions.sh:241` - One external signal can produce two durable wakes when two filed tasks own the same PR. `known` intentionally retains both owners, then this key hashes `task + token`; each observation appends a distinct queue row. Key the wake by canonical URL plus event token and suppress an already-queued shared key, while retaining both task records for acknowledgement.\n- 🚨 `bin/fm-watch.sh:2177` - A poll that both reports an unreadable record and persists a new signal bypasses the new suppression: the diagnostic line makes `contribution_check_invalid=1`, so the watcher appends a generic check wake containing the already-durable signal. Split diagnostics from contribution-wake lines and surface only the diagnostic as the generic observer failure.\n\n🔧 Fix applied.\n✅ Re-checked - no issues remain.\n</details>\n\n<details>\n<summary>✅ **Test** - passed</summary>\n\n✅ No issues found.\n- Live validation: ✅ go - 8 of 8 scenarios driven live against the product\n\n| Scenario | Result | Live | Evidence |\n| --- | --- | --- | --- |\n| Bearings classifies known published contributions by required actor and never treats unchecked coverage as proven silence | ✅ pass | live | fm-contributions-targeted-regression.txt |\n| A replaced PR head makes its earlier verdict stale, while a verdict recorded on the observed replacement can be reassessed | ✅ pass | live | fm-contributions-cli-head-coverage.json; fm-contributions-targeted-regression.txt |\n| An unavailable current PR observation remains unverified rather than becoming fresh or stale | ✅ pass | live | fm-contributions-cli-head-coverage.json |\n| A green merge-authorized yolo delivery during away posture is fleet work in both same-home and secondmate-home summaries | ✅ pass | live | fm-contributions-targeted-regression.txt |\n| Unsupported-forge work, including held and expired-child cases, remains disclosed as unmeasured rather than captain or fleet work | ✅ pass | live | fm-contributions-cli-head-coverage.json; fm-contributions-targeted-regression.txt |\n| A new maintainer comment or review becomes one durable follow-up wake and a repeat observation creates none | ✅ pass | live | fm-contributions-cli-wakes.json; fm-contributions-targeted-regression.txt |\n| A filed issue reaching ready-for-pr becomes one durable planning wake and a repeat observation creates none | ✅ pass | live | fm-contributions-cli-wakes.json; fm-contributions-targeted-regression.txt |\n| One signal shared by two task records creates one durable wake while preserving separate acknowledgements, and watcher diagnostics do not duplicate that wake | ✅ pass | live | fm-contributions-targeted-regression.txt |\n\n- `bash tests/fm-contributions.test.sh`\n- <code>Isolated `FM_HOME` runs of `bin/fm-bearings-snapshot.sh --json` and `bin/fm-contributions.sh snapshot … --all`</code>\n- <code>Isolated `FM_HOME` runs of `bin/fm-pr-check.sh`, registered contribution checks, and `bin/fm-contributions.sh pending`</code>\n- `git status --short`\n</details>\n\n<details>\n<summary>✅ **Document** - passed</summary>\n\n✅ No issues found.\n</details>\n\n<details>\n<summary>✅ **Lint** - passed</summary>\n\n✅ No issues found.\n</details>\n\n<details>\n<summary>✅ **Push** - passed</summary>\n\n✅ No issues found.\n</details>\n","closingIssuesReferences":[{"id":"I_kwDOS4Me3c8AAAABRSDoZA","number":4469,"repository":{"id":"R_kgDOS4Me3Q","name":"firstmate","owner":{"id":"MDQ6VXNlcjMyMzMwMDY=","login":"kunchenguid"}},"url":"https://github.com/kunchenguid/firstmate/issues/4469"}],"state":"MERGED"}
$ bin/fm-issue-closure.sh https://github.com/kunchenguid/firstmate/pull/4627
exit=0
Evidence: Delivery evidence regression transcript

Source: Delivery evidence regression transcript

ok - fm-spawn/fm-promote: authorized intent preserves exact words and refuses operator-address lines
ok - fm-spawn: every legacy worker receives scoped role instructions without changing project or primary instructions
ok - fm-spawn: a ship spawn requires a valid explicit mode and yolo before anything is created
ok - fm-spawn: scout and secondmate spawns refuse ship delivery flags
ok - fm-spawn: the brief's recorded mode and the spawn's explicit mode must agree
ok - fm-spawn: a rigor downgrade against the registered posture is announced, never blocked
ok - fm-spawn: a scout spawn resolves no delivery posture from the registry
ok - fm-promote: promotion requires the delivery contract and records it exactly once
ok - fm-promote: a symlinked task record is refused and its target is left untouched
ok - fm-promote: a promoted worker receives the same mode-specific delivery contract a briefed one does
ok - fm-project-mode: the conditional policy is accepted, mapped for mechanical callers, and readable raw
ok - fm-spawn/fm-promote: leftover Task placeholders are refused until both subsections are filled
# all fm-task-delivery tests passed
Evidence: Task bootstrap regression transcript

Source: Task bootstrap regression transcript

ok - a failed project bootstrap loudly stops spawn before worker launch
ok - stale fingerprints run bootstrap while matching fingerprints skip it
ok - a failed prerequisite stays loud and cannot publish a success marker
ok - only a tracked declaration counts as opt-in
ok - a symlinked .firstmate parent cannot smuggle in a declaration
ok - every declared mode runs with stdin from /dev/null
ok - every available bounded runner stops a hanging run and kills its process group
ok - a host with no bounded runtime refuses the declaration instead of running it unbounded
ok - only timeout, gtimeout, or node may be pinned as the bounded runner
ok - declarations without a timeout mode run under the documented 900s default
ok - an unvalidated pane path is never returned to the treehouse pool
# all task-bootstrap tests passed
Evidence: Spawn dispatch and quota-account routing transcript

Source: Spawn dispatch and quota-account routing transcript

ok - allowlist=absent preserves the operational floor and filters only when opted in
ok - allowlist=missing-config preserves the operational floor and filters only when opted in
ok - allowlist=enabled preserves the operational floor and filters only when opted in
ok - allowlist=empty preserves the operational floor and filters only when opted in
ok - invalid allowlist names refuse before launch or task publication
ok - inaccessible config with present allowlist refuses before launch or task publication
ok - inaccessible config with absent allowlist refuses before launch or task publication
ok - inaccessible ancestor with present allowlist refuses before launch or task publication
ok - inaccessible ancestor with absent allowlist refuses before launch or task publication
ok - secondmate launch inherits the allowlist for subsequent worker launches
ok - local inheritance preserves the allowlist on source errors and mirrors proven absence
ok - remote inheritance preserves the allowlist on source errors and mirrors proven absence
ok - fm-spawn: actual ship/scout launch commands deliver the worker role contract
ok - no --model/--effort records defaults and types the claude launch instructions
ok - non-cursor launches clear inherited Cursor identity markers
ok - relative home overrides ignore CDPATH and become absolute before spawn launch construction
ok - FM_HOME defaults resolve relative paths and preserve absolute spellings
ok - absolute override spellings are preserved in spawn launch paths
ok - unresolvable relative spawn overrides fail with named diagnostics
ok - active crew-dispatch profile requires an explicit harness for ship spawns
ok - active crew-dispatch profile requires an explicit harness for scout spawns
ok - active crew-dispatch profile allows an explicit resolved harness
ok - active crew-dispatch profile allows the legacy positional harness form
ok - active crew-dispatch profile allows the raw launch-command escape hatch
ok - claude receives --model and --effort profile flags
ok - codex receives --model and model_reasoning_effort profile flags
ok - codex Luna receives --model and model_reasoning_effort max profile flags
ok - codex omits max for models without the catalog capability
ok - grok receives --model and --reasoning-effort profile flags
ok - grok omits unsupported max reasoning effort
ok - grok omits unsupported xhigh reasoning effort
ok - cursor receives its model-qualified reasoning class and exact task workspace
ok - cursor refuses model ids absent from its resolved binary's live catalog
ok - cursor preserves the requested model when its live catalog is unreachable
ok - opencode receives --model and omits the unsupported effort axis
ok - native effort validator checks harness and model as separate axes
ok - Ultra is explicit for native Pi and Pi-signed, including direct-PR, and refuses unsupported profiles before provisioning
ok - batch dispatch preserves native Ultra in metadata and launch flags
ok - pi receives --model and --thinking max profile flags
ok - Pi launch probing omits --tui-mode on older Pi and preserves it on supporting Pi
ok - pi-signed shares Pi launch semantics while preserving its configured and recorded identity
ok - pi-signed refuses safely and actionably when the selected executable is unavailable
ok - pi-signed is a distinct persistent secondmate runtime with shared Pi supervision semantics
ok - batch dispatch forwards shared --harness, --model, and --effort to every pair
ok - claude forwards firstmate's CLAUDE_CONFIG_DIR so the crewmate uses the same credential store
ok - claude omits the config-dir prefix when firstmate runs with the single-store default
ok - config/claude-permission-mode=bypass launches exactly as an absent file does
ok - config/claude-permission-mode=auto replaces --dangerously-skip-permissions with --permission-mode auto
ok - config/claude-permission-mode=auto reaches scout launches too
ok - an unrecognized config/claude-permission-mode token refuses before any endpoint or metadata
ok - config/claude-permission-mode changes claude launches only
ok - non-claude harnesses do not receive the claude CLAUDE_CONFIG_DIR prefix
ok - a claude task launch establishes only Firstmate's task control channels through the system prompt
ok - a persistent claude secondmate keeps its supervisor contract without a task-worker authority overlay
ok - a claude crewmate launch carries the attribution-off policy in its own settings
ok - a claude secondmate launch carries the attribution-off policy too
ok - active crew-dispatch profile does not block secondmate launches
# all fm-spawn-dispatch-profile tests passed
Evidence: Teardown endpoint-safety transcript

Source: Teardown endpoint-safety transcript

ok - fm-teardown: missing, empty, malformed, ambiguous, and task-mismatched endpoints refuse before every mutation or runtime call
ok - fm-teardown: a concurrent lifecycle action refuses before mutation
ok - fm-teardown: non-pool cleanup ignores unrelated task publication locks
ok - fm-teardown: destructive cleanup serializes with metadata writers
ok - cleanup identity: valid tmux, Herdr, Zellij, Orca, and cmux records validate while every empty backend target refuses
ok - tmux backend: direct empty target returns nonzero without invoking tmux
ok - process cleanup: creation-time PID identity removes only the exact child and preserves the control child
ok - fm-teardown: exact tmux cleanup preserves invalid and prefix-matched neighbors while removing only the recorded target
ok - fm-teardown: a close that genuinely failed refuses and keeps the record naming the surviving endpoint, and the same teardown finishes once the close works
ok - fm-teardown: --force continues past a close it could not make while still reporting it, and the same case refuses without --force
ok - fm-teardown: a close re-read that could not run refuses, while a definitively absent session or server still completes silently
ok - fm-teardown: forced secondmate cleanup still refuses on a child endpoint close that failed
ok - fm-teardown: an Orca close its missing CLI never attempted refuses even under --force, keeping the record naming the terminal
ok - fm-teardown: an already-exited endpoint, and a server that is already gone, still complete cleanup silently
ok - Treehouse locking resolves a bare local origin against its source project, matching the provisioned clone
ok - fm-teardown: a pool slot named by a second task record is never returned, killed, or reset
ok - fm-teardown: a pool slot held by another firstmate home is never returned
ok - fm-teardown: a task that solely holds its slot still returns it
ok - fm-teardown: a pool slot claimed by another task is left alone while the task's own cleanup finishes
ok - fm-teardown: a task's own slot claim, and an unclaimed slot, both still tear down
ok - fm-teardown: an exact recorded endpoint still tears down after changing cwd outside its worktree
ok - Treehouse project locking anchors at the local root for main-home, local-secondmate, and remote-seeded layouts
ok - fm-teardown: a remote-seeded secondmate home returns its own uncontested pool slot
ok - fm-teardown: slot ownership across a remote-seeded home and its local child still refuses
ok - Treehouse project locking still serializes two homes across the remote-seeded boundary
Evidence: Merge ancestry and upstream-supersession evidence

Source: Merge ancestry and upstream-supersession evidence

$ git show -s --format=%H%n%P 9edd13d3
9edd13d3cee343ef26a430632253370731c9c324
bdef528a522848521983ebddf90599905ede4fe5 af1f2ea37849a2b533097b2c5bcb931ceab24adf
$ git merge-base --is-ancestor af1f2ea3 HEAD
exit=0
$ upstream-verbatim retirement checks
upstream-verbatim: bin/backends/herdr.sh
upstream-verbatim: bin/fm-send.sh
upstream-verbatim: tests/fm-backend-herdr.test.sh
upstream-verbatim: bin/fm-fleet-snapshot.sh
$ git status --short
- Outcome: ⚠️ 1 warning across 2 runs (15m3s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 1 issue found → auto-fixed ✅
  • ⚠️ bin/fm-issue-closure.sh:127 - The brief is documented as a fallback when the PR body has no parseable closing reference, but candidate construction always unions brief references with PR-body and GitHub references. If a PR body says Fixes #7 while its retained task brief contains an older or contextual Fixes #12, teardown can report open fix: wake pi crewmates after each turn kunchenguid/firstmate#12 as a failure of that PR even though the PR never claimed it. Parse body candidates separately and consult the brief only when the body yields none.

🔧 Fix applied.
✅ Re-checked - no issues remain.

⚠️ **Test** - 1 warning
  • ⚠️ bin/fm-issue-closure.sh:19 - fm-issue-closure.sh --help still says candidate issue numbers are “unioned” from all three sources, contradicting the newly fixed behavior and the following bullet that correctly calls the task brief a fallback. The executable behavior passes, but the public CLI guidance is now misleading and should say that body/GitHub candidates are used first and the brief is consulted only when the body has no parseable reference.
  • 🚨 live validation verdict: no-go (8 of 10 scenarios were driven live against the product); failed: A user reading issue-closure help sees the same brief-fallback policy the command implements
  • Live validation: ❌ no-go - 8 of 10 scenarios driven live against the product
Scenario Result Live Evidence
After a merged GitHub PR, teardown’s closure checker reports only genuinely open intended issues and does not block completion ✅ pass live bin/fm-issue-closure.sh https://github.com/kunchenguid/firstmate/pull/4627 used the real GitHub-backed product path, exited 0, and emitted no false discrepancy for the merged PR and closed issue; ad…
A user tears down already-landed work without losing unlanded changes ✅ pass live fm-teardown.log records real script execution over isolated git repositories, including remote branch lookup, merge comparison, file-by-file squash fallback, refusal of unlanded deletion, and issue-…
A fresh ordinary worker is bootstrapped before launch and launch is refused when bootstrap fails ✅ pass live fm-task-bootstrap.log records the executable bootstrap interface succeeding for valid declarations and refusing launch for failures, unsafe runners, and invalid pane reuse.
A raw account-prefixed launch command keeps quota-account routing and all launch placeholders ✅ pass live fm-spawn-dispatch-profile.log records the actual spawn command builder preserving dispatch profiles, harness flags, operational input, brief payload, turn-end hooks, and secondmate exclusions in iso…
Quota-aware dispatch selects available profiles and fails closed on exhausted or invalid quota ✅ pass live fm-quota-choose.log records the public quota chooser consuming one captured quota snapshot, selecting eligible profiles, and failing closed for exhausted or malformed availability.
A user renders a complete fleet snapshot at realistic backlog size ✅ pass live fm-fleet-snapshot-view.log records the executable snapshot/view interfaces at realistic backlog state, including durable reports, tasks-axi rows, and secondmate liveness.
Direct-PR and no-mistakes delivery flows expose real PR/check evidence to workers and promoted scouts ✅ pass live fm-brief.log records generated ship/scout briefs and promoted-scout paths carrying delivery evidence scaffolding.
The upstream sync retains the required true merge ancestry ⏸️ untested no Merge-parent ancestry is a Git history invariant with no running end-user product surface to drive; the outer review/ship phases must preserve both parents and avoid squash/rebase.
The captain’s local crew-dispatch configuration remains valid under the merged validator ⏸️ untested no The required gitignored config/crew-dispatch.json was not supplied in this isolated worktree. Provide a copy inside this worktree to validate it without crossing the workspace boundary.
A user reading issue-closure help sees the same brief-fallback policy the command implements ❌ fail live bin/fm-issue-closure.sh --help printed “Candidate issue numbers are unioned” even though brief references are now fallback-only.
  • bash tests/fm-brief.test.sh
  • bash tests/fm-issue-closure.test.sh
  • bash tests/fm-teardown.test.sh
  • bash tests/fm-task-bootstrap.test.sh
  • bash tests/fm-spawn-dispatch-profile.test.sh
  • bash tests/fm-fleet-snapshot-view.test.sh
  • bash tests/fm-quota-choose.test.sh
  • bin/fm-issue-closure.sh https://github.com/kunchenguid/firstmate/pull/4627
  • bin/fm-issue-closure.sh --help
  • git show -s --format='%H%n%P%n%s' 9edd13d3
  • jq -e 'type=="object"' config/crew-dispatch.json

🔧 Fix applied.
1 warning still open:

  • ⚠️ live validation verdict: inconclusive (2 of 11 scenarios were driven live against the product); untested: A stale task-brief reference cannot supplement an explicit PR-body closing reference, Issue closure verification reports actionable failures without blocking teardown, Direct-PR and promoted worker delivery contracts preserve evidence requirements, Fresh ordinary worker launches bootstrap their project environment and refuse failed bootstrap, Quota-account-prefixed raw launch commands retain harness and profile routing, Teardown accepts already-landed or absent endpoints while preserving unrelated runtime targets, Fleet Bearings completes at realistic backlog size without losing bounded output, The branch preserves true upstream ancestry and leaves explicitly superseded implementations upstream-verbatim, Quota array dispatch works against the host's real account and quota tooling
  • Live validation: ⚠️ inconclusive - 2 of 11 scenarios driven live against the product
Scenario Result Live Evidence
A user reading issue-closure help sees the same brief-fallback policy the command implements ✅ pass live fm-issue-closure-help.log shows the executable help names PR-body and GitHub references as primary and the task brief as fallback.
A stale task-brief reference cannot supplement an explicit PR-body closing reference ⏸️ untested no The prior payload cited an isolated behavioral regression transcript and explicitly recorded live=false; it did not establish this result against the live product.
Issue closure verification handles a real merged GitHub PR whose referenced issue is closed ✅ pass live fm-issue-closure-live.log records merged upstream PR 4627, its real closing reference to issue 4469, and a silent successful product exit.
Issue closure verification reports actionable failures without blocking teardown ⏸️ untested no The prior payload cited executable regression coverage but explicitly recorded live=false; it did not establish this behavior against the live product.
Direct-PR and promoted worker delivery contracts preserve evidence requirements ⏸️ untested no The prior payload cited a delivery-contract regression transcript but explicitly recorded live=false; it did not establish this result against the live product.
Fresh ordinary worker launches bootstrap their project environment and refuse failed bootstrap ⏸️ untested no The prior payload cited a task-bootstrap regression transcript but explicitly recorded live=false; it did not establish this result through a live worker launch.
Quota-account-prefixed raw launch commands retain harness and profile routing ⏸️ untested no The prior payload described spawn commands exercised in isolation and explicitly recorded live=false; it did not establish routing against the live product.
Teardown accepts already-landed or absent endpoints while preserving unrelated runtime targets ⏸️ untested no The prior payload cited an isolated teardown regression transcript and explicitly recorded live=false; it did not establish endpoint safety against the live product.
Fleet Bearings completes at realistic backlog size without losing bounded output ⏸️ untested no The prior payload cited a regression transcript using a large backlog but explicitly recorded live=false; it did not establish this result against the live product.
The branch preserves true upstream ancestry and leaves explicitly superseded implementations upstream-verbatim ⏸️ untested no The prior payload cited repository ancestry and file-comparison checks and explicitly recorded live=false; it did not establish a live product result.
Quota array dispatch works against the host's real account and quota tooling ⏸️ untested no The dedicated live E2E requires explicit FM_QUOTA_ARRAY_DISPATCH_LIVE_E2E=1 opt-in and access to host quota/account tooling; provide that opt-in in an authorized isolated quota environment to drive…
  • bin/fm-issue-closure.sh --help
  • tests/fm-issue-closure.test.sh
  • gh pr view https://github.com/kunchenguid/firstmate/pull/4627 --json state,body,closingIssuesReferences
  • bin/fm-issue-closure.sh https://github.com/kunchenguid/firstmate/pull/4627
  • tests/fm-task-delivery.test.sh
  • tests/fm-task-bootstrap.test.sh
  • tests/fm-spawn-dispatch-profile.test.sh
  • tests/fm-teardown-endpoint-safety.test.sh
  • tests/fm-bearings-snapshot.test.sh
  • tests/fm-quota-array-dispatch-live-e2e.test.sh (skipped: explicit live opt-in absent)
  • git show -s --format='%H%n%P' 9edd13d3
  • git merge-base --is-ancestor af1f2ea3 HEAD
  • upstream-verbatim comparisons for bin/backends/herdr.sh, bin/fm-send.sh, tests/fm-backend-herdr.test.sh, and bin/fm-fleet-snapshot.sh
  • git status --short
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

kunchenguid and others added 30 commits August 18, 2026 11:25
* Adopt quota-axi 0.1.29 spendPriority-primary array dispatch.

quota-axi 0.1.29 publishes schema 5 with selection.spendPriority as the primary comparative signal and demotes derivation fields out of default --json. Rank comparable-fit candidates on that scalar, keep runway versus the completion horizon as a hard gate, and raise the compatibility floor so a pre-consolidation build cannot reach dispatch intake.

* no-mistakes(review): Correct schema fixtures and remove prescriptive selection prompts

* no-mistakes(document): Correct quota verification evidence chronology

* Collapse quota-array-dispatch onto TOON-first spendPriority ranking.

Decide from quota-axi's default TOON; keep --json as a rare defensive fallback.
Rank by spendPriority after eligibility, reasoning-class, and runway-feasibility gates, and drop the hand-computed Pareto, pace, reserve, and window-id layers.

* no-mistakes(review): Permit ambiguous JSON fallback and correct reset fixtures

* no-mistakes(review): Correct runway semantics and escalate unresolved uncertainty

* no-mistakes(document): Document TOON-first quota dispatch evidence
* docs: add GROK_BOT.md Grok Bot system prompt

* docs: amend GROK_BOT.md with charter report-back and delegation marker

* docs: classify GROK_BOT.md as public-product

* docs: make GROK_BOT.md the plain Grok Bot system prompt
Refine language for clarity and consistency in instructions.
…#2595)

* fix(bin): guarantee inactive-reconcile scan progress under second quantization

The inactive-outcome scan computed its aggregate deadline in whole seconds,
so a 1-second budget's effective value lands anywhere in (0,1]; a scan
starting just before a wall-clock second boundary rounded its whole budget
away mid-scan and exited having visited no child, while the durable cursor
had already advanced past the never-examined child. This is the CI flake
behind tests/fm-inactive-reconcile.test.sh's 'next bounded scan did not
resume with the following child' (watcher-wake-lock family, portable
serial 2, seen on the PR kunchenguid#2590 run).

Every scan now visits at least its first due child with the per-child
state-read bound floored at one second, so no invocation can be a zero-work
no-op. The outer process-group kill moves to budget+1s: the scan's own
deadline enforces the budget, and the kill is a backstop for a scan wedged
in an unbounded wait instead of a racer that routinely preempts the clean
bounded exit. The wake-lock-wait test bound tracks the backstop (3s -> 4s);
the previously flaky assertion is unchanged.

* no-mistakes(document): Document inactive-reconcile deadline backstop
Refactor the guidelines for Firstmate's role and delegation process, emphasizing the importance of crewmates and asynchronous work.
Clarified guidelines for handing off work to crewmates and managing secrets.
…tions deterministic (kunchenguid#2617)

Three assertions in tests/fm-procevent.test.sh depended on a detached runner
having finished work that the command starting it does not wait for.

reconcile's replacement runner is started through detach_runner, which only
forks: reconcile returns and counts the start before that runner has claimed
its source or exec'd its child. Any assertion taken straight after reconcile
therefore samples a race.

- The publish-before-apply recovery section left its always-ready /bin/echo
  source registered across the recovery reconcile, so that reconcile launched
  a competing detached poll (observed: started=1) that then raced every later
  assertion for the source claim, the next capture sequence, and this home's
  applied record, and outlived the section holding a live claim. It is now
  retired before that reconcile - re-announcement is proven from the durable
  inbox alone and needs no registration - and started=0 is asserted so a
  competing poll cannot be reintroduced unnoticed. This is the same
  retire-before-reconcile discipline the self-announcing section already
  carries; that section acquired it after the identical race made its
  "not-autohandled: self-src" assertion read "already owned: self-src".

- The crashed-leader replacement section snapshotted the replacement's claim
  file and execution log behind a fixed 0.5s settle window. On a loaded
  machine that window expires first, which is the CI flake behind "a
  replacement runner started without recording its own claim" and "reconcile
  did not start exactly one replacement source". Both effects are now waited
  for with the suite's bounded wait helpers; the exact one-replacement count
  is still asserted afterwards, unchanged.

- The duplicate-start section slept 0.5s for reconcile's runner to record
  ownership before asserting that a second start loses to it. It now waits
  for that claim.

Also tighten one assertion that could not fail as written: "autohandled:
self-src" is a substring of "not-autohandled: self-src", so the applied path
was accepted even when the runner reported the capture left for the handler.

Evidence: on the unmodified suite, 128 full runs at 6-8x concurrency produced
6 failing runs, all in the crashed-leader section. On the fixed suite, 216
full runs under the same load produced none. Reverting the self-announcing
section's retire-before-reconcile line reproduces "already owned: self-src"
on the first iteration, confirming the shared mechanism.
)

* fix(bin): keep pending-reply expectations honest on both send legs

Two related asymmetries let the parent-owned secondmate reply guard drop or
nag requests it should not have.

Local delivered-unconfirmed dropped the expectation. A marked request whose
submit read-back stayed unconfirmed (verdict=pending) is the same
not-a-failure outcome the remote leg reports as delivered, but fm-send
discarded the parent's pending-reply record for it, so a request that very
likely landed stopped being tracked entirely. The record now stays armed on
its unconfirmed-delivery marker: a correlated report still resolves it, and
an unanswered one still surfaces through the library's own reconciliation.
Exit 3 and the local rule that an unconfirmed answer never closes a decision
key are unchanged.

Remote replies were nagged for a repost they did not need. A remote mate's
report reaches the parent's status log only through the asynchronous mirror
in fm-procevent-remote-reply.sh, yet the guard read an absent correlated
line as proof the mate never reported - even while the answer was still in
flight, which is the common case because the mirror's poll window is
comparable to the recovery grace. The mirror now publishes one caught-up
watermark from a quiet window, and the guard admits a missing report as
evidence only once that watermark passes the turn that should have produced
it. A genuinely missed report still gets exactly one repost, and a channel
that is behind, unarmed, or broken leaves the request durably open and
un-nagged rather than nagging blind; the mirror escalates its own continuity
failures as before.

Tests: a local unconfirmed secondmate send keeps its expectation armed and
resolvable; a mirrored correlated remote reply resolves with no repost; a
stale or absent watermark withholds the repost while a fresh one still
releases it; a quiet remote window publishes the watermark and retirement
clears it.

* no-mistakes(review): Distinguish preempted polls from quiet windows

* no-mistakes(document): Clarify remote reply channel freshness

* no-mistakes(lint): Annotate shared remote preemption exit constant
…d#2619)

* fix(watch): honor a declared pause on a busy pane's completed-turn bound

A worker that declares an external wait (`paused:`) and then blocks in one
long foreground call - a review-hosting scout parked in a single blocking
`lavish-axi poll`, a bounded watch loop, a rate-limit sleep - keeps its pane
BUSY, so the stale path that already honors declared pauses never ran for it.
The busy-pane completed-turn bound instead routed it straight into
wedge_timer_check, which re-escalated "possible wedge, escalation N" (and, past
the threshold, demand-deep-inspection) every FM_STALE_ESCALATE_SECS for as long
as the review stayed open.

busy_turn_bound_check now owns which absorber takes a crossed bound: a crew
whose own last status line declares an external wait or a verified captain-held
transfer takes the bounded FM_PAUSE_RESURFACE_SECS recheck, and everything else
keeps the unchanged wedge timer. The discriminator is the declaration together
with liveness (the caller has already confirmed the pane is busy), never a
blanket silencing - a crew that declared nothing, or whose pane is not live,
escalates exactly as before, and a declared pause still re-surfaces once per
long cadence so a forgotten wait cannot rot invisibly. Away mode is untouched:
the daemon owns pause triage there and already reads the same vocabulary.

The two call sites also no longer clear pause bookkeeping in the same poll the
pause cadence recorded it, which would have erased the re-surface throttle and
turned the long cadence back into a per-poll re-surface.

Tests: a three-phase regression fixture pins the absorbed pause, its long-cadence
recheck, and the restored wedge escalation once the declaration is lifted on the
same busy over-age pane.

Also de-flakes tests/fm-watch-triage.test.sh, which failed spuriously on a loaded
machine: fixed liveness budgets were reaping watchers mid-startup, so assertions
on post-poll state passed vacuously or failed spuriously. Waits that describe a
poll's outcome now wait for a completed poll cycle via the liveness beacon, the
heartbeat test waits for the heartbeat it asserts on, and every wait_for_exit
budget is the uniform 10s already used elsewhere in the file.

* no-mistakes(review): Fail poll-cycle waits on timeout

* no-mistakes(review): Prevent poll timeout test hangs

* no-mistakes(document): Clarify paused busy-pane supervision
Added guidelines for decision communication to the captain.
Clarify communication protocols with crewmates regarding task delegation and reporting.
* fix(herdr): confirm local steers that native agent-state misses

Herdr can leave agent_status idle for a landed Claude turn and can keep
queued Enter text visible while busy, so fm-send was reporting false
swallows. Confirm those cases through the shared queued-Enter verdict
and a cleared composer, and keep a genuine idle pending composer as
unconfirmed.

* no-mistakes(review): Stop Herdr Enter retries on unreadable composers

* no-mistakes(review): Reject queued delivery when all Herdr Enter sends fail

* no-mistakes(review): Prevent confirmation after failed Herdr Enter

* no-mistakes(review): Pace Herdr retries and clarify submit fallback

* no-mistakes(review): Align Herdr submit docs with idle fallback

* no-mistakes(document): Correct Herdr submit-confirmation documentation
* feat(bin): accept any-origin decision bindings with full-identity keys

An aggregation surface (the bearings board) carries captain answers for holds
across origins, but a binding was one-origin-per-source and the Lavish adapter
capped question keys at 64 chars while real full hold identities measure 69-81.

- fm-decision-hold.sh: bind <source-id> --any-origin records the (any) marker;
  binding prints it verbatim and answers accepts it, so the runner's feed seam
  carries an any-origin source with no runner change. In any-origin mode each
  key is a full hold identity <origin>-decision-<key>, split at its first
  -decision-; a key with no separator (merge/dispatch instructions) is skipped
  and feeds nothing, keeping non-decision answers out of the hold ledger by
  construction. Every existing close guard applies unchanged.
- fm-procevent-lavish.sh: raise the question-key cap 64 -> 128 so a full hold
  identity fits; the slug-shape security property is unchanged.
- tests: cross-origin closure through the real runner seam, an 81-char
  identity through the adapter, cap and shape refusals, routed-work skips,
  nonexistent-identity skips, and idempotent replay.

* feat(bearings): add the /bearings lavish interactive fleet board

/bearings lavish renders the bearings snapshot onto a shipped, reusable board
template and arms it as a Lavish process-event source, so the captain answers
Captain's Call items on the board and firstmate is woken by an ordinary check
wake - no conversational turn ever blocks on a poll.

- .agents/skills/bearings/assets/board-template.html: the shipped template
  (myfirstmate design system inlined, one fm-bearings-board.v1 JSON slot,
  fail-closed schema guard that renders an error card instead of an empty
  fleet). Per-invocation agent work is composing the payload only.
- bin/fm-bearings-board.sh: build/refresh owner - fail-closed payload
  validation, slot injection with a round-trip check and \u003c escaping,
  stable board path, any-origin bind ALWAYS before arm, arm-if-absent.
- bearings SKILL.md: the lavish invocation option, board composition rules,
  board-wake handling, and the captain-ruled merge-click authorization with
  its mandatory safeguards (PR resolved from the task's own meta record,
  wake-time green re-verification, never a red or changed PR, merges only
  through bin/fm-pr-merge.sh, chat echo with the full PR URL).
- process-event-sources SKILL.md: one-line board-wake routing trigger.
- tests: payload refusals, injection round-trip, bind-before-arm, idempotent
  re-arm, and template slot integrity.

Fleet pickup: homes receive this after merge plus a firstmate self-update;
landing timing is coordinated with the main firstmate.

* no-mistakes(review): Harden bearings board validation and wake handling

* no-mistakes(review): Require HTTPS for bearings board PR links

* no-mistakes(review): Fail closed and bound bearings board answers

* no-mistakes(review): Enforce UTF-8 byte limits for board answers

* no-mistakes(review): Serve bearings board before arming and reject empty actions

* no-mistakes(review): Prove bind-before-arm ordering through live answer consumption

* no-mistakes(document): Document bearings board and cross-origin answers
…enguid#2707)

* fix(bearings): always show decision options and a close/drop control

Freeform-only Captain's Call cards hid the option buttons the board was designed around, and there was no way to drop a stale hold without inventing an answer. Require selectable options, keep freeform as a supplement, and route the reserved __drop__ answer through decline so the hold leaves Captain's Call.

* no-mistakes(review): Fix drop closure and decision-only option validation

* no-mistakes(review): Preserve answerability for non-decision cards

* no-mistakes(document): Clarify decision drop documentation
Signature-only PRs can hide skipped review, test, or document steps. Fail unless no-mistakes >= 1.46.0 attests those three steps completed.
…#2728)

* feat(captain-hold): collapse the decisions concept into tasks held for the captain

A decision is no longer a separate type: it is an ordinary backlog task held
for the captain, identified by its task id. bin/fm-captain-hold.sh owns the
surviving behaviors - guarded hold creation, the recorded-answer close
(answer/answers with a release mode for captain-gated work), the source
bindings, and the investigation completion gate - and bin/fm-decision-hold.sh
becomes a one-release compatibility shim over it.

The fleet snapshot now parses hold-until and computes captain_actionable as
queued + captain-held + unblocked + due, independent of row kind, plus a
presentation-only deferred_marker for prose-deferred rows. Bearings renders
every due captain-held task in Captain's Call, date-deferred holds as dated
Charted Next gates, suppresses prose-deferred rows from default views with an
omitted disclosure, and excludes from Recently Landed anything that closed
while still held for the captain.

Legacy compatibility: pre-collapse <origin>-decision-<key> rows are already
plain task ids and keep working; short keys in recorded metadata, concrete
origin bindings, chat --resolve-key fallbacks, and old resolution records all
resolve in place.

* no-mistakes(review): Fix captain answer replay and body preservation

* no-mistakes(review): Fix captain hold idempotency and legacy replay

* no-mistakes(review): Validate card close modes and compatibility routing

* no-mistakes(review): Enforce release replay mode matching

* no-mistakes(review): Prevent duplicate decision cards and released replay mismatches

* no-mistakes(review): Preserve answer columns and legacy resolve replays

* no-mistakes(document): Document strict replay and legacy compatibility

* no-mistakes(lint): Quote done literals to satisfy ShellCheck

* no-mistakes: apply CI fixes

* fix(rebase): keep collapsed captain hold board semantics
…id#2733)

* fix(watch): announce recovery once per generation and keep successors supervising

A lost Pi/OpenCode handling handshake re-announced the same recovery
generation on every cycle and spent the successor's first ~55s blind, so
a real crew event could be ignored and then dropped. Record the
announcement in the durable marker, confirm the handshake before the
follow-up without swallowing failure, and enter the poll loop immediately.

* no-mistakes(review): Tighten recovery event timing regression

* no-mistakes(document): Document recovery-loop supervision guarantees
* fix(bin): signal a captain call resolved in the log but still held

A captain call has two records and closing one has never closed the
other: a `resolved [key=...]` line closes the status-log fold, while the
backlog task held for the captain closes only through
`fm-captain-hold.sh answer`. Answering on the status side alone left no
trace of the disagreement - the fold went quiet, the durable record kept
saying the captain owed an answer, and nothing warned. The defect was
never the separation; it was the silence.

Add `fm-captain-hold.sh diverged`, a read-only report of that
contradiction, and print it from `fm-wake-drain.sh` as a bounded RECORD
DIVERGENCE section beside OPEN DECISIONS on every drain. It flags one
condition: a task still open and still carrying the captain-hold
annotations whose key was closed on the status side by the resolve verb,
under the collapsed identity or the legacy derived one.

It closes nothing, ever. A captain call closed wrongly leaves review
entirely, which is worse than the noise, so both reconciliation
directions stay human-owned and the printed hint names both - a
resolution is not proof the captain ruled, since a call can dissolve on a
false premise or turn out to have been a question of fact.

Three states are deliberately not divergence: a `captain-held` close is
the verified transfer `complete` writes, a still-open keyed decision
belongs to the OPEN DECISIONS fold, and a captain call with no routed
work item is legitimate rather than incomplete, so routed work is no part
of the test.

`fm-classify-lib.sh` gains `status_key_closing_verb`, which reports how
the status side currently reads one key by replaying the existing
`_fm_decision_fold_line` rule rather than re-deriving it, so the two
closing verbs stay distinguishable in one place. The per-wake cost is one
`tasks-axi list`, one key scan per status log, and the precise per-key
fold only for a key that already names a still-open task; the call is
hard-bounded so a slow backlog tool can never delay wake presentation.

* fix(document): Correct divergence lifecycle documentation

* fix(document): Neutralize divergence lifecycle prose
…escalation while a worktree is written (kunchenguid#2524)

* fix(watch): re-arm supervision after an abandoned auto-arm claim

A Claude auto-arm cycle that armed, delivered one rewake, and exited left
its single-flight lock behind. Both Stop-event participants then deferred
to that lock forever, because its recorded pid was still live: the
turn-end guard read it as recovery under way and allowed the stop, and the
next Stop firing treated it as another owner and declined to arm. On
2026-08-14 a home with two tasks in flight lost supervision for about 40
minutes with no watcher process and no watcher lock, its beacon frozen at
the one delivery, and both crewmates' finished reports sat in the durable
queue until an operator drained it by hand.

Abandonment is now proven from the epoch ledger instead of inferred from
pid liveness. A lock whose holder pid matches the ledger's own owner_pid
while the recorded outcome is anything other than arming has already
finished its decision, so that claim is reclaimed under the lock's steal
mutex, stops counting as recovery ownership in the guard, and is cleared
by the guard's terminal check rather than deferred to. A failed clear
re-blocks instead of allowing a blind stop, and an arming entry stays in
flight however old it is, because its owner foregrounds the arm for the
whole watcher cycle.

Issue kunchenguid#2251's PR kunchenguid#2263 does not cover this failure. It is closed and
unmerged, lives entirely in bin/fm-watch-arm.sh, and retires the stalled
watcher and matching stale watcher lock of an arm that is currently
running. Here no arm and no watcher were running and no watcher lock
existed, so it has nothing to retire and the home stays blind.

tests/fm-claude-stop-autoarm.test.sh covers the reclaim, the still-arming
and unnamed-owner cases that must keep the gate closed, and the failed
clear. tests/fm-turnend-guard.test.sh covers the guard side of the same
boundary. Both fail without this change.

* fix(watch): defer a wedge escalation while the task worktree is written

The wedge detector had two inputs, rendered pane quietness and the run
step, and neither can see a crew that is writing source, then tests, then
documentation behind a static pane. On 2026-08-14 one crewmate produced
eight consecutive possible-wedge escalations in a single afternoon, three
of them demanding deep inspection, while it was demonstrably working and
then committed. Every one of them cost a supervision turn to disprove by
hand.

Add write activity inside the crew's own recorded worktree as a third
liveness input. crew_worktree_written_since compares the worktree against
the caller's existing idle-window timer file, so -newer needs no clock
arithmetic, no temp file, and no portable mtime write. The probe runs only
inside the branch that was about to escalate, which bounds it to one
pruned, depth-bounded walk per window per FM_STALE_ESCALATE_SECS and
leaves the per-poll stale sweep exactly as cheap as before.

Positive evidence defers rather than cancels. The idle timer restarts so
the next window probes again, the escalation counter is neither advanced
nor reset so a later genuine wedge keeps the demand-deep-inspection
history it earned, and a .writing-since marker ages the whole deferral
chain so the pane still re-surfaces once per FM_PAUSE_RESURFACE_SECS,
through the same throttle shape a declared pause already uses, labeled as
a recheck rather than a wedge. This can only reduce false positives: every
absence of evidence, including no recorded worktree, a torn-down worktree,
a missing anchor, and a failed walk, falls through to the unchanged
escalation schedule, so a crew that writes nothing still escalates on the
existing timetable.

What the signal cannot see, by design or by construction:

- CPU burn with no writes, such as a long compaction, is invisible. That
  case keeps the old behavior exactly.
- A commit-only phase writes only .git, which is pruned first so that
  firstmate's own read-only git commands against the worktree can never
  make the probe self-fulfilling.
- Writes under the pruned generated trees, or deeper than
  FM_WORKTREE_WRITE_MAXDEPTH, do not count.
- The probe cannot attribute a write to the crew, so a background build or
  another process touching the tree looks the same. The hourly re-surface
  is what bounds that, and a churny file cannot buy silence.
- The away-mode daemon's own escalation path is deliberately untouched.

tests/fm-watch-triage.test.sh covers the classifier including the .git
prune, both halves of the live case on one fixture (quiet plus writing
defers, quiet plus silent still escalates and counts), and the bounded
re-surface. All three fail without this change.

* no-mistakes(review): prove autoarm claims by identity; skip mate-home write probe

* no-mistakes(document): document away-mode wedge boundary and probe filesystem limit

* no-mistakes(document): qualify turn-end recovery condition for abandoned auto-arm claims

* fix(watch): keep a write deferral scoped to its own idle window

Two consistency gaps in the worktree write probe, both found while reviewing
the wedge-deferral change on this branch.

A write deferral is a bounded chain: its .writing-since marker ages the whole
chain so a churning worktree still re-surfaces once per resurface window. That
is only sound while the chain belongs to the current quiet stretch, so every
path that restarts the idle-window timer has to drop it too. Two did not: the
corrupt-timer repair in wedge_timer_check, and both first-sight branches for a
captain-relevant status. A chain left over from an earlier quiet stretch made
the first deferral of the new window re-surface immediately instead of after a
full fresh window.

FM_WORKTREE_WRITE_PRUNE is a skip list, so clearing it reads as "skip nothing"
and is the obvious way to widen the probe to the whole depth-bounded tree.
Instead an empty list reported no evidence at all, quietly costing the wedge
detector its third liveness input on a home that meant to widen the walk. An
empty list now widens the walk, and the header says so.

Neither change alters when a stall that writes nothing escalates.

Regressions in tests/fm-watch-triage.test.sh cover all three paths and each
one fails on the pre-fix code.

* no-mistakes(review): honor an empty write-prune, bound the probe, share window_key

* no-mistakes(document): align probe knob count and guard regression-coverage ownership

* no-mistakes(lint): silence deliberate single-quote SC2016 in write-prune env test
…lared pause (kunchenguid#2748)

* fix(bin): give a captain hold the same bounded pause cadence as a declared pause

Two supervisors read a finished task's last status line and disagreed about which
declarations mean an idle endpoint is expected. bin/fm-inactive-reconcile.sh
suppresses its inactive-outcome scan only on `captain-held`, while the away-mode
daemon's wedge path gated deferral on `paused` alone. Both read the LAST line, so
the two verbs are mutually exclusive and no finished task waiting on a person
could satisfy both at once. Marking 11 such tasks `captain-held:` silenced the
900s outcome scan and immediately produced five possible-wedge escalations in one
batch, because the 240s wedge detector no longer saw a pause verb.

fm-classify-lib.sh's status_is_paused_or_captain_held already owns the combined
question, and bin/fm-watch.sh's ordinary-crew wedge path already asked it. This
extends that same answer to the paths still asking the narrower one:

- bin/fm-supervise-daemon.sh, all six sites, which form one subsystem and have to
  move together. classify_stale returns the pause action, reconcile_pause_tracking
  and migrate_watcher_pause_markers record and migrate the marker, and
  housekeeping defers the wedge and then re-surfaces the recheck. Changing only
  the stale-persistence gate would defer the escalation while
  reconcile_pause_tracking recorded nothing, so the wedge marker would persist and
  the sweep would `continue` past it forever: quiet, but never re-surfacing.
- bin/fm-watch.sh's secondmate stale gate, whose downstream owner
  pause_state_class already treats both declarations identically.
- bin/fm-push-transition-lib.sh's absorb, where either declaration already names
  the human the transition would report and the wait is already durably recorded.

Quieting alone would be half a fix, so the bounded re-surface had to reach a hold
too. A hold has no current-state mapping, unlike `paused`, so authoritative crew
state reports it as unknown and pause_state_class received `none`. An ordinary
crew recovers pause classification from that state through confirmed agent death,
which proves no live decision gate is being silenced. A secondmate's endpoint
liveness is deliberately never read there, because an idle mate is healthy by
design, so that confirmation is unavailable by construction and cannot be
required: without recovering the classification for a mate, every caller silenced
a held mate outright and its hold would rot invisibly. That promotion is bounded
by the declared-wait guard at the top of the function, so it can only reclassify a
task that already declared a wait and shows no positive working evidence.

Two narrow `status_is_paused` calls are deliberately left alone.
bin/fm-crew-state.sh's map_log_state is a current-state reporting contract, not a
wedge path; reporting a hold as `paused` would erase the distinction
status_key_closing_verb and fm-captain-hold.sh depend on, where a `captain-held`
close is a verified durable transfer and a `resolved` close claims outright
settlement. fm-classify-lib.sh's call inside status_is_captain_relevant needs no
change because that function's own case list already returns non-relevant for
`captain-held`.

bin/fm-inactive-reconcile.sh keeps its `captain-held` suppression as it is. Its
guard exists because a finished task's crew state still reports done from a
higher-priority source than the log, and a declared pause needs no such guard: the
scan only reports done or failed, and nothing else reaches its record path.
Widening it would change a separate subsystem's reporting contract, which this
defect does not require.

Coverage extends the existing colocated patterns for these predicates and asserts
both halves. tests/fm-daemon.test.sh covers the classification, the wedge marker
converting to pause tracking with no escalation, the bounded re-surface with its
window reset, and the boundary case where an answered hold stops claiming the
cadence. tests/fm-watch-triage.test.sh covers a held secondmate re-surfacing on
the same bounded cadence without being labeled a wedge.
tests/fm-supervision-events.test.sh covers the absorbed push transition. Every one
of these fails on the pre-fix code except the answered-hold boundary case, which
is there to pin that the quieting was not widened too far.

The `paused:` workaround appended to those 11 tasks is live supervision state and
is untouched here. It can be retired once this lands.

* no-mistakes(review): name the captain in a held task's bounded recheck

* no-mistakes(document): extend declared-wait supervision docs to captain-held holds
…guid#2758)

* fix(lint): name the installer when ShellCheck or actionlint is missing

A missing actionlint exited 127 like a bare command-not-found. Fail with
exit 1 and point at the pinned installer, matching the missing-ShellCheck
path, without weakening the version pin.

* test: isolate kimi and muse detection from inherited Cursor markers

Harness detection checks CURSOR_AGENT before ancestry, so these
markerless-adapter cases failed when the suite itself ran under Cursor.
Clear the verified markers the same way the secondmate harness tests already do.

* no-mistakes(document): Document Muse Cursor marker cleanup
…lled but inert (kunchenguid#2684)

* feat(checks): report tool updates that are available or installed but inert

Firstmate had no way to notice that tooling this home depends on needs an
update, and no way at all to notice the worse case: an update that installed
correctly and then did nothing.

That second case is why this exists. A tool that self-installs into
~/.local/bin while a version manager keeps its own older copy earlier on PATH
looks completely up to date to anything that asks only "is a newer version
published". On 2026-08-20 a Herdr update landed at 0.8.2 while an older 0.8.0
copy stayed earlier on PATH, so every Herdr command failed on a protocol
mismatch and firstmate could not read its own fleet.

bin/fm-tool-update-check.sh reports the two conditions separately:

  <tool> update available      a newer version exists at the update source.
  <tool> update not in effect  a newer copy is installed on this host, but
                               PATH still resolves an older one.

PATH skew is measured, never inferred. Every executable copy of a watched
command on PATH is asked for its own version and those answers are compared,
so one lookup cannot hide the skew, and a directory name is never read as a
version because a version manager's "latest" directory can hold an older
build. A copy that will not report a version is a check failure, not a pass.

The watched tools live in local, gitignored config/watched-tools.json, so
adding a tool is a config edit rather than a code change, and the file is
never propagated to another home. Update sources cover both shapes: a local
clone's commit distance from its remote branch, and a command's own version
and update announcement, including a tool like no-mistakes that prints its
version on one command and announces a new release on another.

The check prints one line when something needs attention and prints nothing
otherwise, so it rides the existing watcher state-check contract with its
trust binding instead of introducing a schedule of its own, and
state/.tool-updates keeps the same pending update from being reported on
every poll.

The check only reports. It never installs, updates, reorders PATH, touches a
version manager, or fetches into a watched repository; every git probe is
read-only.

Tests cover the skew case as a regression, and it was verified by mutation:
removing the skew report, or stopping after the first PATH hit as a single
lookup would, each make that test fail.

* no-mistakes(review): fix tool update check probe reporting, budget, and shim write

* no-mistakes(review): keep sweeps alive on broken patterns and oversized budgets

* no-mistakes(review): roll back failed arm, widen budget clamp, bound repo probe

* no-mistakes(review): guard git probes at the budget, record uncut findings

* no-mistakes(document): fix stale watched-tool report-record wording in docs and header

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

The behavior shard's watch-triage suite failed on the new worktree-write wedge
tests. Those five tests are the only ones in the file that do not use its
standard waits. They give a fixed 3 second liveness budget to the one poll that
now spawns the bounded worktree walk, and 4 seconds to an escalating watcher
where every other test in the file gives 10. On a loaded runner that poll
outlives the fixed budget, so the round is reaped before the deferral it asserts
on is recorded, and the test reports a lost deferral instead of the deferral
under test. Wait for a completed poll cycle through the file's own
wait_poll_cycle, which is what its header documents this hazard for, and use the
file's standard 100 tick exit budget.

Verified against a load that reproduces the failure: 11 of 12 runs failed
before, 8 of 8 pass after. Verified by mutation too, so the waits still prove
the behavior: removing the write deferral, and keeping a finished deferral chain
across an idle-timer repair, each still fail their test.
* fix: treat yolo as merge authority only, not ask-user finding authority

Yolo on/off was documented as also deciding no-mistakes ask-user findings, which hid firstmate's duty to judge unambiguous-toward-design findings itself. Keep every safety boundary; this is a contract clarification, not a relaxation.

* no-mistakes(document): Clarify yolo documentation ownership and merge posture
… work over (kunchenguid#2767)

* feat(voice): spoken round trip on Nova Sonic 2 with a measured relay cost

Step one of the spoken interface: the laptop captures and plays audio, this
desktop holds the model session, and no AWS credential leaves the desktop.

Measured, amazon.nova-2-sonic-v1:0 in eu-north-1, end of speech to first byte
of reply audio, 6 runs each, all answered, on a question that forces a records
read:

  relay path   1.229 1.379 1.428 1.447 1.481 1.516  median 1.438
  direct       1.147 1.179 1.203 1.237 1.244 1.317  median 1.220

The relay costs about 0.22s of the median. The direct figure reproduces the
earlier survey, which is what makes it a usable control. Excluded: the
captain's own ssh round trip, microphone capture, and speaker output. This
desktop has no microphone and no speaker, so every run used audio files.

Three pieces:

  bin/fm-voice-relay.py    holds the conversation on this host
  bin/fm_voice_records.py  what a spoken answer may read, and the handover
  bin/fm-voice-client.py   the laptop end; audio devices UNVERIFIED
  bin/fm_voice_frame.py    the wire format both machines share

Real work is handed to the existing bin/fm-inbox.sh rather than a second
queueing surface, and the agent says it is handing over rather than answering
as firstmate.

Read scope: Done history and free-form note bodies are never assembled at any
scope, so the wide default cannot reach the places commercial detail
accumulates. config/voice-read-scope narrows it to counts only, and
config/voice-read-deny excludes a named item in one line. The boundary is an
executable test that widening the reader fails.

Push to talk is the default because it is cheaper and the choice is still open;
--listen open-mic is the single flip.

Two traps worth knowing: a clip with no trailing silence is never answered, and
the end of a reply is contentEnd with stopReason END_TURN, not completionEnd.
A second user turn in one session is treated as barge-in unconditionally, and
an interrupted turn that calls a tool is lost, so the session reconnects per
turn and gives up conversational memory. That is the concrete thing step three
has to solve.

* no-mistakes(review): fix voice relay credential reuse, frame validation and record parsing

* no-mistakes(review): test uplink header guard, bound unknown expiry, align state dir

* no-mistakes(review): decide deny per item, guard turn failures, bound ambient credentials

* no-mistakes(review): read account config from home, harden deny and turn failures

* no-mistakes(review): close status verb set, fix inbox help, pair data override

* no-mistakes(review): keep profile-free relay alive, unblock loop, fix dead assertion

* no-mistakes(review): hide finished pull requests, refuse open mic, keep suite offline

* no-mistakes(review): survive reader failures, release devices, fix claims

A failure while handling a model event, or while sending a tool result,
left the reader task dead with ended and turn_done clear, and close()
re-raised the stored failure on every await. One dropped stream became a
relay that could never build another session. The reader now reports the
session over in a finally whatever killed it, and close() absorbs the
task the same way it already absorbed its sends.

The laptop client releases what it already started when a later startup
step refuses, SystemExit from the handshake wait included, and names a
device refusal instead of leaking a raw PortAudio error. Whether it
releases correctly against a real device is still unverified here.

The records docstring claimed every reading was filtered to open ids.
Only the pull request count and list are; the worker count and the state
histogram cover every live runtime record, finished ids included,
because a meta file still on disk still needs tearing down.

The finished-work deny half of the suite asserted things that held with
the deny list absent. It is replaced by a deny on an open title, which
removes the row and says so while the count stays honest.

* no-mistakes(review): name reader failures, split file and device refusals

A failure inside the model reader released the waiting turn and told
nobody. The session was not marked spent, no notice reached the client,
and the client waits for a reply end or a notice, so the captain got
their whole timeout of silence and then a record saying the turn went
unanswered with nothing about why. Both ends of the relay now name a
failed turn through one function, once per turn, and --self-test carries
the cause in relay_error the way the client's own record does.

Two things that are not failures stay that way. A stream that simply
ends is the end of a session, which serve still reads on its own terms.
A stream that goes away because close() asked it to is an ordinary
renew, and announcing it would have put a failure notice in front of the
captain on every turn.

On the laptop end, the refusal that became a device error covered the
file-backed playback and capture too, so a mistyped --in-file was
reported as an audio device failure and the advice named the flag that
had just failed. The file ends now report the path and the flag that
chose it and stay an OSError; the device ends keep the device advice and
name the flag for that end. The device paths remain unrun here, so only
the file halves are covered by a test.

* no-mistakes(test): survive model session end, order client turn frames

* no-mistakes(document): sync voice relay docs with reviewed relay behavior

* no-mistakes(document): re-measure relay latency and correct its cause

* no-mistakes(document): correct measurement date and name the unmeasured SSH hop

* no-mistakes(document): describe the unpublished control measurement, fix list formatting

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes
…unchenguid#2763)

* fix: keep Relay public loops open until retire

Delivering a promised-final reply was deleting the only record that tied a public thread to later work, so a follow-on ship silently owed no closing reply. Retain the registration after delivery, rechain follow-on work onto the same thread, and make retire --reason the only close.

* no-mistakes(review): Propagate public follow-up registration removal failures

* no-mistakes(review): Persist retire receipts and align parent resolution

* no-mistakes(review): Make rechain resumable after partial obligation creation

* no-mistakes(review): Repair follow-up state, briefs, and expiry escalation

* no-mistakes(review): Serialize follow-up delivery stamps with retirement

* no-mistakes(review): Serialize rechain claims and protect registration terminal states

* no-mistakes(review): Avoid reporting retired delivery loops as open

* no-mistakes(document): Refresh public-loop documentation and verification evidence

* no-mistakes: apply CI fixes

* no-mistakes(review): Preserve delivered follow-up bindings during registration replay

* no-mistakes(review): Harden public follow-up retirement and rechain races

* no-mistakes(review): Fail closed on unresolved secondmate retirement

* no-mistakes(review): Bind secondmate cleanup to its recorded canonical home

* no-mistakes(review): Fix rechain command output and expiry validation

* no-mistakes(review): Validate brief keys and warn on remote promotion

* no-mistakes(document): Document retained public follow-up loops

* no-mistakes(lint): Remove unused bounded-wait loop variable
…ath (kunchenguid#2779)

* feat(bin): merge GitLab merge requests through the guarded PR merge path

bin/fm-pr-lib.sh already parses a GitLab merge request URL for the watcher,
but bin/fm-pr-merge.sh refused every non-github provider, so a merge request
had to be merged by hand and got none of the recording, guards, or audit
trail a pull request gets.

The merge path now dispatches on the parsed provider. A GitHub URL keeps its
exact previous behavior. A GitLab URL is addressed through glab by the project
URL rebuilt from the parsed host and path, so a merge request on any instance
resolves and no host is hardcoded, and no merge-method flag is added because
the project's own merge method is what should apply.

A GitLab merge happens only after one live read of the merge request confirms
it is open, detailed_merge_status is mergeable, has_conflicts is false,
blocking_discussions_resolved is true, and the head pipeline succeeded at the
exact current head. Every failing condition is reported, not just the first.
The verified head is bound to the merge with glab's --sha, so a push landing
between the read and the merge fails the merge instead of landing commits
nothing verified. Recorded metadata is never the authority for any of this: a
rebase moves the head and leaves a recorded value stale, so a recorded head
that disagrees with the live one is reported rather than trusted, and the
recorded value is read before the recording step because that step drops a
GitLab head it cannot resolve.

* no-mistakes(review): reject bundled -R clusters and make tool-absence cases host-independent

* no-mistakes(test): state authorised GitHub narrowing of bundled -R guard

This branch NARROWS GitHub behaviour. The narrowing was authorised
deliberately rather than slipping in by accident, and it applies to both
providers, GitHub and GitLab alike, because a script that guards one provider
and not the other is a trap for the next reader.

What bin/fm-pr-merge.sh now refuses is extra merge arguments containing a
bundled short-option cluster that includes R, for example "-dR other/repo".
The forge CLIs expand such a cluster one character at a time, so it carries
"--repo other/repo", and that later value wins over the repository the URL
named. Before this change, "fm-pr-merge.sh <task> <github-url> -- -dR
other/repo" reached "gh-axi pr merge 12 --repo example/repo --squash -dR
other/repo" and exited 0 with pr= recorded and the merge poll armed. It now
exits 1 with "extra merge arguments must not override the repository", records
nothing, and invokes no forge merge command. Every other GitHub invocation is
byte-identical to the base commit.

Closing that hole honours the existing rule rather than departing from it. The
file header already forbids --repo and -R because the repository must come
only from the URL, so a bundled cluster carrying a repository override was
never legitimate behaviour to preserve: it was that guard being evaded.
Redirecting a merge to a repository the URL does not name is exactly what the
guard exists to prevent.

The refusal is already pinned on both paths by the existing case
test_bundled_repo_override_args_refuse_before_recording in
tests/fm-pr-merge.test.sh. On GitHub ("-dR wrong/repo") and on GitLab ("-yR
https://other.example/g/p") it asserts exit 1, the refusal wording, no pr= in
the task meta, no armed merge poll, and no forge merge command invoked, with a
control case proving a cluster that carries no repository override still
reaches the forge. No duplicate assertion was added. Both assertions were
confirmed to have teeth by narrowing the guard back to a bare -R and watching
each path fail.

This commit carries no file change: the guard and its coverage landed in
614853d, and this message exists so the pull request description states the
narrowing.

* no-mistakes(document): fix README pointer for GitLab watch and merge doc

* no-mistakes: apply CI fixes
kunchenguid#2788)

* no-mistakes: apply CI fixes

* fix(bin): drop a private record citation and narrow the review rule

Three corrections to the spoken interface that landed in kunchenguid#2767, plus one
fix carried over from that branch after its pull request had already been
merged.

The confidentiality fix. The module docstring of bin/fm-voice-relay.py
cited a private, gitignored fleet record by exact path and section number.
That widens what this public repository points at, and it cannot resolve
for any reader here, because the path has never been in the repository.
Both traps it pointed at are already described in full in the list
immediately below it, and docs/voice-relay.md carries the same two for
operators with no citation at all, so the pointer is removed and no claim
is weakened by losing it. Two comments that referred to "the survey" as
though it were something a reader could open are reworded the same way.
Neither exposed a path, so that half is comprehensibility rather than
confidentiality.

The review rule. .greptile/rules.md is kept, because its conditions are
right and deleting it would leave the next reviewer to re-litigate a
decision already argued out. What was wrong with it is narrower than its
existence: it read as settled repository policy, when whether VISION.md
itself should be reconciled is an open question belonging to the captain.
One sentence now says so, and says that the conditions listed below it are
what the interpretation depends on. That narrows the claim rather than
widening it.

The carried-over fix. The first commit on this branch is 7f98e79 from
fm/voice-relay-build-v4, taken verbatim rather than rewritten. It closes
the window where a transport failure was recorded and then erased, so a
run could be emitted as answered false with relay_error null. That matters
more than it looks: relay_error is the field that keeps an infrastructure
failure from being averaged into a latency figure, so the failure mode is
a dead connection wearing the costume of a slow reply. It landed fifteen
minutes after kunchenguid#2767 merged and so never reached the default branch.

* no-mistakes(review): name a reason on every unanswered-turn close path

* no-mistakes(review): guard the downlink body and pin frames to their turn

* no-mistakes(review): attribute reply audio to its own turn and tell endings apart

* no-mistakes(review): tell a cut-short reply from an unanswered turn

* no-mistakes(review): discard reply audio arriving after the output closes

* no-mistakes(review): count discarded reply audio on the speaker path too

* no-mistakes(review): keep a reason off a turn already answered in full

* no-mistakes(review): say a reset cut a reply short, not that none arrived

* no-mistakes(review): read one turn's audio count once, and hush a tidy exit

* no-mistakes(document): fix stale session-end relay_error claim in voice-relay guide
…unchenguid#2811)

A pi worker parked on an interactive prompt - a permission dialog, a
question menu, a trust dialog - reports agent_status=blocked, because it
is waiting on a human keystroke. Pi draws that menu above its separator
pair, so the composer region between the rules is blank and structure
alone looks like a free composer. _fm_composer_pi_verdict admitted
blocked alongside idle and done, so the shared classifier reported an
affirmatively empty composer for exactly the pane where typing is unsafe.

Every "is it safe to type here?" consumer reads that verdict and proceeds
only on an affirmative empty, so both are told yes on a parked prompt:
the away-mode injection guard in bin/fm-supervise-daemon.sh, and fm-send's
pre-type refusal. The keys then answer the menu instead of composing a
message - the highlighted default is selected, the text is discarded, and
the record attributes a decision to a human who never made it.

blocked now defers to unknown, which every consumer already treats as
fail-closed. idle and done still prove an empty composer, so ordinary
steering is unchanged, and Cursor is unaffected because its always-blocked
panes never reach this pi-only branch.

Regression coverage lands first at both levels: the verdict owner
(a blocked pi defers) and the herdr adapter (a parked pi prompt is not an
empty composer).
…2849)

* fix(bin): require a clone root before fleet-sync touches a project

Git repository discovery walks upward, so `git -C projects/<dir>` on a plain
directory nested under projects/ resolves to the enclosing repository - in a
firstmate home, the firstmate checkout itself. fm-fleet-sync.sh guarded its
candidates with `rev-parse --is-inside-work-tree`, which such a directory
passes, so every later git call read, pruned and fast-forwarded firstmate's own
default branch and reported it under the project directory's label. A running
session's AGENTS.md changed underneath it, and the report named a project that
had nothing to do with the change.

Require each candidate to be the root of its own work tree before any other git
command: compare `rev-parse --show-toplevel` against the directory's own
physical path. Both sides are physical, so a symlinked clone still compares
equal. Anything else is skipped by name, naming the repository that would have
been touched, and bootstrap relays that as a FLEET_SYNC line.

Regression coverage reproduces the wrong-repo fast-forward against a home nested
inside another repository, in both the whole-fleet and single-project forms, and
pins that a symlinked clone dir still syncs.

* no-mistakes(review): Keep enclosing fixture clean during clone-root regression
pablontiv and others added 25 commits September 14, 2026 15:38
…ailure (kunchenguid#4474)

* fix(bin): recover Claude auto-arm after timeout

* no-mistakes(document): Add host-timeout signal coverage to autoarm test-coverage list
* fix(spawn): establish Claude task channel authority

* no-mistakes(document): Document Claude task-worker control-channel trust in harness-adapters reference
…or pending text (kunchenguid#4458)

* fix: guard relaunch exit against pending input

* no-mistakes(review): Verifying test run in progress

* no-mistakes(document): docs(agent-control): document exit's composer-empty fail-safe guard

* no-mistakes(ci): fixed 2 tests broken by approved do_exit fail-safe change (empty-only composer gate). herdr-smoke test's sleep-stand-in never renders a real composer -> updated assertion to expect "not proven empty" refusal instead of stale "did not stop" msg. secondmate-restart fake tmux capture-pane returned bare '> ' glyph (never valid empty proof) -> changed to bordered empty box matching fm-control-relaunch fixture. all 4 related suites pass locally now
…unchenguid#4460)

* fix: reconcile diverged secondmate updates

* no-mistakes(document): Fix stale fm-update.sh/fm-ff-lib.sh purpose lines in docs/scripts.md

* no-mistakes(document): docs: reflect secondmate divergence reconcile in README/SKILL.md
…d#4497)

* fix(dispatch): support Codex Luna max effort

* no-mistakes(review): use portable CODEX_HOME path in codex effort reference
kunchenguid#4498)

* feat(calm): render smooth Unicode swell

* feat(calm): make sails asymmetric

* feat(calm): use quarter sail glyph

* no-mistakes(review): docs: sync calm feasibility sprite passage with approved renderer

* no-mistakes(document): docs: sync calm wave phase doc comment

* no-mistakes(ci): CI の Lint 失敗は tests/fm-calm-pi-extension.test.sh の test_interactive_terminal_e2e 関数で `boat_narrow_sails` が local 宣言に残っていたことによる ShellCheck SC2034 でした。関数内での参照を確認したところ、狭幅端末の検査は boat_narrow_previous / boat_narrow_direction / boat_narrow_reversed に移行済みで、boat_narrow_sails は代入も参照も一切ありませんでした。そのため local 宣言からこの 1 語のみを削除しました(3315 行目)。Calm の描画実装、他のテストアサーション、ドキュメントは変更していません。検証: bin/fm-lint.sh(ローカル変更ファイルモード)exit 0、CI 相当の `shellcheck --norc --external-sources tests/fm-calm-pi-extension.test.sh` exit 0(SC2034 解消)、`bash -n` 構文チェック通過、actionlint 1.7.12 でワークフロー 3 件 valid。
kunchenguid#4491)

* fix: supersede scout delivery brief on promotion

* fix: preserve ship safety contract after promotion

* no-mistakes(document): Document fm-promote.sh now supersedes brief.md on relaunch
…d stop cleanup dropping accents from a held body (kunchenguid#4471)

* fix(bin): let captain holds work on hosts with an older JSON::PP

Holding a task for the captain, and the cleanup that keeps a captain-held row
open, both fail outright on any host whose JSON::PP defaults allow_nonref off -
2.27202 on a Linux desk is one. Both read a task's body back with `decode_json`,
but tasks-axi shows a scalar field as a JSON-encoded bare string, and an older
library rejects that whole value with "must be object or array".

The consequence is fleet-wide on such a host, not one broken command: a worker
there cannot formally record a decision for the captain at all. It can only
mention the decision in passing in a status line, where it can be missed - which
is how a real decision goes unrecorded. The hold reports that the task lost its
hold-set stamp; the cleanup cannot return the row to Queued.

Both call sites now ask for allow_nonref explicitly rather than inheriting
whatever the installed library defaults to. The second one is worth naming: its
`/\A"/` guard reads as deliberate, but a leading quote is exactly the bare-string
case that fails, so the guard selects for the failing input rather than
protecting against it.

The regression case forces the older default back off for every perl the commands
spawn, then drives both paths - holding a task that carries a body, and tearing
down a captain-held row whose deliverable must still be appended. It also probes
that the simulation genuinely rejects a bare scalar, so the case cannot pass
vacuously on a lenient host. Each half was verified failing on its own unfixed
call site with that site's real error message. Suites: fm-captain-hold-lifecycle
51 cases, fm-backlog-atomicity 99 cases, 0 failures.

Verification limit: the mechanism is reproduced and tested, but neither fix is
verified against a real JSON::PP 2.27202 host, because none is in the loop. This
laptop runs 4.06, where the bug does not manifest.

`bin/fm-procevent-lavish.sh:471` was checked and left alone - it matches a
brace-delimited object before decoding, so allow_nonref never applies.

* fix(bin): stop cleanup silently dropping accented characters from a held body

Cleanup rewrites a captain-held row's body to append the finished work's
deliverable, and the decoder it reads that body with printed decoded characters
to a stream with no `:raw` layer. A character at or below U+00FF then came out
as one latin-1 byte instead of two UTF-8 ones, so a body reading "café" lost the
accent. `fm_backlog_retain` writes that body straight back through
`--body-file`, and nothing reported an error - the character was simply gone
from a row still waiting on the captain.

The decoder now writes bytes, the same `binmode STDOUT, ":raw"` plus
`utf8::encode` that the sibling decoder in `bin/fm-captain-hold.sh` already
used.

Review of the parent commit found this on one of the lines that commit already
changed. It predates that change.

The test asserts bytes rather than decoded strings, because comparing strings
cannot tell latin-1 from UTF-8. It uses two separate rows on purpose: any
character above U+00FF makes perl print the whole string as UTF-8, so one body
carrying both an accent and an em dash passes even unfixed and proves nothing.
Verified failing before the fix on the accented row, passing after. Suites:
fm-captain-hold-lifecycle 52 cases, fm-backlog-atomicity 99 cases, 0 failures.

* no-mistakes(document): record body-decode regression proofs in captain-hold lifecycle doc

* no-mistakes(review): drop whole-file UTF-8 check from retained-body test

* no-mistakes(review): correct stale JSON::PP fleet-host claim in lifecycle doc

* no-mistakes(review): anchor native-reproduction claims per defect in lifecycle doc
…furniture (kunchenguid#4532)

* fix(composer): read codex 0.154's idle starfield and status footer as furniture

codex-cli 0.154.0 animates a braille "starfield" around its idle composer:
on the row above the bold `›` prompt row, on the `›` row behind the SGR-2
dim `Ask Codex to do anything` placeholder, and on the row below it, then
draws a bright status footer (`<model> <effort>[ fast] · <path> · <title>`).
The cells are truecolor greys on both sides of the ghost luminance ceiling,
so the brighter ones survive ghost stripping, and the rows below the glyph
carry no structural edge. The shared classifier selected the bare `›` shape,
extended its wrap region over the two rows beneath the glyph, read the
survivors and the footer as wrapped typed input, and answered `pending`;
the steering doorbell defers on exactly that verdict, so no doorbell ever
reached an idle codex 0.154 pane.

bin/fm-composer-lib.sh now recognises that furniture by shape, declared
once next to the idle placeholders and reached from the two wrap-region
boundary points:
- a row whose non-whitespace content is entirely braille cells
  (U+2800..U+28FF, detected byte-exactly under LC_ALL=C) is furniture: it
  never counts as wrapped typed content and bounds a bare composer's wrap
  region; braille behind the glyph row's content is stripped before the
  emptiness decision when nothing else follows the glyph; a row mixing
  braille with other text stays typed content;
- the codex status footer bounds the wrap region exactly as omp's status
  row does, anchored on the effort token, a spaced middle dot, and a `~` or
  `/` path cell, so a typed `fix · tests` stays composer input;
- `^Ask Codex to do anything$` joins the verified idle-placeholder set; the
  ghost strip remains what proves that row empty, and the bare-row rule that
  bright placeholder text is real input is unchanged.

Unchanged: the strict blank-row rule, the styled=0 degradation (a plain
cmux/orca capture of this screen still reads `unknown`, never `pending`),
FM_COMPOSER_GHOST_LUMA_MAX, and every other harness's shape.

tests/fm-composer-lib.test.sh carries both live Herdr samples byte-for-byte
with the divergence (letters in place of the starfield read `pending`) and
the over-stripping negatives; tests/fm-composer-codex-idle-live-e2e.test.sh
is the default-on live guard (token-free, skips explicitly without codex or
tmux) that launches the installed codex idle and asserts `empty` through
both the tmux and the cursorless styled reads, naming codex --version on
failure. docs/verification/runtime-backends.md records the dated Herdr
evidence: `pending` before, `empty` after, on the captured screen.

* no-mistakes(review): drop unreachable codex footer rule and inert placeholder entry

---------

Co-authored-by: Todd Billings <todd@usdvcapital.com>
* fix(bin): refuse empty text steers in fm-send

A marked secondmate request sent with an empty message delivered only
marker and correlation bytes and minted a pending-reply expectation the
parent could never see resolved, stalling the fleet with no loud error
(kunchenguid#4255). Fail closed on an empty or whitespace-only message on the text
path, mirroring the existing --resolve-key refusal.

* chore: retain ambient Pi-lens autoformat as its own commit

Formatting-only edits produced by ambient Pi-lens autoformat during the
msg-loss investigation, kept separate from the behavioural change in
c23acba so the fix stays reviewable on its own.

AGENTS.md is deliberately excluded: its only autoformat edit stripped the
trailing space from the documented FM_OPERATIONAL_PREFIX value, which
bin/fm-operational-input.sh:28 defines as "FIRSTMATE_OP: " and line 11
records as permanent compatibility. Documenting that constant without its
trailing space makes the doc wrong about the contract, so that one line was
restored rather than retained.
…chenguid#4554)

On rose-pine-moon the two-color water (cyan crests over blue troughs) read as
a pink stripe over aqua, the yellow left sail and mast clashed with the red
right sail, and the hull carried a blue interior run. Every water cell is now
blue so the swell reads through glyph height alone, and both sail halves, the
mast, and the whole hull are one yellow run. Geometry, cadence, animation,
direction flip, resize clamping, and the narrow fallback are unchanged.

Update the unit and real-TUI color assertions to the new palette and the Calm
docs that described the old one.
…chenguid#4270)

* fix(watch): stop aging a second mate's active turn from its launch

The parent watcher's second-mate wake-loop stall check exempts a mate that
is demonstrably inside an active turn, but secondmate_in_active_turn asked
busy_turn_over_age first and returned "not in a turn" whenever that said
the bound was crossed.

busy_turn_over_age ages from state/<task>.turn-ended, falling back to
state/<task>.meta. A second mate's turns end in its own home, so the
parent never gets a turn-ended mark for it and the fallback ages the
mate's last launch. Every mate launched more than BUSY_TURN_MAX_SECS ago
was therefore permanently "over age", the busy pane was never consulted,
and any turn outstripping FM_SECONDMATE_WAKE_STALL_SECS raised a false
wake-loop stall.

The gate now bounds the busy exemption by <idle> - how long the queue's
drain position has not moved - which is evidence this home actually
holds. A busy mate stays exempt while the queue has been frozen for less
than BUSY_TURN_MAX_SECS, and a mate stuck busy forever still alarms, so
the bound that stops a busy pane from proving liveness forever is kept
rather than removed. busy_turn_over_age is untouched; its remaining
callers are the ordinary crew busy-pane bound.

The regression pins the case that actually broke: a mate whose launch
record predates BUSY_TURN_MAX_SECS and which is demonstrably mid-turn
must not escalate, while the same mate with its queue frozen past the
bound still publishes exactly one notification. The existing coverage
only exercised a freshly launched mate, which passes either way.

Reaching that alert now costs a pane capture inside the gate, so the
three checkpoints in this suite that assert an alert move from a 1s to a
4s bound - the value the neighbouring active-turn cases already use. The
bound is a ceiling, not a wait: the checkpoint returns on the first
actionable wake. On a loaded machine a 1s bound missed the alert
repeatedly; at 4s it did not miss in 20 runs under the same load.

* no-mistakes(review): scope the second-mate active-turn regression test's coverage claim

* no-mistakes(document): fix stale second-mate active-turn comments in fm-watch
…unchenguid#4278)

* feat(bin): add read-only PR blocker and reviewer-discovery commands

Two focused, opt-in commands that read GitHub and never write to it.

fm-pr-state.sh reports what still blocks one pull request from the
author's side: a closed or merged state, draft state, unknown or
conflicting mergeability, absent or failing required checks, and a
blocking CHANGES_REQUESTED decision explained by each reviewer's latest
verdict, marked STALE when it was left at a superseded head. A pull
request that only awaits an approval is not reported as blocked, and
advisory checks are omitted. Every reading is taken against one exact
head; a push that lands mid-read invalidates the whole result rather
than mixing two snapshots.

fm-pr-reviewers.sh suggests reviewers from the most recent commits to
the pull request's exact changed paths, counting each commit once,
resolving handles through GitHub's own commit author.login mapping, and
excluding the author and Bot accounts.

Both stay read-only: no review request, no approval, no merge.
Unresolved review-thread state is left unreported because the REST API
does not expose it and unattended commands may not use GraphQL.

Closes kunchenguid#3731

* no-mistakes(review): accept only PR URLs and stop at terminal state

* no-mistakes(review): report unconfirmed required checks; make URL-only guards discriminate

* no-mistakes(review): stop attributing readings to unverified heads

* no-mistakes(review): narrow readiness contract to checks that have reported

* no-mistakes(review): read the pull request once, drop the head guard

* no-mistakes(document): scope pr-forge isolation proof to its measured members

* no-mistakes(document): record uncovered pr-forge members and their pending proof

* docs(isolation-proof): re-prove pr-forge at its full membership

tests/fm-pr-state.test.sh and tests/fm-pr-reviewers.test.sh joined the
pr-forge family in this branch, and script_allows_concurrency grants
four workers by family membership alone, so both ran concurrently on a
proof measured before they existed.

Re-proved the family at all eight members: two consecutive runs, 0
failures, each begun with the one-minute load average below 6.0 so the
result measures isolation rather than contention. A third run taken
between them is disclosed rather than recorded, because it started
while the previous run's workers were still decaying.

The new durations are not comparable with the six-member measurement
above them, so they are not presented as evidence about the two new
members, and that record's 1.72x four-worker figure is left as a
statement about its own run rather than restated as current.

* no-mistakes(review): disclose gh error-text coupling at its matching site and tests
…uid#2752)

* fix(bin): teach validation-round pauses in briefs

* no-mistakes(document): Point classifier comments to authoritative pause examples
…guid#4510)

* fix(teardown): refuse a cleanup whose endpoint close failed

bin/fm-teardown.sh discarded both the exit status and the stderr of every
fm_backend_kill call, so a close that genuinely failed was indistinguishable
from one that succeeded. Teardown continued past it, deleted the task's durable
records, returned its worktree, and reported the cleanup as completed. The
deleted metadata is the only record of which endpoint belongs to the task, so
such a close did not merely leave a stray session behind, it stranded one:
nothing was left on disk naming it.

The adapters could not carry that signal either. Driven against the real code,
every backend arm returned 0 for a genuine failure exactly as it did for an
already-exited endpoint, so there was nothing for the four call sites to
propagate even once they stopped swallowing it.

The tmux arm now resolves a close that did not succeed against the window's
exact recorded identity, since kill-window fails the same way for a window that
is gone and one that is still there. The Orca arm reports a close its missing
CLI never attempted. Both stay silent for an endpoint that is already
legitimately gone, and the remaining arms are unchanged: their close-command
timing cannot be established without the real Zellij, Orca, and cmux binaries,
and a gate that refused ordinary cleanup of an already-exited session would be
worse than the defect. docs/verification/runtime-backends.md records what each
backend can prove.

A reported close failure now reaches teardown's existing retain-and-stop
refusal before the records naming the endpoint are removed, matching where the
Herdr confirmed-gone gates already sit for the same hazard, and the retained
records let a rerun finish once the close works.

* no-mistakes(review): refuse unreadable tmux close re-read; honor --force override

* no-mistakes(review): drop unreachable Orca force arm; prove CLI-absent close

* no-mistakes(document): document endpoint-close refusal in its backend and retirement owners

* no-mistakes(ci): The two reported failing checks are NOT code defects. Both "CI" (run 34935529184) and "Require no-mistakes" (run 34935529206) returned conclusion=action_required with zero jobs and 0s duration (run_started_at == updated_at), which is this repo's workflow-approval gate holding the run before any job starts. No job executed, so nothing in the diff could have caused them; two unrelated branches (fm/captain-hold-json-nonref, fm/presenter-core-l1) show the identical shape in the same time window. Verified the change locally instead: bin/fm-lint.sh clean, bin/fm-test-run.sh --check-coverage ok, and all suites the diff touches pass (fm-teardown-endpoint-safety 25/25 including the five new endpoint-close cases, fm-backend-orca, fm-backend, fm-backend-tmux-smoke, fm-backend-cmux, fm-backend-zellij, fm-backend-herdr). Separately, I found and fixed a genuinely flaky test that the phase rules require me to make deterministic: tests/fm-tmux-agent-liveness.test.sh intermittently failed "an idle shell pane must classify dead" (verdict ambiguous, comms=[bash sleep]). It is selected by --changed for this diff, so it would run against this PR once CI is approved. Root cause, established by instrumenting the pane's process group: the idle window was created by `new-session` with no command, so it inherited tmux's default-shell, i.e. whoever runs the suite. ps on the pane tty showed `-zsh` -> `bash` -> `sleep`, all sharing pgid==tpgid, i.e. the host operator's shell configuration spawning a periodic helper directly into the pane's FOREGROUND process group, which is the one surface the classifier reads. `sleep` classifies as `other`, so fg_other=1 and the verdict became `ambiguous` instead of `dead` whenever that helper overlapped the 10s poll window. Every other window in the suite runs an explicit command via new_window; the idle case was the only one whose process group the host defined. Fix (smallest root-cause, test-only, 1 line + explanatory comment): create the idle window with an explicit bare `/bin/sh` (`-- /bin/sh`), the same shell the neighbouring background case already execs. Its foreground group is now exactly one process (verified: `/bin/sh` alone), so no host configuration can inject into it. This flake is pre-existing and NOT caused by this PR: an interleaved A/B showed base commit da5e658 failing the identical case (2/6 runs) alongside head (3/7 runs), and the diff only extracted the tmux inventory read into a helper with identical semantics while never touching fm_backend_tmux_foreground_comms. After the fix: 8/8 consecutive passes, with lint and the coverage guard still clean. Change left uncommitted in the working tree
* feat(calm): ship the Claude Code Calm and sailboat mod behind the function-hooks flag

Add .claude/mods/firstmate-calm, a Claude Code mod (function-hooks plugin) that
brings Calm to Claude Code: the sailboat replaces the stock working row through a
Raster repainted on the sprite's own tick, and tool, tool-group, mid-turn narration,
and canonically classified operational user rows draw at zero height. /calm is
registered by the hooks module itself and toggles the same per-home config/calm
preference the Pi extension uses, so one choice applies on either harness; rows
redraw retroactively on toggle and stay hidden across claude --continue.

The mod loads only while Claude Code's default-off CLAUDE_CODE_ENABLE_FUNCTION_HOOKS
flag is on. Nothing sets that flag in any settings file, and the plugin carries no
command file, skill, agent, or classic hook, so it is a complete no-op while the
flag is off. The trusted project auto-loads it through an .agents/skills symlink,
the only path Claude Code scans for project plugins.

Extract the working-ship geometry, bounce track, cadences, and freeze/resume state
into a harness-neutral sprite core inside the mod (Claude Code refuses hooks-module
imports from outside the plugin folder) and have the Pi widget paint that core's
frames as standard ANSI, byte for byte as before; the Pi suite stays green. Classify
operational rows through a port of bin/fm-operational-input.sh's classify command
guarded by a corpus parity test against the shell owner.

Tests: portable Node checks (plugin shape, sprite parity with Pi's rendering,
Raster packing, policy, classifier parity), the mod's own claude plugin test suites
behind a default-on wrapper, and an opt-in live TUI guard proving the flag-off no-op,
the moving boat, hidden rows, the persisted toggle, and resume on Claude Code 2.1.272.

Docs: record the version-scoped Claude Code evidence and the three bounded gaps in
docs/calm-mode-feasibility.md, describe the Claude Code contract in docs/calm.md,
and make the shared preference, layout, and contributor notes harness-neutral.

* no-mistakes(review): Preserve colliding final replies and strengthen parser parity

* no-mistakes(review): Preserve final replies and strengthen canonical parity checks

* no-mistakes(review): Require exact function-hooks opt-in before Calm activation

* no-mistakes(review): Clarify Calm module loading and activation boundaries

* no-mistakes(review): Reset Calm presentation state across session starts

* no-mistakes(document): Refresh Calm session lifecycle documentation

* feat(calm): paint the Claude Code working ship in Claude's own theme colors

The captain picked the "Claude native" palette for the Claude Code mod's Raster:
every water cell takes the spinner blue of the active theme family (#93a5ff dark,
#5769f7 light) and the whole boat takes the Claude orange of the stock spinner
(#d77757), one water color and one boat color. The family follows the `theme`
setting's prefix, read at load through $.config.list and re-read on a
config.set of that row, with `auto` and custom themes falling back to the dark
set. The Pi extension keeps its standard ANSI blue and yellow, byte for byte.

Rename the shared sprite's color classes from hue names to `water` and `boat`,
since each harness now maps them to its own colors; geometry, motion, cadence,
and the activation gate are untouched.

Tests cover both palettes' packing and the family rule under Node, and the
plugin kit drives every theme value, a theme change mid-session, the Calm-off
pass-through, and inertness of the menu read while the flag is off. The docs
describe the Claude Code colors and record the guard passing on 2.1.273.

* no-mistakes(review): Use light palette for unresolved Claude themes

* no-mistakes(document): Refresh Claude Calm verification evidence
…kunchenguid#4586)

* fix(watch): honour a declared wait before wedge-escalating a quiet pane

wedge_timer_check escalated on elapsed idle time alone. Nothing asked
whether the worker had already said why its pane was quiet, so a lane
that declared a bounded external wait climbed the escalation ladder for
as long as the wait lasted, and past FM_WEDGE_DEMAND_INSPECT_COUNT every
repeat carried demand-deep-inspection - which by its own wording forbids
re-absorbing on the run-step or pane state, so the supervisor could not
use the evidence that was there either.

The generated brief promises that declaring `paused:` buys the long
recheck cadence instead of a wedge, but the timer was still reachable
while that declaration stood: a crew that declares a wait and then has an
active run or busy pane attributed to it is handed to the timer as
provably-working. The declaration is what the worker said about its own
silence, so it now outranks a liveness verdict that only says something
is running.

The consult runs in the at-threshold branch that was about to escalate,
beside the worktree walk already there, and costs one status-line read.
Either status-line record defers to the same FM_PAUSE_RESURFACE_SECS
recheck the declared-wait absorber already uses, so the wait is still
rechecked and cannot rot invisibly. Which verb declared it decides the
wording, because the two block on different people: a `paused:` wait is
owed by an external dependency and asks the reader to confirm it still
holds, while a `captain-held:` transfer is owed by the captain reading
the recheck and asks them to answer or release the hold. A hold is not
rechecked at all while the away-posture record exists, as on every other
captain-held path, and that absorb arms no throttle so the recheck is
owed in full on return.

A declared clearing time that has already passed stops counting, and a
lane that never declared one keeps the identical escalation schedule,
reason, count and demand-deep-inspection wording, so detection and its
worst-case time are unchanged. The deferral restarts the idle timer
rather than cancelling it, so a lane that stops waiting escalates again
within one threshold.

A lane quiet because its own validation run is parked at a gate awaiting
a human decision is deliberately out of scope: reading that state needs a
signal carrying who the wait is on and what clears it, rather than one
inferred from a parked verdict that also covers gates awaiting the
crewmate itself.

Tests pin both directions for each case and were each confirmed to fail
with the consult removed.

* no-mistakes(document): docs: honour declared waits in stale-escalation docs
* 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
True merge of kunchenguid/firstmate main (af1f2ea, PRs kunchenguid#1699..kunchenguid#4627) into the
fork, resolved with fc3684a as the effective base: PR #9 squash-synced to that
upstream commit without keeping ancestry, so the recorded merge-base was stale
and produced 114 conflicts where the real base produces 15.

Upstream wins everywhere. Fork features upstream lacks are ported onto
upstream's files:
- #2 delivery-evidence gate, now rendered by fm_dod_block in bin/fm-dod-lib.sh
  for the direct-PR and no-mistakes modes so promoted scouts receive it too.
- #3 bin/fm-issue-closure.sh post-merge report, run by fm-teardown after the
  fleet sync and before the home-summary refresh.
- #4 landed-work proofs (ls-remote branch lookup, merge-commit and file-by-file
  comparison, refusal evidence notes, FM_LS_REMOTE_TIMEOUT_SECS) unioned with
  upstream's PR_URL resolution in pr_is_merged; upstream's kunchenguid#3870 header
  paragraph is amended rather than replaced.
- #6 bin/fm-task-bootstrap.sh pre-launch hook plus tmux/zellij/cmux abort
  cleanup, placed after the pooled-base refresh and trust pre-registration and
  never on --relaunch; the abort arm sits inside the non-relaunch branch and
  the worktree binding precedes the slot claim.
- #5 Bearings end-to-end regression test kept (ci.yml count 59 -> 60); its
  argv-transport fix is superseded by upstream's --slurpfile transport
  (31cba0d).

Retired as superseded: #7 herdr submit postcondition and fm-send diagnostic
sidecar (upstream 87681a4 confirms a landed turn from a cleared composer,
steers ride the durable inbox, and bin/fm-control.sh proves /exit by agent
state); those files are upstream verbatim.

The tree differs from upstream/main in exactly 18 paths.
Two follow-ups to the upstream merge, both inside fork-ported code.

bin/fm-spawn.sh: the endpoint abort arm announced "the endpoint was
closed" unconditionally, even when fm_backend_kill had just failed, so a
refusal that told the operator to inspect the window was followed by a
claim the window no longer existed. Report the outcome that actually
happened instead, matching the warning form the neighbouring orca, herdr
and slot-claim arms already use. The call moves into an if-condition, so
errexit behaviour is unchanged.

bin/fm-test-run.sh: register measured serial duration hints for the two
fork-only suites, tests/fm-issue-closure.test.sh and
tests/fm-task-bootstrap.test.sh, so the coverage guard counts them as
measured and the lane packer can balance them like every other script.
Copilot AI lite review requested due to automatic review settings September 17, 2026 12:51
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 514 files, which is 414 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: f10897fa-d23d-4c8e-a2b8-af51f980bdda

📥 Commits

Reviewing files that changed from the base of the PR and between bdef528 and 8155425.

⛔ Files ignored due to path filters (1)
  • docs/pi-supervision-branch-poster.svg is excluded by !**/*.svg
📒 Files selected for processing (514)
  • .agents/skills/afk/SKILL.md
  • .agents/skills/ahoy/SKILL.md
  • .agents/skills/ask-user-authority/SKILL.md
  • .agents/skills/bearings/SKILL.md
  • .agents/skills/bearings/assets/board-template.html
  • .agents/skills/bootstrap-diagnostics/SKILL.md
  • .agents/skills/captain-hold-lifecycle/SKILL.md
  • .agents/skills/decision-hold-lifecycle/SKILL.md
  • .agents/skills/firstmate-calm
  • .agents/skills/firstmate-coding-guidelines/SKILL.md
  • .agents/skills/firstmate-orca/SKILL.md
  • .agents/skills/fmx-respond/SKILL.md
  • .agents/skills/harness-adapters/SKILL.md
  • .agents/skills/harness-adapters/references/common/control-and-recovery.md
  • .agents/skills/harness-adapters/references/common/dispatch.md
  • .agents/skills/harness-adapters/references/common/model-and-effort.md
  • .agents/skills/harness-adapters/references/common/primary-hooks.md
  • .agents/skills/harness-adapters/references/harness/agy.md
  • .agents/skills/harness-adapters/references/harness/claude.md
  • .agents/skills/harness-adapters/references/harness/codex.md
  • .agents/skills/harness-adapters/references/harness/cursor.md
  • .agents/skills/harness-adapters/references/harness/gemini.md
  • .agents/skills/harness-adapters/references/harness/grok.md
  • .agents/skills/harness-adapters/references/harness/kimi.md
  • .agents/skills/harness-adapters/references/harness/muse.md
  • .agents/skills/harness-adapters/references/harness/omp.md
  • .agents/skills/harness-adapters/references/harness/opencode.md
  • .agents/skills/harness-adapters/references/harness/pi.md
  • .agents/skills/harness-adapters/references/harness/rovo.md
  • .agents/skills/process-event-sources/SKILL.md
  • .agents/skills/project-management/SKILL.md
  • .agents/skills/quiet/SKILL.md
  • .agents/skills/quota-array-dispatch/SKILL.md
  • .agents/skills/secondmate-provisioning/SKILL.md
  • .agents/skills/stow/SKILL.md
  • .agents/skills/stuck-crewmate-recovery/SKILL.md
  • .agents/skills/updatefirstmate/SKILL.md
  • .claude/mods/firstmate-calm/.claude-plugin/plugin.json
  • .claude/mods/firstmate-calm/hooks/hooks.json
  • .claude/mods/firstmate-calm/hooks/register.ts
  • .claude/mods/firstmate-calm/lib/fm-calm-presentation.ts
  • .claude/mods/firstmate-calm/lib/fm-calm-ship-raster.ts
  • .claude/mods/firstmate-calm/lib/fm-calm-working-ship-sprite.ts
  • .claude/mods/firstmate-calm/lib/fm-operational-input.ts
  • .claude/mods/firstmate-calm/tests/calm.test.ts
  • .claude/mods/firstmate-calm/tests/support.ts
  • .claude/mods/firstmate-calm/tests/working-ship.test.ts
  • .claude/settings.json
  • .codex/hooks.json
  • .cursor/hooks.json
  • .gitattributes
  • .github/workflows/ci.yml
  • .github/workflows/no-mistakes-required.yml
  • .github/workflows/windows-herdr-spike.yml
  • .gitignore
  • .greptile/rules.md
  • .no-mistakes.yaml
  • .omp/extensions/fm-primary-omp-watch.ts
  • .omp/extensions/fm-primary-turnend-guard.ts
  • .omp/fm-worker-overlay.yml
  • .opencode/plugins/fm-primary-watch-arm.js
  • .opencode/plugins/lib/fm-operational-input.js
  • .pi/extensions/fm-branch-supervision.ts
  • .pi/extensions/fm-calm.ts
  • .pi/extensions/fm-primary-pi-watch.ts
  • .pi/extensions/fm-primary-turnend-guard.ts
  • .pi/extensions/lib/fm-async-exec.ts
  • .pi/extensions/lib/fm-branch-dispatch.ts
  • .pi/extensions/lib/fm-branch-model-picker.ts
  • .pi/extensions/lib/fm-calm-assistant-layout.ts
  • .pi/extensions/lib/fm-calm-visibility.ts
  • .pi/extensions/lib/fm-calm-working-ship-sprite.ts
  • .pi/extensions/lib/fm-calm-working-ship.ts
  • .pi/extensions/lib/fm-native-contract.ts
  • .pi/extensions/lib/fm-operational-input.ts
  • .pi/extensions/lib/fm-sessionstart-supervisor.mjs
  • AGENTS.md
  • CLAUDE.md
  • CLAUDE.md
  • CONTRIBUTING.md
  • GROK_BOT.md
  • README.md
  • VISION.md
  • bin/backends/cmux.sh
  • bin/backends/herdr.sh
  • bin/backends/orca.sh
  • bin/backends/tmux.sh
  • bin/backends/zellij.sh
  • bin/fm-afk-contract.sh
  • bin/fm-afk-launch.sh
  • bin/fm-afk-return.sh
  • bin/fm-afk-start.sh
  • bin/fm-agent-process-lib.sh
  • bin/fm-agy-trust.sh
  • bin/fm-arm-pretool-check.sh
  • bin/fm-backend.sh
  • bin/fm-backlog-handoff.sh
  • bin/fm-backlog-receive.sh
  • bin/fm-backlog-transition-lib.sh
  • bin/fm-bearings-board.sh
  • bin/fm-bearings-snapshot.sh
  • bin/fm-bootstrap.sh
  • bin/fm-branch-outcome.sh
  • bin/fm-branch-prompt.sh
  • bin/fm-brief.sh
  • bin/fm-busy-event.sh
  • bin/fm-busy-lib.sh
  • bin/fm-captain-hold.sh
  • bin/fm-cd-pretool-check.sh
  • bin/fm-check-register.sh
  • bin/fm-check-unregister.sh
  • bin/fm-classify-lib.sh
  • bin/fm-claude-stop-autoarm.sh
  • bin/fm-claude-trust.sh
  • bin/fm-composer-lib.sh
  • bin/fm-config-inherit-lib.sh
  • bin/fm-contributions.jq
  • bin/fm-contributions.sh
  • bin/fm-control-lib.sh
  • bin/fm-control.sh
  • bin/fm-crew-state.sh
  • bin/fm-cursor-lib.sh
  • bin/fm-decision-hold.sh
  • bin/fm-dod-lib.sh
  • bin/fm-ensure-agents-md.sh
  • bin/fm-extension-launch-barrier.mjs
  • bin/fm-extension.mjs
  • bin/fm-extension.sh
  • bin/fm-ff-lib.sh
  • bin/fm-fleet-snapshot.sh
  • bin/fm-fleet-sync.sh
  • bin/fm-gemini-lib.sh
  • bin/fm-guard.sh
  • bin/fm-harness.sh
  • bin/fm-herdr-lab-viewer.py
  • bin/fm-herdr-lab.sh
  • bin/fm-herdr-session-cleanup.sh
  • bin/fm-home-seed.sh
  • bin/fm-home-summary-refresh.sh
  • bin/fm-hook-host-lib.sh
  • bin/fm-inactive-reconcile.sh
  • bin/fm-inbox.sh
  • bin/fm-install-actionlint.sh
  • bin/fm-install-shellcheck.sh
  • bin/fm-issue-closure.sh
  • bin/fm-landed-lib.sh
  • bin/fm-lease-lib.sh
  • bin/fm-lease.sh
  • bin/fm-line-cap-lib.sh
  • bin/fm-lint-workflows.sh
  • bin/fm-lint.sh
  • bin/fm-lock-lib.sh
  • bin/fm-lock.sh
  • bin/fm-mail-check.sh
  • bin/fm-mail.py
  • bin/fm-mail.sh
  • bin/fm-merge-authority-lib.sh
  • bin/fm-merge-local.sh
  • bin/fm-merge-outcome-lib.sh
  • bin/fm-nm-run-lib.sh
  • bin/fm-on.sh
  • bin/fm-operational-input.sh
  • bin/fm-parent-channel-lib.sh
  • bin/fm-peek.sh
  • bin/fm-pending-reply-lib.sh
  • bin/fm-pr-check-migrate.sh
  • bin/fm-pr-check.sh
  • bin/fm-pr-lib.sh
  • bin/fm-pr-merge.sh
  • bin/fm-pr-reviewers.sh
  • bin/fm-pr-state.sh
  • bin/fm-procevent-extension-capture.pl
  • bin/fm-procevent-lavish.sh
  • bin/fm-procevent-lib.sh
  • bin/fm-procevent-quota.sh
  • bin/fm-procevent-remote-reply.sh
  • bin/fm-procevent-when.sh
  • bin/fm-procevent.sh
  • bin/fm-project-mode.sh
  • bin/fm-project-origin-lib.sh
  • bin/fm-promote.sh
  • bin/fm-public-followup-collect.sh
  • bin/fm-public-followup-emit.sh
  • bin/fm-public-followup-lib.sh
  • bin/fm-public-followup.sh
  • bin/fm-push-transition-lib.sh
  • bin/fm-quota-axi-lib.sh
  • bin/fm-quota-choose.sh
  • bin/fm-remote-delta-read.sh
  • bin/fm-remote-doctor.sh
  • bin/fm-remote-entrypoint.sh
  • bin/fm-remote-file.sh
  • bin/fm-remote-herdr-guard.sh
  • bin/fm-remote-herdr-owner-lib.sh
  • bin/fm-remote-home-provision.sh
  • bin/fm-remote-home-seed.sh
  • bin/fm-remote-inherit-push.sh
  • bin/fm-remote-inherit.sh
  • bin/fm-remote-job-lib.sh
  • bin/fm-remote-job-reap-orphans.sh
  • bin/fm-remote-job-worker.sh
  • bin/fm-remote-secondmate-control.sh
  • bin/fm-secondmate-parent-lib.sh
  • bin/fm-secondmate-reconcile.sh
  • bin/fm-secondmate-report.sh
  • bin/fm-secondmate-restart-lib.sh
  • bin/fm-secondmate-restart.sh
  • bin/fm-send.sh
  • bin/fm-session-lock-lib.sh
  • bin/fm-session-start.sh
  • bin/fm-sessionstart-cursor.sh
  • bin/fm-sessionstart-nudge.sh
  • bin/fm-sessionstart-run.sh
  • bin/fm-spawn.sh
  • bin/fm-startup-memory-budget-lib.sh
  • bin/fm-startup-network.sh
  • bin/fm-stow-cascade.sh
  • bin/fm-supervise-daemon.sh
  • bin/fm-supervision-instructions.sh
  • bin/fm-supervision-lib.sh
  • bin/fm-task-inbox-lib.sh
  • bin/fm-tasks-axi-lib.sh
  • bin/fm-tasks-axi.sh
  • bin/fm-teardown.sh
  • bin/fm-test-isolation-proof.sh
  • bin/fm-test-run.sh
  • bin/fm-timeout-lib.sh
  • bin/fm-timing-lib.sh
  • bin/fm-tmux-lib.sh
  • bin/fm-tool-update-check.sh
  • bin/fm-turnend-guard-cursor.sh
  • bin/fm-turnend-guard.sh
  • bin/fm-update.sh
  • bin/fm-vendor-auth-probe.sh
  • bin/fm-voice-client.py
  • bin/fm-voice-relay.py
  • bin/fm-wake-drain.sh
  • bin/fm-wake-grant.sh
  • bin/fm-wake-lib.sh
  • bin/fm-watch-arm.sh
  • bin/fm-watch-checkpoint.sh
  • bin/fm-watch.sh
  • bin/fm-x-followup.sh
  • bin/fm-x-lib.sh
  • bin/fm-x-link.sh
  • bin/fm-x-poll.sh
  • bin/fm_voice_frame.py
  • bin/fm_voice_records.py
  • docs/agent-control.md
  • docs/architecture.md
  • docs/arm-pretool-check.md
  • docs/calm-mode-feasibility.md
  • docs/calm.md
  • docs/captain-hold-lifecycle.md
  • docs/cd-guard.md
  • docs/cmux-backend.md
  • docs/configuration.md
  • docs/decision-hold-lifecycle.md
  • docs/documentation-audiences.json
  • docs/examples/process-event-extension/file-signal.mjs
  • docs/examples/process-event-extension/firstmate-extension.json
  • docs/examples/watched-tools.json
  • docs/extension-bindings.md
  • docs/fm-test-isolation-proof.json
  • docs/fm-test-isolation-proof.md
  • docs/fm-test-portable-shards.md
  • docs/gitlab-merge-watch.md
  • docs/herdr-backend.md
  • docs/orca-backend.md
  • docs/pi-supervision-branch.md
  • docs/remote-secondmates.md
  • docs/scripts.md
  • docs/secondmate-parent-channel.md
  • docs/sessionstart-nudge.md
  • docs/subagent-guard.md
  • docs/supervision-protocols/claude.md
  • docs/supervision-protocols/codex.md
  • docs/supervision-protocols/cursor.md
  • docs/supervision-protocols/grok.md
  • docs/supervision-protocols/omp.md
  • docs/supervision-protocols/opencode.md
  • docs/supervision-protocols/pi.md
  • docs/supervision-protocols/unknown.md
  • docs/tmux-backend.md
  • docs/trace-context.md
  • docs/turnend-guard.md
  • docs/verification/agy.md
  • docs/verification/dispatch-auth.md
  • docs/verification/lint-option-a.md
  • docs/verification/muse.md
  • docs/verification/process-event-sources.md
  • docs/verification/public-followup.md
  • docs/verification/rovo.md
  • docs/verification/runtime-backends.md
  • docs/verification/secondmate-parent-channel.md
  • docs/verification/stow-memory.md
  • docs/verification/supervision.md
  • docs/verification/trace-context.md
  • docs/voice-relay.md
  • docs/watcher-continuity.md
  • docs/zellij-backend.md
  • skills/stow/SKILL.md
  • tests/assets/board-render-harness.mjs
  • tests/fixtures.sh
  • tests/fm-afk-contract.test.sh
  • tests/fm-afk-inject-e2e.test.sh
  • tests/fm-afk-inject-herdr-e2e.test.sh
  • tests/fm-afk-launch.test.sh
  • tests/fm-afk-pi-herdr-return-e2e.test.sh
  • tests/fm-afk-return.test.sh
  • tests/fm-agy-harness.test.sh
  • tests/fm-agy-signals-live-e2e.test.sh
  • tests/fm-arm-pretool-check.test.sh
  • tests/fm-ask-user-authority.test.sh
  • tests/fm-backend-autodetect-smoke.test.sh
  • tests/fm-backend-cmux.test.sh
  • tests/fm-backend-herdr-agent-exit-shell-e2e.test.sh
  • tests/fm-backend-herdr-focus-flash-e2e.test.sh
  • tests/fm-backend-herdr-launcher-workspace-e2e.test.sh
  • tests/fm-backend-herdr-presentation-e2e.test.sh
  • tests/fm-backend-herdr-smoke.test.sh
  • tests/fm-backend-herdr-stale-active-tab-e2e.test.sh
  • tests/fm-backend-herdr-workspace-per-home-e2e.test.sh
  • tests/fm-backend-herdr.test.sh
  • tests/fm-backend-orca.test.sh
  • tests/fm-backend-zellij.test.sh
  • tests/fm-backend.test.sh
  • tests/fm-backlog-atomicity.test.sh
  • tests/fm-backlog-handoff.test.sh
  • tests/fm-backlog-read-bound.test.sh
  • tests/fm-bearings-board-lavish-live-e2e.test.sh
  • tests/fm-bearings-board-render.test.sh
  • tests/fm-bearings-board.test.sh
  • tests/fm-bearings-snapshot.test.sh
  • tests/fm-bootstrap-network-parallel.test.sh
  • tests/fm-bootstrap.test.sh
  • tests/fm-branch-supervision.test.sh
  • tests/fm-brief.test.sh
  • tests/fm-busy-adapter-wiring.test.sh
  • tests/fm-busy-state.test.sh
  • tests/fm-calm-claude-mod-live-e2e.test.sh
  • tests/fm-calm-claude-mod-plugin.test.sh
  • tests/fm-calm-claude-mod.test.sh
  • tests/fm-calm-pi-extension.test.sh
  • tests/fm-captain-hold-lifecycle.test.sh
  • tests/fm-cd-pretool-check.test.sh
  • tests/fm-check-unregister.test.sh
  • tests/fm-ci-workflow.test.sh
  • tests/fm-classify-corr-token.test.sh
  • tests/fm-classify-decision-key.test.sh
  • tests/fm-claude-stop-autoarm-live-e2e.test.sh
  • tests/fm-claude-stop-autoarm.test.sh
  • tests/fm-claude-trust.test.sh
  • tests/fm-cmux-claude-composer-live-e2e.test.sh
  • tests/fm-codex-continuity-live-e2e.test.sh
  • tests/fm-composer-codex-idle-live-e2e.test.sh
  • tests/fm-composer-ghost.test.sh
  • tests/fm-composer-lib.test.sh
  • tests/fm-composer-matrix-live-e2e.test.sh
  • tests/fm-contributions.test.sh
  • tests/fm-control-herdr-smoke.test.sh
  • tests/fm-control-relaunch.test.sh
  • tests/fm-control.test.sh
  • tests/fm-crew-state.test.sh
  • tests/fm-cursor-harness.test.sh
  • tests/fm-cursor-primary-live-e2e.test.sh
  • tests/fm-cursor-primary.test.sh
  • tests/fm-daemon.test.sh
  • tests/fm-decision-hold-lifecycle.test.sh
  • tests/fm-ensure-agents-md.test.sh
  • tests/fm-extension-binding.test.sh
  • tests/fm-fleet-snapshot-view.test.sh
  • tests/fm-fleet-sync.test.sh
  • tests/fm-gate-refuse.test.sh
  • tests/fm-gemini-harness.test.sh
  • tests/fm-gitignore-config.test.sh
  • tests/fm-gotmp.test.sh
  • tests/fm-grok-continuity-live-e2e.test.sh
  • tests/fm-grok-harness.test.sh
  • tests/fm-grok-stop-live-e2e.test.sh
  • tests/fm-guard-stale-banner.test.sh
  • tests/fm-harness-adapter-instructions-live-e2e.test.sh
  • tests/fm-harness-adapter-references.test.sh
  • tests/fm-harness-liveness-drift-live-e2e.test.sh
  • tests/fm-harness-precedence.test.sh
  • tests/fm-herdr-attached-viewer-live-e2e.test.sh
  • tests/fm-herdr-lab.test.sh
  • tests/fm-herdr-pi-stale-registration-live-e2e.test.sh
  • tests/fm-herdr-submit-confirm-live-e2e.test.sh
  • tests/fm-herdr-version-floor-live-e2e.test.sh
  • tests/fm-home-summary-refresh.test.sh
  • tests/fm-inactive-reconcile.test.sh
  • tests/fm-issue-closure.test.sh
  • tests/fm-kimi-harness.test.sh
  • tests/fm-lint-workflows.test.sh
  • tests/fm-lint.test.sh
  • tests/fm-live-gate.test.sh
  • tests/fm-mail-check.test.sh
  • tests/fm-mail.test.sh
  • tests/fm-muse-harness.test.sh
  • tests/fm-muse-signals-live-e2e.test.sh
  • tests/fm-nm-test-contract.test.sh
  • tests/fm-no-mistakes-required.test.sh
  • tests/fm-omp-harness.test.sh
  • tests/fm-omp-primary-live-e2e.test.sh
  • tests/fm-on.test.sh
  • tests/fm-opencode-primary-live-e2e.test.sh
  • tests/fm-operational-input.test.sh
  • tests/fm-peek-remote.test.sh
  • tests/fm-pending-reply.test.sh
  • tests/fm-pi-branch-extension.test.sh
  • tests/fm-pi-branch-live-e2e.test.sh
  • tests/fm-pi-branch-responsiveness-live-e2e.test.sh
  • tests/fm-pi-codex-native.test.sh
  • tests/fm-pi-primary-live-e2e.test.sh
  • tests/fm-pi-primary-types.test.sh
  • tests/fm-pi-watch-extension.test.sh
  • tests/fm-pi-windows-shell-invocation.test.sh
  • tests/fm-pr-check-security.test.sh
  • tests/fm-pr-merge.test.sh
  • tests/fm-pr-reviewers.test.sh
  • tests/fm-pr-state-live-e2e.test.sh
  • tests/fm-pr-state.test.sh
  • tests/fm-procevent-quota.test.sh
  • tests/fm-procevent-when.test.sh
  • tests/fm-procevent.test.sh
  • tests/fm-project-origin.test.sh
  • tests/fm-public-followup.test.sh
  • tests/fm-quota-array-dispatch-live-e2e.test.sh
  • tests/fm-quota-choose.test.sh
  • tests/fm-remote-backlog-handoff.test.sh
  • tests/fm-remote-doctor.test.sh
  • tests/fm-remote-entrypoint.test.sh
  • tests/fm-remote-herdr-guard.test.sh
  • tests/fm-remote-job-orphan-reap.test.sh
  • tests/fm-remote-job.test.sh
  • tests/fm-remote-reply.test.sh
  • tests/fm-remote-secondmate-lifecycle-e2e.test.sh
  • tests/fm-remote-secondmate-parent-binding.test.sh
  • tests/fm-remote-secondmate-trace-context.test.sh
  • tests/fm-remote-transport-lanes.test.sh
  • tests/fm-rovo-harness.test.sh
  • tests/fm-rovo-signals-live-e2e.test.sh
  • tests/fm-secondmate-harness.test.sh
  • tests/fm-secondmate-lifecycle-e2e.test.sh
  • tests/fm-secondmate-liveness.test.sh
  • tests/fm-secondmate-reconcile.test.sh
  • tests/fm-secondmate-restart.test.sh
  • tests/fm-secondmate-safety.test.sh
  • tests/fm-secondmate-sync.test.sh
  • tests/fm-send-agy-confirm.test.sh
  • tests/fm-send-inbox-doorbell-live-e2e.test.sh
  • tests/fm-send-inbox.test.sh
  • tests/fm-send-popup-settle.test.sh
  • tests/fm-send-remote-delivery.test.sh
  • tests/fm-send-resolve-key.test.sh
  • tests/fm-send-secondmate-marker-herdr-e2e.test.sh
  • tests/fm-send-secondmate-marker.test.sh
  • tests/fm-send-settle.test.sh
  • tests/fm-send-strict.test.sh
  • tests/fm-session-lock-ancestry.test.sh
  • tests/fm-session-start.test.sh
  • tests/fm-sessionstart-hook-live-e2e.test.sh
  • tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh
  • tests/fm-sessionstart-nudge.test.sh
  • tests/fm-shared-captain-inheritance.test.sh
  • tests/fm-spawn-batch.test.sh
  • tests/fm-spawn-dispatch-profile.test.sh
  • tests/fm-spawn-pool-base-freshen.test.sh
  • tests/fm-spawn-worktree-settle.test.sh
  • tests/fm-startup-memory-budget.test.sh
  • tests/fm-startup-network.test.sh
  • tests/fm-stat-shadowing.test.sh
  • tests/fm-stow-cascade.test.sh
  • tests/fm-supervision-events.test.sh
  • tests/fm-supervision-instructions.test.sh
  • tests/fm-tangle-guard.test.sh
  • tests/fm-task-bootstrap.test.sh
  • tests/fm-task-delivery.test.sh
  • tests/fm-task-inbox.test.sh
  • tests/fm-tasks-axi.test.sh
  • tests/fm-teardown-endpoint-safety.test.sh
  • tests/fm-teardown.test.sh
  • tests/fm-test-fixture-cleanup.test.sh
  • tests/fm-test-fixtures.test.sh
  • tests/fm-test-isolation-proof.test.sh
  • tests/fm-test-run.test.sh
  • tests/fm-tmux-agent-liveness.test.sh
  • tests/fm-tmux-submit-busy.test.sh
  • tests/fm-tool-update-check.test.sh
  • tests/fm-trace-context-lib.test.sh
  • tests/fm-trace-context-spawn.test.sh
  • tests/fm-turnend-guard.test.sh
  • tests/fm-update.test.sh
  • tests/fm-voice-relay.test.sh
  • tests/fm-wake-daemon-lifecycle-e2e.test.sh
  • tests/fm-wake-drain-open-decisions-cursor.test.sh
  • tests/fm-wake-drain-open-decisions.test.sh
  • tests/fm-wake-drain-outcome-backstop.test.sh
  • tests/fm-wake-drain-unread-status.test.sh
  • tests/fm-wake-queue.test.sh
  • tests/fm-watch-arm.test.sh
  • tests/fm-watch-checkpoint.test.sh
  • tests/fm-watch-recovery-loop.test.sh
  • tests/fm-watch-triage.test.sh
  • tests/fm-watcher-lock.test.sh
  • tests/fm-x-mode.test.sh
  • tests/git-config-helpers.sh
  • tests/herdr-client-pair-fixture.sh
  • tests/herdr-test-safety.sh
  • tests/lib.sh
  • tests/remote-herdr-fixture.sh
  • tests/secondmate-helpers.sh
  • tests/wake-helpers.sh

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

One or more issues must be addressed before approval.

Pull request overview

Captain, this PR merges upstream main while retaining fork-specific delivery, teardown, bootstrap, Bearings, and quota-dispatch behavior.

Changes:

  • Restores true upstream merge ancestry.
  • Ports fork-only delivery, issue-closure, landed-work, and task-bootstrap features.
  • Preserves quota-aware dispatch and account-prefixed raw launches.
File summaries
File Description
tests/secondmate-helpers.sh Updated as part of this pull request.
tests/remote-herdr-fixture.sh Updated as part of this pull request.
tests/herdr-test-safety.sh Updated as part of this pull request.
tests/git-config-helpers.sh Updated as part of this pull request.
tests/fm-watch-checkpoint.test.sh Updated as part of this pull request.
tests/fm-trace-context-lib.test.sh Updated as part of this pull request.
tests/fm-supervision-events.test.sh Updated as part of this pull request.
tests/fm-spawn-batch.test.sh Updated as part of this pull request.
tests/fm-send-settle.test.sh Updated as part of this pull request.
tests/fm-send-secondmate-marker-herdr-e2e.test.sh Updated as part of this pull request.
tests/fm-secondmate-liveness.test.sh Updated as part of this pull request.
tests/fm-remote-secondmate-trace-context.test.sh Updated as part of this pull request.
tests/fm-remote-entrypoint.test.sh Updated as part of this pull request.
tests/fm-pr-state-live-e2e.test.sh Updated as part of this pull request.
tests/fm-operational-input.test.sh Updated as part of this pull request.
tests/fm-opencode-primary-live-e2e.test.sh Updated as part of this pull request.
tests/fm-nm-test-contract.test.sh Updated as part of this pull request.
tests/fm-issue-closure.test.sh Updated as part of this pull request.
tests/fm-harness-adapter-references.test.sh Updated as part of this pull request.
tests/fm-grok-stop-live-e2e.test.sh Updated as part of this pull request.
tests/fm-grok-harness.test.sh Updated as part of this pull request.
tests/fm-grok-continuity-live-e2e.test.sh Updated as part of this pull request.
tests/fm-gitignore-config.test.sh Updated as part of this pull request.
tests/fm-codex-continuity-live-e2e.test.sh Updated as part of this pull request.
tests/fm-claude-stop-autoarm-live-e2e.test.sh Updated as part of this pull request.
tests/fm-cd-pretool-check.test.sh Updated as part of this pull request.
tests/fm-backend-herdr-workspace-per-home-e2e.test.sh Updated as part of this pull request.
tests/fm-backend-herdr-smoke.test.sh Updated as part of this pull request.
tests/fm-backend-autodetect-smoke.test.sh Updated as part of this pull request.
tests/fm-ask-user-authority.test.sh Updated as part of this pull request.
tests/fm-arm-pretool-check.test.sh Updated as part of this pull request.
tests/fm-afk-inject-herdr-e2e.test.sh Updated as part of this pull request.
tests/fm-afk-inject-e2e.test.sh Updated as part of this pull request.
docs/zellij-backend.md Updated as part of this pull request.
docs/verification/trace-context.md Updated as part of this pull request.
docs/trace-context.md Updated as part of this pull request.
docs/supervision-protocols/unknown.md Updated as part of this pull request.
docs/supervision-protocols/opencode.md Updated as part of this pull request.
docs/supervision-protocols/grok.md Updated as part of this pull request.
docs/supervision-protocols/cursor.md Updated as part of this pull request.
docs/supervision-protocols/codex.md Updated as part of this pull request.
docs/supervision-protocols/claude.md Updated as part of this pull request.
docs/subagent-guard.md Updated as part of this pull request.
docs/orca-backend.md Updated as part of this pull request.
docs/examples/watched-tools.json Updated as part of this pull request.
docs/examples/process-event-extension/firstmate-extension.json Updated as part of this pull request.
docs/cmux-backend.md Updated as part of this pull request.
docs/arm-pretool-check.md Updated as part of this pull request.
bin/fm-x-poll.sh Updated as part of this pull request.
bin/fm-watch-checkpoint.sh Updated as part of this pull request.
bin/fm-vendor-auth-probe.sh Updated as part of this pull request.
bin/fm-startup-memory-budget-lib.sh Updated as part of this pull request.
bin/fm-sessionstart-nudge.sh Updated as part of this pull request.
bin/fm-sessionstart-cursor.sh Updated as part of this pull request.
bin/fm-secondmate-parent-lib.sh Updated as part of this pull request.
bin/fm-remote-inherit.sh Updated as part of this pull request.
bin/fm-remote-inherit-push.sh Updated as part of this pull request.
bin/fm-remote-file.sh Updated as part of this pull request.
bin/fm-remote-delta-read.sh Updated as part of this pull request.
bin/fm-project-mode.sh Updated as part of this pull request.
bin/fm-peek.sh Updated as part of this pull request.
bin/fm-operational-input.sh Updated as part of this pull request.
bin/fm-lock.sh Updated as part of this pull request.
bin/fm-lock-lib.sh Updated as part of this pull request.
bin/fm-line-cap-lib.sh Updated as part of this pull request.
bin/fm-issue-closure.sh Updated as part of this pull request.
bin/fm-hook-host-lib.sh Updated as part of this pull request.
bin/fm-herdr-session-cleanup.sh Updated as part of this pull request.
bin/fm-extension.sh Updated as part of this pull request.
bin/fm-check-unregister.sh Updated as part of this pull request.
bin/fm-check-register.sh Updated as part of this pull request.
bin/fm-backlog-receive.sh Updated as part of this pull request.
bin/fm-afk-start.sh Updated as part of this pull request.
.pi/extensions/lib/fm-sessionstart-supervisor.mjs Updated as part of this pull request.
.pi/extensions/lib/fm-native-contract.ts Updated as part of this pull request.
.pi/extensions/lib/fm-calm-visibility.ts Updated as part of this pull request.
.opencode/plugins/lib/fm-operational-input.js Updated as part of this pull request.
.omp/fm-worker-overlay.yml Updated as part of this pull request.
.no-mistakes.yaml Updated as part of this pull request.
.greptile/rules.md Updated as part of this pull request.
.gitignore Updated as part of this pull request.
.github/workflows/no-mistakes-required.yml Updated as part of this pull request.
.gitattributes Updated as part of this pull request.
.cursor/hooks.json Updated as part of this pull request.
.codex/hooks.json Updated as part of this pull request.
.claude/settings.json Updated as part of this pull request.
.claude/mods/firstmate-calm/hooks/hooks.json Updated as part of this pull request.
.claude/mods/firstmate-calm/.claude-plugin/plugin.json Updated as part of this pull request.
.agents/skills/project-management/SKILL.md Updated as part of this pull request.
.agents/skills/harness-adapters/references/common/primary-hooks.md Updated as part of this pull request.
.agents/skills/harness-adapters/references/common/model-and-effort.md Updated as part of this pull request.
.agents/skills/harness-adapters/references/common/dispatch.md Updated as part of this pull request.
.agents/skills/harness-adapters/references/common/control-and-recovery.md Updated as part of this pull request.
.agents/skills/firstmate-orca/SKILL.md Updated as part of this pull request.
.agents/skills/ahoy/SKILL.md Updated as part of this pull request.
Review details
  • Files reviewed: 80/515 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.