feat: harden secondmate recovery and coordination - #16
Merged
Merged
Conversation
added 7 commits
July 28, 2026 21:49
Keep routed secondmate answers observable without scraping chat, and make live inherited-config updates generation-bound so older values cannot overtake newer ones.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Port the approved upstream secondmate reliability batch onto the current Firstmate fork in two sub-batches: first, confident missing endpoint detection and startup recovery; second, correlated secondmate replies plus generation-bound inherited-config rereads. Preserve fork-specific safety, canonical FIRSTMATE_OP operational inputs, direct-report ownership, all five harness/backend semantics, and the full inheritance allowlist (crew-dispatch.json, crew-harness, backlog-backend, forge-hosts, signing-agent, and captain-shared.md), while continuing to exclude secondmate-harness, local captain.md, and learnings.md. Pending replies must remain parent-owned, correlation-bound, observable without reading secondmate chat, and resilient to malformed, symlinked, hard-linked, wrong-device, wrong-home, or wrong-destination private artifacts. Config rereads must use exact validated destination bytes, generation-specific pointers, durable retry/quarantine handling, and per-home serialization so older values cannot overtake newer ones. Add extensive hermetic coverage, preserve direct captain input behavior, validate every script/test/backend, and ship the committed branch for review without merging it.
What Changed
Risk Assessment
✅ Low: Captain, the latest change makes stale generations logically ineligible before best-effort cleanup, preserves merged immutable values across partial updates, and leaves a crash-recoverable successor generation without introducing a substantiated remaining defect.
Testing
The previously successful full baseline plus focused end-to-end scripts exercised confident endpoint recovery across backend semantics, parent-owned correlation and escalation without chat scraping, hostile private-artifact rejection, exact-byte generation-bound config rereads with retry/quarantine/serialization, inheritance and direct-input behavior, and session-start recovery; all passed, reviewer-visible CLI transcripts were captured, and the worktree remained unchanged.
Evidence: Correlated pending-reply end-to-end transcript
Evidence: Secondmate endpoint detection and recovery transcript
Evidence: Inherited-config propagation and generation safety transcript
Evidence: Session-start recovery transcript
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 3 issues found → auto-fixed (4) ✅
bin/fm-config-inherit-lib.sh:1081- The required invariant “older values cannot overtake newer ones” remains reachable. If generation G1 is pending, a later push creates G2, then this code sorts and sends G1 before G2; if G1 succeeds but G2 fails, the live secondmate applies G1 after the destination already contains G2. Coalesce/supersede older pending generations before delivery, or otherwise enforce latest-generation application at this shared boundary.bin/fm-config-inherit-lib.sh:1005- The required “exact validated destination bytes” are not durably retained when initial instruction creation fails before producing a temporary file. The code saves only the change report, then a later retry rebuilds that generation by rereading the mutable destination, which may already contain a newer value. Capture immutable destination bytes during the originating locked propagation, and never reconstruct an old generation from current destination state.bin/fm-pending-reply-lib.sh:601- A pending reply resolves on any parent status line containing the correlation token, including aworkingprogress line. The secondmate brief permits material working reports, so a correlated progress update can terminally resolve the expectation before the actual answer arrives, defeating the required observable pending-reply guarantee. Restrict resolution to answer-bearing terminal/status-pointer forms or define an explicit acknowledgement grammar.🔧 Fix: Captain, enforce monotonic rereads and final replies
2 errors still open:
bin/fm-config-inherit-lib.sh:991- The required durable retry/quarantine behavior is blocked by any.reportartifact left by the previous implementation: this branch returns before capturing or sending the current immutable generation, and every later push repeats the same failure. Quarantine unsupported mutable reports at this shared boundary, then continue from a freshly captured immutable destination snapshot.bin/fm-config-inherit-lib.sh:1020- When coalescing backlog, the synthetic report marks every allowlisted item as pushed regardless of the current propagation report. A skipped or failed destination—such as a symlink or non-gitignored path—is therefore rendered asABSENTand sent as authoritative, even though propagation deliberately left it unchanged. Build the latest snapshot only from destination states validated as copied or authoritatively absent; do not convert unvalidated items into removals.🔧 Fix: Captain, quarantine legacy retries and preserve validation
1 error still open:
bin/fm-config-inherit-lib.sh:1085- Coalescing a partial propagation drops still-needed authoritative bytes from the prior immutable generation. Example: G1 contains a successfully copiedforge-hostschange but its send is pending; G2 validates onlycrew-harnessbecauseforge-hostsis now skipped or errors. The new snapshot contains onlycrew-harness, then deletes G1, so the secondmate never receives the validated G1forge-hostsvalue. At this boundary, merge current validated items with the latest immutable values for currently unvalidated items before retiring the backlog.🔧 Fix: Captain, preserve validated items across partial coalescing
1 error still open:
bin/fm-config-inherit-lib.sh:1158- The required monotonic guarantee remains reachable when retiring merged backlog artifacts fails. This loop ignoresrmfailures, publishes the merged generation, and forgets the surviving old paths. On a later partial push, a surviving old generation can be merged without the already-delivered newer value and reapply older bytes. Require complete retirement/quarantine before publication, or keep the newest merged snapshot in the durable backlog until stale artifacts are conclusively neutralized.🔧 Fix: Captain, prevent superseded config generations from reactivating
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; exit "$rc"Configured baseline:command -v tmux >/dev/null || { echo "tmux is required for e2e tests" >&2; exit 1; }; tmux -V; rc=0; for t in tests/*.test.sh; do echo "== $t =="; bash "$t" || rc=1; done; exit "$rc"bash tests/fm-pending-reply.test.shbash tests/fm-secondmate-liveness.test.shbash tests/fm-secondmate-harness.test.shbash tests/fm-session-start.test.shgit status --shortto verify testing introduced no worktree artifacts✅ **Document** - passed
✅ No issues found.
🔧 **Lint** - 1 issue found → auto-fixed ✅
🔧 Fix: Fix ShellCheck temp handoff and unreachable assertions
✅ Re-checked - no issues remain.
✅ **Push** - passed
✅ No issues found.