Skip to content

branch: report role-limited MAIN-only outcomes as captain verdict - #30

Merged
pruge merged 1 commit into
mainfrom
fm/fm-branch-actionable-wake
Aug 26, 2026
Merged

pruge merged 1 commit into
mainfrom
fm/fm-branch-actionable-wake

Conversation

@pruge

@pruge pruge commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Problem

The supervision branch reports every handled fleet event as routine or captain (fm_branch_report). Only captain opens a follow-up turn on MAIN (.pi/extensions/fm-branch-supervision.ts:340-349); routine merges a note with no turn.

The branch is deterministically forbidden from three MAIN-only actions (bin/fm-lease-lib.sh's fm_lease_forbid_branch):

  • bin/fm-pr-merge.sh:45 - PR merge
  • bin/fm-merge-local.sh:25 - local-only landing
  • bin/fm-spawn.sh:993 - new-task spawn

When the branch handles an event whose finishing action is one of these and reports it routine, nobody acts on it: the branch can't (role limit) and MAIN never wakes (no turn opens). Measured evidence (2026-08-26): PR kunchenguid#107's merge-ready outcome was reported routine three times (fm_branch_outcomes seq 26/27/28) and sat unhandled for 8 minutes until the captain noticed; the same gap left a crew model-policy update (seq 228) unhandled until the captain re-raised it.

Fix

Added exactly one bullet to the existing captain list in bin/fm-branch-prompt.sh's "Verdict: routine or captain" section: an outcome only MAIN can finish because the branch's role limits forbid it (PR ready to merge, local-only landing ready to approve, a new task that needs spawning), with the reason inline (reporting it routine leaves it done by nobody). No new delivery tier, no change to .pi/extensions/fm-branch-supervision.ts's routine/captain delivery mechanics, no change to what "routine" means for everything else.

Updated docs/pi-supervision-branch.md's line 49, which previously claimed the verdict criteria mirror only the captain-etiquette escalation list - that's no longer exactly true, since this addition has no counterpart in captain etiquette (it exists purely because of the branch's own role limits).

Evidence

  1. Rendered prompt output showing the new criterion (not a diff):
$ bin/fm-branch-prompt.sh | sed -n '36,47p'
# Verdict: routine or captain

Report verdict captain only for what a human must see:
- work ready for review - always include the full https:// PR URL in the summary;
- a decision only the captain can make, including every ask-user finding from a validation gate;
- a real blocker or failure after the playbook is exhausted;
- a needed credential or login;
- anything destructive, irreversible, or security-sensitive;
- an outcome only MAIN can finish because your role limits forbid it - a PR ready to merge, a local-only landing ready to approve, a new task that needs spawning - since reporting one of these routine would leave it done by nobody: you cannot act on it and routine never wakes MAIN.
Everything else - routine status, a successful automatic recovery, an absorbed poll, a healthy pause - is verdict routine.
  1. Existing branch tests pass (bin/fm-test-run.sh --changed --base main, full logs in /tmp/changed-test-run.log in the task worktree):
FM_TEST_BEGIN tests/fm-branch-supervision.test.sh
ok - branch prompt is byte-stable across homes, cwd, timezone, and time, above the cache floor
ok - outcome store is append-only and refuses sequence reuse after a torn tail
ok - startup replay skips silent outcomes and preserves visible and legacy rows
ok - lease exclusivity, same-actor refresh, release, staleness, and sweep hold
ok - fm-control and fm-teardown refuse the other actor's live lease and pass through otherwise
ok - PR merge, local landing, and new-task spawn refuse the branch actor and spare main
ok - a non-Pi home ignores stale Pi leases even when the recycled pid owns its lock
ok - lease liveness requires an exact valid session-lock pid
ok - concurrent stale-lease claims serialize so exactly one actor succeeds
ok - guard stale cleanup cannot race with or delete a newer lease claim
ok - lease guard excludes a concurrent actor for the complete mutation
ok - a claim naming the other actor fails loudly instead of silently impersonating it
ok - release commands authorize the caller and bulk release drops only that actor's leases
ok - the branch cannot force a teardown or bypass fm-control for a relaunch
FM_TEST_END tests/fm-branch-supervision.test.sh exit=0

FM_TEST_BEGIN tests/fm-pi-branch-extension.test.sh
ok - branch owns accepted wakes with a stable prefix contract and verdict-driven merge delivery
ok - branch prompt_cache_key is stable per home across sessions and distinct between homes
ok - branch default-on eligibility (task-scoped, heartbeat, afk) binds and a broken branch falls back to main
ok - pre-drain eligibility re-check defers a newly main-owned row
ok - pre-drain eligibility re-check no-ops an already-drained wake
ok - dialog mirror filters tool and operational traffic, lands before wakes, and keeps a durable cursor
ok - branch session persists across process restarts through the recorded pointer
ok - replacement activation cleans old branch leases and retries failed cleanup
ok - branch activates on a cold start once the lock is acquired, never before
ok - queued wakes and mirrors stop mutating branch state after lock ownership is lost
ok - stale reports, shells, mirrors, cursors, leases, and prompts perform no side effects
ok - a Pi session that does not own the lock accepts nothing and mutates no branch state
ok - an extension rebind re-mirrors undelivered dialog instead of dropping it
FM_TEST_END tests/fm-pi-branch-extension.test.sh exit=0

Full changed-selection run: FM_TEST_SUMMARY total=28 failed=0 skipped_gate=1.

  1. The new criterion is pinned by test: tests/fm-branch-supervision.test.sh's test_branch_prompt_is_byte_stable_and_above_cache_floor already asserted prompt substrings (role preamble, recovery playbook), so I added one more substring assertion there covering the new bullet's key phrases ("only MAIN can finish", "PR ready to merge", "routine never wakes MAIN") rather than inventing a new test file.

  2. docs/pi-supervision-branch.md line 49 read and corrected: it previously said the verdict criteria "mirror the captain-etiquette escalation list," which was no longer literally true once this branch-role-limit-only criterion was added (captain etiquette has no counterpart for it, since only the branch has role limits). Reworded to say the criteria mirror that list "plus one addition that list has no reason to carry," with the reason stated inline.

Not done (scope discipline)

Did not touch .pi/extensions/fm-branch-supervision.ts's routine/captain delivery mechanics and did not add a third delivery tier - the brief explicitly ruled that out as a much larger change (extension + docs + tests) when the defect is fully addressed by tightening the verdict criterion in the branch prompt.

Reading this codebase

Used bin/fm-project.sh first per the brief, but this worktree has no data/projects.md registry entry for firstmate pointing at itself (no registry at .../data/projects.md, and fm-project.sh list reports the same), so the door never engaged for this task. Fell back to plain grep -n and read throughout, which worked fine for this task's shape: two known symbols (fm_lease_forbid_branch, fm_branch_report) with brief-supplied line numbers, and one prose contract file (bin/fm-branch-prompt.sh) that is comments-as-text rather than code structure - not a natural fit for a symbol-graph query anyway. Never got to try the P explore/node/query/callers/callees/impact verbs since nothing here needed a structural "who calls this" answer; everything was "find this exact string/section" (P grep/P read's territory) and plain grep -rn answered it in one shot. One concrete improvement: if the door auto-detected "this checkout IS the project" when no registry entry matches and the cwd itself is a git repo whose remote matches a known project name, it would have saved the one dead-end call and the registry lookup before falling back.

The supervision branch cannot merge a PR, land local-only work, or
spawn a new task (fm_lease_forbid_branch in fm-pr-merge.sh,
fm-merge-local.sh, fm-spawn.sh). When such an outcome was reported
routine, no follow-up turn opened on MAIN, and nobody else can act on
it either - the branch is forbidden and routine never wakes MAIN. Add
this class to the branch prompt's captain-verdict list, with the
reason inline, and update docs/pi-supervision-branch.md's claim that
the verdict criteria mirror only the captain-etiquette escalation
list.
@pruge
pruge merged commit 1be2fdc into main Aug 26, 2026
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