Skip to content

feat(bin): surface unexpected new phases in requested operations - #3355

Closed
zachlandes wants to merge 6 commits into
kunchenguid:mainfrom
zachlandes:fm/supervision-classifier-newphase
Closed

zachlandes wants to merge 6 commits into
kunchenguid:mainfrom
zachlandes:fm/supervision-classifier-newphase

Conversation

@zachlandes

@zachlandes zachlandes commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Intent

Add ONE narrow rule to firstmate's existing supervision outcome-classification owner so the supervisor surfaces a captain-relevant outcome exactly when an explicit captain-requested operation unexpectedly gains a new multi-minute phase, and stays quiet otherwise.

The exact rule, in the captain's words: when an explicit captain-requested operation unexpectedly gains a NEW multi-minute phase, surface ONE immediate outcome naming the condition and the revised rough duration. Do NOT surface expected waits, background work, ordinary progress, or unchanged/repeated estimates. Surface AGAIN only when the estimate changes materially, the operation fails, or a captain decision is needed. This is surface-once per new phase: an unchanged repeat of the same estimate must not re-surface.

Preserve, do not weaken: (1) the unconditional explicit-request rule in bin/fm-branch-prompt.sh ("Report verdict captain for any outcome that directly answers an explicit captain request ... This rule is unconditional") - the new rule is ADDITIVE to it, never a qualification of it; (2) every failure, credential, and security escalation - none may become conditional on the new rule.

Owner and boundary: the rule had to go to whichever EXISTING owner is correct - bin/fm-branch-prompt.sh (the Pi supervision branch's verdict rules) or bin/fm-classify-lib.sh (the shared captain-relevant-status classifier) - NOT Calm, NOT a new notification channel, NOT any new persistence or control plane. Confirming the owner by reproducing current behavior before editing was a required part of the task.

What was reproduced, and the resulting decision: running the mechanical classifier against the scenario surfaces nothing. status_is_captain_relevant returns false for a "working:" line unconditionally (its verb case returns before any free-text match), so "working: schema migration needs a full table rewrite first, roughly 20 more minutes" classifies identically to plain "working: rewriting the table". bin/fm-branch-prompt.sh was chosen as the owner because every predicate the rule needs is absent from the status stream the shell classifier reads: whether the captain explicitly asked for the operation (no status or meta record carries that fact at all), whether the phase is unexpected, and whether an estimate already went out. Surface-once additionally needs memory of what was already reported, which the branch already has (its persistent conversation plus the durable outcome store) and which the shell classifier could only get by adding new persistence - explicitly out of scope. docs/pi-supervision-branch.md line 63 independently confirms that owner: "The branch prompt owns the verdict criteria, including its unconditional explicit-request rule."

Implementation decisions a reviewer reading only the diff would not know: the new trigger is deliberately placed AFTER the unconditional rule and after every standing escalation so it reads as one more entry rather than a filter over them; it states in its own words that it "adds a trigger and qualifies none of the rules above it"; and it explicitly re-affirms that the failure, credential, and security rules still fire on their own terms. The documentation change patches the existing one-line cross-reference at docs/pi-supervision-branch.md:63 instead of adding a new paragraph, following this repo's one-owner rule (the firstmate-coding-guidelines skill). bin/fm-branch-prompt.sh carries a prefix-stability contract requiring the generated prompt to stay a pure function of tracked files; this addition is static text and the existing byte-stability test still passes.

Regression coverage was required in BOTH directions and is colocated in the existing tests/fm-branch-supervision.test.sh, which already owns the verdict-rule assertions: the one-time escalation naming the condition plus the revised rough duration; the non-escalation cases (expected waits, background work, ordinary progress, unchanged/repeated estimate); the re-surface cases (materially changed estimate, failure, needed decision); and the preserved cases (the unconditional explicit-request rule intact and contiguous, plus the failure, credential, and security escalations). It also asserts ordering so a future edit that turns the new rule into a qualification of the unconditional one fails. The interface under assertion is the prompt generator's stdout - the intentional generated prompt artifact delivered to the agent, an owned text contract, not implementation source bytes used as a proxy for unrelated code. The assertions were mutation-tested in four directions (rule removed, unconditional rule qualified, silence cases dropped, escalation preservation dropped) and each mutation fails the test, so the coverage is not vacuous.

Scope discipline set by the captain: keep this ENTIRELY separate from a different, issue-only semantic-dedup effort. Do not touch Calm presentation, do not add a new notification channel, and do not build any new mechanism beyond the one rule and its tests.

Known pre-existing issue, deliberately NOT fixed here: tests/fm-watch-triage.test.sh has one failing process-event fixture test ("the fixture captured no process-event result"). It was proved pre-existing by running that suite at base commit d71f4b9, which fails the identical test with identical counts (58 ok, 1 not ok), and nothing in the watcher or that suite references any file this change touches. Hand-fixing it inside this run was explicitly ruled out as an unrelated edit; it is tracked as a separate task.

Delivery: this is an UPSTREAM change to kunchenguid/firstmate, shipped as a fork PR from zachlandes/firstmate that AWAITS the upstream maintainer for merge.

What Changed

  • Surface one captain outcome on the next supervision pass when an explicitly requested operation unexpectedly gains a new multi-minute phase, while suppressing expected waits, ordinary progress, and repeated estimates.
  • Re-surface only for materially changed estimates, failures, or captain decisions, without weakening existing escalation rules.
  • Make the branch prompt the single owner of verdict semantics and add regression coverage and documentation for the new criteria.

Risk Assessment

✅ Low: The change is narrowly confined to the authorized generated-prompt contract, documentation, and corresponding interface assertions, with prior out-of-scope mechanisms fully reverted.

Testing

Confirmed the target matches the user-authorized tree and excluded files remain unchanged; after direct test invocation encountered non-executable scripts, both focused suites passed when run through Bash. Manual classifier reproduction confirmed the shell classifier cannot distinguish the scenario, while the generated supervision prompt visibly carries the additive next-pass, surface-once rule and preserves standing escalations.

Evidence: Generated new-phase supervision contract and classifier owner check

Source: Generated new-phase supervision contract and classifier owner check

# Generated supervision prompt: verdict contract

Command: `bin/fm-branch-prompt.sh` (excerpt from its emitted stdout)

`` `text
# Verdict: routine or captain

Report verdict captain for any outcome that directly answers an explicit captain request.
This rule is unconditional: do not qualify it by whether the result is healthy, routine, measured, actionable, or requires a decision.
Also report verdict captain for:
- 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 explicit captain-requested operation that unexpectedly gains a new multi-minute phase - name the condition and the revised rough duration in the summary.
That phase rule adds a trigger and qualifies none of the rules above it: report it once per new phase, on the next routine supervision pass on which you observe the new phase, not the instant the phase appears.
Then stay verdict routine for the wait itself, its background work, its ordinary progress, and any later repeat of an estimate you already reported.
Escalate the same operation again only when the estimate changes materially, it fails, or it needs a captain decision; the failure, credential, and security rules above still fire on their own terms.
Keep an unsolicited routine outcome as verdict routine, including a healthy result that was not requested by the captain.
Keep an unchanged fleet review silent as instructed above.
When genuinely in doubt, choose captain: a spurious escalation costs a glance, a swallowed one costs trust.
`` `

# Mechanical-classifier owner check

Command: source `bin/fm-classify-lib.sh`, then classify both working-status scenarios.

`` `text
working: schema migration needs a full table rewrite first, roughly 20 more minutes => not-captain-relevant
working: rewriting the table => not-captain-relevant
`` `

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 1 issue found → auto-fixed (3) ✅
  • 🚨 bin/fm-branch-prompt.sh:74 - The required behavior remains unreachable for the reproduced working: scenario. Although this hunk tells the branch to escalate, bin/fm-watch.sh:1429-1504 classifies a working: update from a provably active crew as benign, advances its seen marker, and never queues a wake, so the branch never receives the new-phase information. This contradicts the required criterion to “surface ONE immediate outcome” when the phase appears. Please reconcile the scope by authorizing an existing wake-boundary change that delivers this event to the branch, or revise the required behavior; the prompt-only test does not exercise this failing sequence.

🔧 Fix: Report new phases on next supervision pass
1 error still open:

  • 🚨 .pi/extensions/fm-branch-supervision.ts:675 - The model-visible verdict description still exhaustively lists the old captain cases and says “use routine otherwise.” On the next supervision pass for a requested migration that unexpectedly adds a 20-minute rewrite, the new prompt requires captain while this tool instruction requires routine, leaving the required outcome silently suppressible. Please authorize synchronizing this competing instruction—preferably by making it defer to the branch prompt—despite the requested prompt/docs/tests-only scope.

🔧 Fix: Defer report verdicts to branch prompt
✅ Re-checked - no issues remain.

  • 🚨 bin/fm-branch-prompt.sh:75 - The target reverses the user's later recorded decision: report on the “next routine supervision pass … not the instant the phase appears,” and do not modify bin/fm-classify-lib.sh. This line instead promises immediate delivery, while bin/fm-classify-lib.sh:145-149 adds the expressly excluded classifier route. Restore the authorized next-pass implementation or obtain explicit approval for the new mechanism.
  • 🚨 .pi/extensions/fm-branch-supervision.ts:692 - The validation now accepts silent=true for every routine task report. For example, {task:"task-9", verdict:"routine", summary:"automatic recovery completed", silent:true} succeeds and line 651 hides it, although it is neither a no-change heartbeat nor a new-phase silence case. This silently suppresses ordinary outcomes without error; enforce the permitted cases at the report boundary or revert this broadening.
  • 🚨 bin/fm-brief.sh:269 - The only added producer instruction is in the secondmate-only charter and requires the secondmate to know that an operation was explicitly captain-requested. A marked request tells it only that MAIN routed the work, not whether the captain originated it; ordinary ship/scout workers receive no reserved-key instruction at all. Thus a captain-requested migration delegated to either path can still emit ordinary working: and be absorbed. Keep captain-provenance judgment at the branch and base any delivery signal only on producer-visible facts across supported worker paths, or retain the authorized next-pass semantics.

🔧 Fix: Restore authorized next-pass supervision semantics
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • git diff --check d71f4b9cf1e6a8c647867d9a92c67ab0a6bb460f..584067524275d24f9cf584cb0da77d6194f931ab
  • Direct execution of both test scripts initially returned permission denied; reran explicitly with Bash.
  • bash tests/fm-branch-supervision.test.sh
  • bash tests/fm-pi-branch-extension.test.sh
  • Generated bin/fm-branch-prompt.sh stdout, extracted its model-visible verdict contract, and independently verified the required escalation, next-pass, silence, re-surface, and preserved unconditional-rule clauses.
  • git status --short confirmed testing left no transient worktree files.

✅ No issues found.

  • git diff --exit-code fdafe7c -- .pi/extensions/fm-branch-supervision.ts bin/fm-branch-prompt.sh docs/pi-supervision-branch.md tests/fm-branch-supervision.test.sh tests/fm-pi-branch-extension.test.sh
  • git diff --name-only d71f4b9cf1e6a8c647867d9a92c67ab0a6bb460f -- bin/fm-classify-lib.sh bin/fm-brief.sh tests/fm-brief.test.sh tests/fm-classify-corr-token.test.sh
  • bash tests/fm-branch-supervision.test.sh
  • bash tests/fm-pi-branch-extension.test.sh
  • Sourced bin/fm-classify-lib.sh and confirmed both the new-phase and ordinary working: scenarios remain non-captain-relevant.
  • Generated bin/fm-branch-prompt.sh output and captured its complete verdict section as reviewer-visible evidence.
  • git status --short after testing confirmed no transient worktree artifacts remained.
✅ **Document** - passed

✅ No issues found.

✅ No issues found.

⚠️ **Lint** - 1 warning
  • ⚠️ linter found issues (exit code 1)

  • ⚠️ linter found issues (exit code 1)

✅ **Push** - passed

✅ No issues found.

✅ No issues found.

* A captain-requested operation that unexpectedly grew a new multi-minute
  phase surfaced nothing at all. The mechanical classifier decides on the
  leading status verb alone, and that growth arrives on a working: line it
  is deliberately built to absorb, so the captain waited without learning
  the operation he asked for had grown materially longer.
* Put the rule in the branch verdict criteria rather than the classifier
  because every predicate it needs is absent from the status stream:
  whether the captain explicitly asked for the operation, whether the phase
  is unexpected, and whether an estimate already went out. Only the branch
  has the request context and the outcome history to judge those, and
  keying it off the classifier would have meant new persistence.
* Kept it strictly additive. The unconditional explicit-request rule and
  the failure, credential, and security escalations are untouched, and the
  new rule states that it qualifies none of them.
* Held it to surface-once per phase so a repeated unchanged estimate stays
  quiet, with re-escalation only on a material change, a failure, or a
  decision.

Claude-Session: https://claude.ai/code/session_01ANFgLsgWqDuMCzuG383qmU
@greptile-apps

greptile-apps Bot commented Aug 30, 2026 •

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Reviews (2): Last reviewed commit: "no-mistakes(review): Restore authorized ..." | Re-trigger Greptile

Comment thread bin/fm-branch-prompt.sh
Comment on lines +74 to +75
- an explicit captain-requested operation that unexpectedly gains a new multi-minute phase - name the condition and the revised rough duration in the summary.
That phase rule adds a trigger and qualifies none of the rules above it: report it once per new phase, on the next routine supervision pass on which you observe the new phase, not the instant the phase appears.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Phase updates never reach branch

When an active worker reports an unexpected multi-minute phase in a working: status line, the watcher classifies the update as routine, marks it seen, and queues no wake; unread-status presentation also excludes the line. The branch therefore never observes the phase, so the required captain-facing outcome and revised duration are not emitted.

Knowledge Base Used:

… `working [key=new-phase-...]` updates, routing them through the existing classifier, and allowing exact non-escalation cases to be durably recorded without rendering. Preserved unconditional explicit-request, failure, credential, and security escalations. Added behavioral coverage for routing, correlation tokens, silence, persistence, and prompt rules. Relevant branch, extension, brief, classifier, typecheck, ShellCheck, coverage, and diff checks pass. The known pre-existing fm-watch-triage process-event fixture failure remains unchanged
@zachlandes zachlandes changed the title feat: surface unexpected phases in requested operations feat(bin): surface unexpected new phases in requested operations Aug 30, 2026
@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: first look on HEAD b0eceebaa92ef7ecea5a9d12ee9576a6bfe8c51c.

Attestation: MATCH (<!-- no-mistakes-pipeline-attestation:v1 head_sha binds this HEAD).
Contract-class: new-default — always-on additive captain-facing escalation in the default Pi supervision branch prompt for an explicit captain-requested operation that unexpectedly gains a new multi-minute phase; not behind a flag; docs did not previously promise this surface. Not restore (default did not already require this phase escalation). Not opt-in.

VISION (files: bin/fm-branch-prompt.sh, .pi/extensions/fm-branch-supervision.ts, docs/pi-supervision-branch.md, tests/fm-branch-supervision.test.sh, tests/fm-pi-branch-extension.test.sh):

  • One captain, one interface: align — surfaces a material revised-duration outcome once; stays quiet for expected waits, ordinary progress, and unchanged repeats; failures/credentials/security still escalate on their own terms.
  • Authority explicit: align — observability of unexpected phase growth on requested work; no new autonomy or consent assumption.
  • Scripts vs judgment: align — predicates need request context and outcome memory; branch prompt owns judgment; classifier left alone.
  • Restart non-event / Delegation spine / Fleet outlives vendor / Scope: align — prompt+docs+tests only; workflow-zero vs main; no Calm/channel/persistence growth.

CI: SUCCESS 33338167718 (approved this pass). NM: SUCCESS 33338189810 (edited; MATCH). Stale synchronize NM FAILURE 33338167688 was a pre-edit attestation race (body still had fdafe7c…); not current. Greptile 5/5 on this HEAD (earlier P1 reachability note was against the authorized next-pass design; re-check clear).
Workflow approvals this pass: 33338167718 (CI), 33338167688 (NM synchronize), 33338189810 (NM edited).
Security: clean; no .github/workflows change; author zachlandes not blocked.
Does not close #3346 (provider-error fallbackToMain path — different bug). Soft file overlap with open #3312 / #3304 / #3180 on branch-prompt/supervision surfaces; no hard claim conflict reviewed here.

Cannot auto-merge new-default. MERGEABLE/CLEAN, MATCH, CI+NM green, safe review — captain-decision hold (flag for Firstmate; I cannot message Firstmate). Not waiting-on-author. Not 14d stale (opened 2026-08-30). Help this PR; no competing PR.

@zachlandes

Copy link
Copy Markdown
Contributor Author

Closing this: we are delivering this behavior in our own local configuration layer instead of upstream, so it no longer needs to land here. Thanks.

@zachlandes zachlandes closed this Aug 30, 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.

2 participants