Skip to content

fix: harden review resolution and merged poll retirement - #13

Merged
eyevanovich merged 5 commits into
mainfrom
fm/firstmate-upstream-review-lifecycle
Jul 28, 2026
Merged

eyevanovich merged 5 commits into
mainfrom
fm/firstmate-upstream-review-lifecycle

Conversation

@eyevanovich

Copy link
Copy Markdown
Owner

Intent

Port the two captain-approved upstream review-lifecycle correctness fixes onto the Firstmate fork at PR #12's landed head. Review diffs must prefer freshly fetched GitHub or GitLab review heads stored under private refs, use reachable recorded pr_head only as an offline fallback, refuse forge-origin identity mismatches, and warn before final local-branch fallback. Merged-review polling must retire exactly once only after its notification is durable, use provider-neutral identity-bound crash-recovery receipts, preserve GitLab's hash-registered custom-check route including trusted self-hosted forges, retain task and persistent-secondmate lifecycle state, preserve X-mode and non-executing migration security, and safely recover through watcher, migration, rearm, replacement, tamper, and teardown races. Preserve all fork-specific GitLab, direct signing, no-mistakes observer, local-only, secondmate, away/X-mode, and five-backend behavior; add comprehensive counterfactual tests; do not merge.

What Changed

  • Prefer freshly fetched GitHub or GitLab review heads in private refs, with validated offline and warned local-branch fallbacks.
  • Retire merged review polls through provider-neutral, identity-bound recovery receipts that preserve replacement polls and lifecycle state across races and restarts.
  • Extend migration, teardown, security, and counterfactual coverage for GitHub and GitLab review lifecycles; the full test suite and targeted lifecycle tests pass.

Risk Assessment

🚨 High: Captain, the change should not merge until the crash window that can duplicate a merged-review notification is explicitly resolved or approved.

Testing

The full baseline was already green; focused end-to-end CLI tests then exercised review-head selection, identity and fallback handling, exactly-once durable retirement and recovery races, migration/X-mode security, and GitLab lifecycle behavior, all successfully. A reviewer-visible CLI transcript was captured; no screenshot was applicable because this change has no rendered UI surface.

Evidence: Review lifecycle end-to-end transcript
$ bash tests/fm-review-diff.test.sh
ok - fresh GitHub review heads defeat stale reachable recorded heads
ok - fresh GitLab review heads defeat stale reachable recorded heads
ok - trusted self-hosted GitLab identity can fetch the current review head
ok - remote unavailability falls back to a reachable recorded review head
ok - invalid and multiline recorded review heads are refused
ok - tasks without a recorded review retain the local branch diff
ok - the local branch is a warned final fallback when neither review head is usable
ok - remote review identity mismatches refuse rather than using recorded metadata
$ bash tests/fm-pr-check-security.test.sh
ok - raw-byte parser accepts canonical URLs and rejects the complete adversarial matrix
ok - merged GitHub polls notify once and retirement preserves lifecycle metadata
ok - nonterminal, failed, tampered, and replacement poll states retain the right authority
ok - retirement recovers across notification, migration, and teardown crash boundaries
ok - PR and teardown entrypoints reject invalid arguments before every side effect
ok - valid direct and merge flows record exact metadata and reject multiline head metadata
ok - rejected metacharacter bytes remain inert at generation and watcher time
ok - static poll is silent except for one merged line and remains watcher-bounded
ok - interrupted atomic preparation cleans private temporaries and publishes nothing
ok - concurrent watchers observe only complete private poll publications
ok - post-rename poll validation faults revoke both names and allow a clean retry
ok - migration creates and validates private state before watcher exclusion
ok - migration pauses older watchers and acquires exclusion before its first scan or marker
ok - poll, marker, diagnostic, and quarantine paths refuse symlinks and directories
ok - marker and diagnostic rename errors and signals fail closed and recover durably on retry
ok - post-rename marker, diagnostic, and obligation faults are revoked and reconstructed on retry
ok - quarantine type and mode faults fail closed and recover only when a retry can validate them
ok - canonical and ambiguous failure obligations block every retry until all task artifacts are repaired
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
●  WATCHER DOWN - SUPERVISION IS OFF
●  1 task(s) in flight, but no watcher has a fresh beacon (last beat: never, grace 300s).
●  Trust the emitted supervision protocol for this harness; do not use shell & for watcher repair.
●  This is a supervision warning only; the guarded operation WILL still run.
●  resume supervision according to the session-start block for this harness; do not use shell &.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ok - ambiguous migration recovery accepts an explicitly validated replacement poll
ok - ambiguous repair rejects copied, metadata- or task-mismatched, forged, and partial poll publications
ok - all live, marker, diagnostic, X, custom-check, obligation, and teardown boundaries require single-link files
ok - canonical publication failure remains incomplete until a later clean retry rebuilds the poll
ok - legacy reserved obligations and delimiter-bearing task IDs retry without ambiguity
ok - migration never executes legacy checks, preserves X mode, quarantines ambiguity, and is idempotent
ok - historical X shims migrate only from the exact single-link mode-0755 identity
ok - direct registration refreshes authenticated v1 X shims across marker states
ok - bootstrap runs the non-executing migration at the locked session boundary
ok - bootstrap isolates incomplete poll migration from unrelated recovery sweeps
ok - watcher signals promptly stop custom checks and clean private state
ok - returned custom check descendants are drained on installed and fallback timeout paths
ok - teardown removes safe poll artifacts and refuses quarantine-directory symlinks without traversal
$ bash tests/fm-gitlab-review-lifecycle.test.sh
ok - GitLab review metadata and authenticated merge poll are recorded
ok - GitLab merged polls retire once while trust tampering preserves evidence
ok - pushed GitLab tasks require canonical merge proof for cleanup
ok - GitLab cleanup proof covers task state and identity failures
ok - the guarded merge path delegates GitLab to the narrow adapter
ok - an explicit GitLab merge method is not overridden
ok - merged GitLab work is proven landed before cleanup
- Outcome: 🔧 1 issue found → auto-fixed (2) ✅ across 3 runs (1h12m32s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 error
  • 🚨 bin/fm-watch.sh:827 - The required criterion says, “Merged-review polling must retire exactly once only after its notification is durable” and must recover safely through watcher races. The wake is durably appended here before the retirement receipt is published. A crash between lines 827 and 829 leaves the poll armed with no recovery receipt, so the next watcher can append a duplicate merged notification. Couple the durable notification to an identity-bound retirement intent that recovery can correlate before permitting another poll execution.
🔧 **Test** - 1 issue found → auto-fixed (2) ✅
  • 🚨 tests failed with exit code 1
  • 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"

🔧 Fix: Supply teardown fixtures’ new check-library dependency
1 error still open:

  • 🚨 tests failed with exit code 1
  • 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"

🔧 Fix: Supply legacy teardown fixture’s check-library dependency
✅ Re-checked - no issues remain.

  • 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"
  • Baseline previously completed: 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-review-diff.test.sh
  • bash tests/fm-pr-check-security.test.sh
  • bash tests/fm-gitlab-review-lifecycle.test.sh
  • Captured combined behavioral output with tee /var/folders/j_/g4b3__rs5j95fq6xf9fb17sc0000gn/T/no-mistakes-evidence/01KYMW7NF1APVGVDT8G2J1RRVJ/review-lifecycle-e2e-transcript.txt
  • Verified git status --short remained empty after testing
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Ivan Miles Piesh added 5 commits July 28, 2026 10:25
Recorded review heads can lag fixes pushed after initial registration,
causing reviewers to assess stale code. Fetch the trusted GitHub or GitLab
review ref into private storage before using the recorded head as an
offline fallback.
Merged review polls otherwise keep producing duplicate notifications after
their terminal result. Retire exact GitHub and GitLab poll artifacts only
after the notification is durable, with identity-bound receipts that make
interrupted cleanup safe to recover without touching task lifecycle state.
@eyevanovich
eyevanovich merged commit 154adfd into main Jul 28, 2026
5 checks passed
@eyevanovich
eyevanovich deleted the fm/firstmate-upstream-review-lifecycle branch July 28, 2026 20:14
eyevanovich added a commit that referenced this pull request Jul 28, 2026
## Intent

Port the approved upstream B3/B4/B5 authority, primary native-delegation
guard, and typed operational-input safeguards onto the current Firstmate
fork as one coherent, tested change. Bound autonomous decisions to the
captain's accepted contract; require captain authority for expansions
into new guarantees, subsystems, threat models, compatibility surfaces,
or repeated questionable abstractions. Prevent genuine primary and
secondmate sessions from starting untracked native subagents while
preserving observation and stop operations, the explicit launch-time
FM_ALLOW_SUBAGENT=1 exception, and legitimate delegation in linked
worker copies. Replace prose and ad hoc markers with the canonical
U+2063 FIRSTMATE_OP v1 typed envelope, opaque bodies, exact kinds,
narrow classifier-only compatibility for locally emitted legacy
transcripts, strict malformed and near-miss rejection, safe
JS/TypeScript adapters, and atomic migration of session-start, watcher,
turn-end, away-supervisor, secondmate-send, and all five harness
launch-brief producers. Keep full procedures in skills, wire formats and
mechanisms in scripts/docs, and concise trigger/safety stubs in
AGENTS.md. Preserve this fork's GitLab, commit-signing, no-mistakes
observer, X/AFK, secondmate, five-runtime, multi-backend, local-only,
review-lifecycle, PR #13 reviewed-head, and merged-poll retirement
behavior. Treat upstream commits as evidence rather than authority;
reimplement against the fork. Preserve current behavior where live
integration cannot be verified, explicitly leaving OpenCode/Grok native
delegation wiring pending rather than claiming unsupported enforcement.
Do not merge the resulting PR.

## What Changed

- Bound autonomous ask-user decisions to the captain-approved contract
and added an escalation procedure for material contract expansions.
- Introduced the canonical typed operational-input envelope and migrated
session, watcher, turn-end, away, messaging, and launch-brief producers
across supported runtimes.
- Added primary and secondmate native-delegation guards while preserving
observation, stop operations, the explicit launch-time exception, and
delegation from linked worker copies.

## Risk Assessment

✅ Low: Captain, the change coherently implements the required authority,
scoped delegation, typed operational-input, compatibility, and
five-harness producer boundaries without a substantiated defect or
intent contradiction.

## Testing

The supplied baseline had passed; focused authority, typed-input,
producer-migration, native-delegation, session-start, secondmate-send,
and turn-end tests also passed, while a captured CLI transcript
demonstrates the user-visible protocol and guard behavior end to end.
This change has no rendered UI surface, so text CLI evidence is the
appropriate reviewer-visible artifact.

<details>
<summary>Evidence: End-to-end typed-envelope and delegation-guard
transcript</summary>

```text
$ printf body | bin/fm-operational-input.sh encode watcher | bin/fm-operational-input.sh kind
watcher
$ encoded envelope (hex prefix and visible payload)
 e2 81 a3 46 49 52 53 54 4d 41 54 45 5f 4f 50 3a
⁣FIRSTMATE_OP: v1 watcher: signal: task-42 complete
$ malformed ASCII near-miss classification (expected: empty)
classifier output: <>
$ genuine primary attempts collaboration.spawn_agent (expected: denied)
exit: 2
{"decision":"deny","reason":"[subagent-dispatch] the Firstmate primary dispatches through its durable supervised lifecycle, not native delegation tools: work started natively has no durable fleet record and dies with this session. Instead, first classify the work under the AGENTS.md intake contract, then write its instructions with bin/fm-brief.sh and dispatch it with bin/fm-spawn.sh (blocked tool: collaboration.spawn_agent, launch-shaped on \"agent\"). Launch the primary session with FM_ALLOW_SUBAGENT=1 for a deliberate exception."}
$ genuine primary observes with collaboration.list_agents (expected: allowed)
exit: 0
$ explicit launch-time exception FM_ALLOW_SUBAGENT=1 (expected: allowed)
exit: 0
```
</details>

## Pipeline

Updates from [git push
no-mistakes](https://github.com/kunchenguid/no-mistakes)

<details>
<summary>✅ **intent** - passed</summary>

✅ No issues found.

</details>

<details>
<summary>✅ **Rebase** - passed</summary>

✅ No issues found.

</details>

<details>
<summary>✅ **Review** - passed</summary>

✅ No issues found.

</details>

<details>
<summary>✅ **Test** - passed</summary>

✅ 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"`
- `Baseline full test command previously completed successfully as
supplied by the outer executor.`
- `bash tests/fm-ask-user-authority.test.sh`
- `bash tests/fm-operational-input.test.sh`
- `bash tests/fm-subagent-pretool-check.test.sh`
- `bash tests/fm-send-secondmate-marker.test.sh`
- `bash tests/fm-sessionstart-nudge.test.sh`
- `bash tests/fm-turnend-guard.test.sh`
- <code>Manual CLI verification of U+2063 envelope
encoding/classification, malformed ASCII near-miss rejection, primary
native-spawn denial, observation allowance, and exact
`FM_ALLOW_SUBAGENT=1` exception.</code>

</details>

<details>
<summary>✅ **Document** - passed</summary>

✅ No issues found.

</details>

<details>
<summary>✅ **Lint** - passed</summary>

✅ No issues found.

</details>

<details>
<summary>✅ **Push** - passed</summary>

✅ No issues found.

</details>

---------

Co-authored-by: Ivan Miles Piesh <ivan@ivanpiesh.info>
eyevanovich added a commit that referenced this pull request Jul 29, 2026
## Intent

Port the approved upstream supervision-continuity and
AFK/current-run-attribution end state onto the current Firstmate fork.
Add bounded one-owner successor cycles for Pi and OpenCode, Claude
Stop-owned auto-arm with shared session-lock identity and bounded block
cooperation, structured AFK terminal classification with independent
wedge aging, and exact code/head-bound no-mistakes run attribution.
Preserve PR #13/#14 security, authority, observer, delegation, signing,
GitLab, and canonical FIRSTMATE_OP behavior; keep Codex and Grok
continuity unchanged; include genuine secondmate primaries while
excluding child task copies and observer/daemon/diagnostic/unrelated
windows. Use the pre-calm Pi implementation and do not introduce
unavailable calm dependencies or the rejected upstream command-gate
change. Update ownership docs and add hermetic plus current-version live
verification, reporting Claude authentication as a blocker rather than
claiming success.

## What Changed

- Add bounded, single-owner watcher successor cycles for Pi and
OpenCode, plus Claude Stop-owned auto-arm coordinated through verified
session-lock identity and bounded turn-end recovery.
- Make AFK terminal classification and wedge aging independent, and bind
no-mistakes run attribution to both branch and commit ancestry.
- Extend primary-session scoping, lifecycle diagnostics, documentation,
and hermetic/live coverage while preserving Codex and Grok continuity.

## Risk Assessment

✅ Low: The follow-up binds auto-arm ownership to process identity and
scopes successor-cycle behavior to Pi, OpenCode, and Claude while
preserving Grok’s default one-cycle semantics, with no remaining
material source issue identified.

## Testing

Completed 1 recorded test check.

- Outcome: ⚠️ 1 error across 2 runs (1h2m47s)

## Pipeline

Updates from [git push
no-mistakes](https://github.com/kunchenguid/no-mistakes)

<details>
<summary>✅ **intent** - passed</summary>

✅ No issues found.

</details>

<details>
<summary>✅ **Rebase** - passed</summary>

✅ No issues found.

</details>

<details>
<summary>🔧 **Review** - 2 issues found → auto-fixed ✅</summary>

- 🚨 `bin/fm-turnend-guard.sh:180` - The Claude guard treats any live
process with the PID recorded in `.claude-autoarm.lock` as proof that
recovery is underway. If an auto-arm process dies without releasing the
lock and its PID is reused, `fm_lock_try_acquire` also refuses the stale
lock, while this guard indefinitely allows stops with no watcher. Bind
generic lock ownership to process identity, as watcher locks already do,
and verify that identity at this shared lock boundary.
- 🚨 `bin/fm-watch-arm.sh:261` - The required criterion says to “keep
Codex and Grok continuity unchanged,” but the shared arm wrapper used by
Grok now follows successor watchers and converts an attached cycle
ending without a successor from a clean completion into a nonzero
failure. This changes Grok’s documented re-arm/notification behavior;
scope the new successor semantics to Pi/OpenCode (and Claude where
intended), or obtain explicit approval to change Grok continuity.

🔧 Fix: Bind auto-arm identity and preserve Grok continuity
✅ Re-checked - no issues remain.

</details>

<details>
<summary>⚠️ **Test** - 1 error</summary>

- 🚨 tests failed with exit code 1
- `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"`

🔧 Fix: Bound recursive lock stealing and stabilize identity mocks
1 error still open:
- 🚨 tests failed with exit code 1
- `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"`

</details>

<details>
<summary>✅ **Document** - passed</summary>

✅ No issues found.

</details>

<details>
<summary>✅ **Lint** - passed</summary>

✅ No issues found.

</details>

<details>
<summary>✅ **Push** - passed</summary>

✅ No issues found.

</details>

---------

Co-authored-by: Ivan Miles Piesh <ivan@ivanpiesh.info>
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.

1 participant