Skip to content

feat: close finished Herdr task panes after delivery receipt - #285

Merged
dnth merged 33 commits into
mainfrom
fm/fm-pane-autoclose
Oct 29, 2026
Merged

dnth merged 33 commits into
mainfrom
fm/fm-pane-autoclose

Conversation

@dnth

@dnth dnth commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

bin/fm-branch-outcome.sh deliver now hands its newest covered endpoint and status identity to a new gate, so a finished task's pane closes when its delivery receipt lands and nothing else is owed.

deliver --task T --status-ident D:I --endpoint N      (bin/fm-branch-outcome.sh)
  record receipt in state/completion-deliveries.jsonl
  fm-pane-autoclose.sh T --endpoint N --status-ident D:I
    live standing event byte endpoint == N
    standing line is done: AND carries its kind/mode artifact
    no open keyed decision/blocker        (status fold)
    no unanswered recorded captain-call   (fm-captain-hold verify, only when
                                           decisions_reviewed=1)
    no ext/x link, no intended check, no open routed reply,
    no unresolved public promise (primary-home owner resolved for a
    delegated secondmate task), no held/blocked board row
    endpoint from state/<id>.meta only  ->  herdr presentation lock
      -> focus-preserving exact-pane close (refuses the active tab,
         never workspace close)  ->  pane confirmed gone

Everything else keeps the pane: a failed:, paused:, or unkeyed-decision receipt, a later status event, a rotated status identity, a local-only completion, a secondmate home, or any follow-on work above. A missing or ambiguous endpoint refuses loudly (exit 2); an absent metadata record means no managed pane exists and nothing is judged. No durable task record is removed.

Files: bin/fm-pane-autoclose.sh (new, owns the gate), bin/fm-branch-outcome.sh (calls it after the receipt is durable), bin/fm-public-followup-lib.sh (teardown's promise-owner resolution extracted so the gate can reuse it), bin/fm-test-run.sh (family map), docs.

Evidence

  • Before: closure only happened at teardown, so a work product delivered as a receipt (finished scout, finished audit) left its pane open indefinitely; Herdr upstream has no auto-reap.
    After: the exact pane closes on the receipt, verified against real Herdr.
Check Result
Guarded lab, final head d1e7bf99 (lab fm-lab-fm-pane-autoclos-1696283-27260) 16/16 pass, 0 skips
Unit suite (tests/fm-pane-autoclose.test.sh) 22/22 pass
CI on this head 19/19 checks pass
  • Live validation ran outside the pipeline sandbox via the guarded bin/fm-herdr-lab.sh helper (named non-default lab session, default-session tripwire verified before and after): the pipeline sandbox cannot provision the lab because it redirects Herdr storage inside its worktree and the tripwire requires the captain's running default session.
  • Herdr 0.9.3 measured behaviour: a close leaves the existing session-history.json intact, and Herdr's next periodic flush rewrites it with live panes only, so a closed pane leaves Herdr's persisted history then. Auto-closed panes therefore do not keep their transcript in Herdr history; the test asserts what the close path controls (history intact at close, surviving pane durable, session still running) and deliberately does not pin that flush.

Merge Danger

Door: two-way. Reverting removes one call site and one script; no data migration, no schema, and no record is deleted by the change.

Blast Radius: medium. Every task delivery receipt now evaluates the gate, and the promise-owner resolution moved into a shared library that teardown also uses.

Pipeline

Updates from git push no-mistakes

✅ **Intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed ✅
  • ⚠️ tests/fm-pane-autoclose-herdr-e2e.test.sh:250 - The intent says: "Close-after-receipt is proven end to end for a scout whose row is still In flight and for a ship PR completion with no inventory." The guarded real-Herdr lab proves only the scout close (section 1, lines 207-250). Every later lab case is a keep or a refusal. The ship-PR-without-inventory close is covered only in tests/fm-pane-autoclose.test.sh:245 (test_ship_pr_receipt_without_inventory_closes), which runs deliver against a fake herdr. The round-1 review-1 decision put that regression in the unit suite. It is unclear whether "end to end" also requires a real-Herdr lab case. If it does, add a lab case: a kind=ship, mode=no-mistakes task with no decisions_reviewed, status done: PR https://.../pull/N, delivered through deliver, asserting closed: and that the pane is gone.
  • ⚠️ tests/fm-pane-autoclose-herdr-e2e.test.sh:27 - Simplification: the lab test adds an optional FM_PANE_AUTOCLOSE_ARTIFACT_DIR export knob (header line 27, copy block around lines 285-291). It copies the transcript and history snapshots outside the scratch root. No intent requirement asks for exported review artifacts, and no other repo file sets this variable. The persisted-history assertions run without it. Recommended remedy: remove the knob and its copy block.

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

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 12 of 13 scenarios driven live against the product
Scenario Result Live Evidence
Lab: scout done: receipt with a named report, board row still In flight, delivered and its exact pane closes ✅ pass live herdr-lab-e2e.log: 'closes the pane after a scout done: receipt while its board row is In flight'
Lab: ship PR completion with no captain-call inventory is delivered and its pane closes ✅ pass live herdr-lab-e2e.log: 'closes a ship PR completion's pane when no captain-call inventory exists'
Lab: closing a pane leaves the session running, the sibling pane readable, the history file intact and the process files on disk ✅ pass live herdr-lab-e2e.log: 'closure keeps Herdr's persisted history intact; the session survives the next flush'
Lab: exclusions keep the pane (failed receipt, keyed decision, keyed blocker, blocked row, held row, local-only completion, linked captain call, external request, routed reply, armed check, primary-ho… ✅ pass live herdr-lab-e2e.log: 11 'keeps the pane…' lines
Lab: the captain's active tab is never closed ✅ pass live herdr-lab-e2e.log: 'never closes the captain's active tab'
Lab: ambiguous endpoint metadata is refused with exit 2 and no pane closes ✅ pass live herdr-lab-e2e.log: 'refuses loudly on an ambiguous endpoint and closes nothing'
Lab: after done(X), working, done(X), the older byte endpoint keeps the pane and the latest endpoint closes it ✅ pass live extra-lab-driver.log A-old-endpoint(54)/A-new-endpoint(127)
Lab: delivering an unkeyed needs-decision receipt records it and keeps the pane ✅ pass live extra-lab-driver.log B
Lab: a done: line without a named artifact keeps the pane ✅ pass live extra-lab-driver.log C
Lab: a secondmate home record keeps its pane ✅ pass live extra-lab-driver.log D
Lab: a task with no metadata record closes nothing (exit 0, nothing to judge) ✅ pass live extra-lab-driver.log E(rc=0)
Lab: the default Herdr session is untouched and lab sessions are torn down ✅ pass live sessions-before.json/sessions-after.json default record identical; no fm-lab-fm-pane-autoclos sessions remain
Completion and keyed work are rechecked after waiting on the presentation lock (a working, blocker or repeated line appended during the wait keeps the pane) ⏸️ untested no Not driven against real Herdr. It needs an external holder on the firstmate presentation lock and a status append during the wait. The existing test does this with a fake herdr. I did not build a race…
  • bash tests/fm-pane-autoclose-herdr-e2e.test.sh (real Herdr lab: 16 cases, rc=0)
  • bash ~/.no-mistakes/evidence/01M6391HD956G8X2HCJKEGK0H6/extra-lab-driver.sh (one-off real Herdr lab driver: superseded byte endpoint, latest endpoint closes, unkeyed decision delivered, done: without an artifact, secondmate home, absent metadata; rc=0)
  • bash tests/fm-pane-autoclose.test.sh (targeted suite with a fake herdr, which covers the presentation-lock recheck; rc=0)
  • herdr session list --json before and after, diffing the default session's record to check the tripwire
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

dnth added 30 commits October 26, 2026 20:32
Finished brief and scout panes accumulated because pane closure only ran at
teardown, so work that ended in a delivery receipt kept its pane open forever.

deliver now hands its newest covered status endpoint and status identity to
the new bin/fm-pane-autoclose.sh, which owns one gate: the covered line is the
live standing done: claim with the artifact its kind/mode requires, no keyed
decision, blocker, linked external request, routed reply, custom check, public
commitment, or held board row remains, and the recorded endpoint resolves from
ordinary task metadata alone. A failed:, paused:, or unkeyed decision receipt
keeps the pane, and so does a later status event, a rotated status identity,
an already-closed endpoint, or a secondmate home.

The close is teardown's guarded exact-pane path: the named session's
presentation lock, then the focus-preserving pane close that refuses the
captain's active tab and never calls workspace close. It closes the pane view
only: the Herdr session, its other panes, transcripts, and every durable task
record stay intact.

Coverage: tests/fm-pane-autoclose.test.sh pins sixteen gate decisions against a
stateful fake herdr, including a rotated status identity that must not close a
replacement pane; tests/fm-pane-autoclose-herdr-e2e.test.sh drives a real
guarded lab session for close-after-receipt with the board row still In flight,
every keep exclusion, the active-tab refusal, transcript survival, and the
ambiguous-endpoint refusal.
A receipt recorded after teardown removed the task record has no live claim
left to judge, so the walk keeps silently instead of reporting a refusal on a
normal reconciliation path. An unsafe status path still refuses.
Finished brief and scout panes accumulated because pane closure only ran at
teardown, so work that ended in a delivery receipt kept its pane open forever.

deliver now hands its newest covered status endpoint and status identity to
the new bin/fm-pane-autoclose.sh, which owns one gate: the covered line is the
live standing done: claim with the artifact its kind/mode requires, no keyed
decision, blocker, linked external request, routed reply, custom check, public
commitment, or held board row remains, and the recorded endpoint resolves from
ordinary task metadata alone. A failed:, paused:, or unkeyed decision receipt
keeps the pane, and so does a later status event, a rotated status identity,
an already-closed endpoint, or a secondmate home.

The close is teardown's guarded exact-pane path: the named session's
presentation lock, then the focus-preserving pane close that refuses the
captain's active tab and never calls workspace close. It closes the pane view
only: the Herdr session, its other panes, transcripts, and every durable task
record stay intact.

Coverage: tests/fm-pane-autoclose.test.sh pins sixteen gate decisions against a
stateful fake herdr, including a rotated status identity that must not close a
replacement pane; tests/fm-pane-autoclose-herdr-e2e.test.sh drives a real
guarded lab session for close-after-receipt with the board row still In flight,
every keep exclusion, the active-tab refusal, transcript survival, and the
ambiguous-endpoint refusal.
A receipt recorded after teardown removed the task record has no live claim
left to judge, so the walk keeps silently instead of reporting a refusal on a
normal reconciliation path. An unsafe status path still refuses.
The lint gate flagged the review-1 output globals as unused; annotate them the
way the rest of the repo marks sourcing-caller outputs. Add the guarded-lab
case the supervisor required: a promise registered in the primary home for
secondmate:alice keeps the secondmate task's pane, proven against real Herdr
and real tasks-axi.
The suite's setup_home assigns fixture globals in the parent shell while the
backgrounded and substituted helpers only read them, so pin that intent once at
the file head rather than fifteen per-line directives.
…rebase

The pipeline rebased the two task commits onto current main; the remote still
carries their original copies. Merging them keeps every prior commit reachable
without changing the tree.
Its content was revised by the later review rounds, so this merge changes no
files; it only keeps the pre-rebase commit reachable for the private mirror.
* fix(spawn): refuse a fresh brief that duplicates another task's content

A brief filled with another task's stale Task or acceptance text launched a
worker against the wrong work. A fresh ship or scout spawn now compares its
normalized Task section and acceptance-criteria block with live and
recently written briefs in this home and refuses an exact match, naming the
other task. --allow-duplicate-brief <other-task-id> permits deliberate reuse;
--relaunch reuses its own brief and skips the check.

* no-mistakes(review): Forward duplicate brief overrides through batch spawn dispatch

* no-mistakes(document): Clarify duplicate brief comparison help

* no-mistakes(lint): Fix SC2181 status checks in task delivery tests
…vides

The previous assertion required Herdr to retain a closed pane in
session-history.json. Measured on 0.9.3 that is false: the file is written on a
periodic flush, a close leaves the existing text intact, and the next flush
rewrites it with live panes only, so a closed pane leaves the persisted
history.

The test now proves what closure does and does not touch: it waits for Herdr's
own capture of both the target and a sibling pane before the close, asserts
both survive the close immediately, then waits for one flush and asserts the
surviving pane's history persists and the session is still running. It does not
pin when a closed pane's text disappears.
Finished brief and scout panes accumulated because pane closure only ran at
teardown, so work that ended in a delivery receipt kept its pane open forever.

deliver now hands its newest covered status endpoint and status identity to
the new bin/fm-pane-autoclose.sh, which owns one gate: the covered line is the
live standing done: claim with the artifact its kind/mode requires, no keyed
decision, blocker, linked external request, routed reply, custom check, public
commitment, or held board row remains, and the recorded endpoint resolves from
ordinary task metadata alone. A failed:, paused:, or unkeyed decision receipt
keeps the pane, and so does a later status event, a rotated status identity,
an already-closed endpoint, or a secondmate home.

The close is teardown's guarded exact-pane path: the named session's
presentation lock, then the focus-preserving pane close that refuses the
captain's active tab and never calls workspace close. It closes the pane view
only: the Herdr session, its other panes, transcripts, and every durable task
record stay intact.

Coverage: tests/fm-pane-autoclose.test.sh pins sixteen gate decisions against a
stateful fake herdr, including a rotated status identity that must not close a
replacement pane; tests/fm-pane-autoclose-herdr-e2e.test.sh drives a real
guarded lab session for close-after-receipt with the board row still In flight,
every keep exclusion, the active-tab refusal, transcript survival, and the
ambiguous-endpoint refusal.
A receipt recorded after teardown removed the task record has no live claim
left to judge, so the walk keeps silently instead of reporting a refusal on a
normal reconciliation path. An unsafe status path still refuses.
The lint gate flagged the review-1 output globals as unused; annotate them the
way the rest of the repo marks sourcing-caller outputs. Add the guarded-lab
case the supervisor required: a promise registered in the primary home for
secondmate:alice keeps the secondmate task's pane, proven against real Herdr
and real tasks-axi.
The suite's setup_home assigns fixture globals in the parent shell while the
backgrounded and substituted helpers only read them, so pin that intent once at
the file head rather than fifteen per-line directives.
Finished brief and scout panes accumulated because pane closure only ran at
teardown, so work that ended in a delivery receipt kept its pane open forever.

deliver now hands its newest covered status endpoint and status identity to
the new bin/fm-pane-autoclose.sh, which owns one gate: the covered line is the
live standing done: claim with the artifact its kind/mode requires, no keyed
decision, blocker, linked external request, routed reply, custom check, public
commitment, or held board row remains, and the recorded endpoint resolves from
ordinary task metadata alone. A failed:, paused:, or unkeyed decision receipt
keeps the pane, and so does a later status event, a rotated status identity,
an already-closed endpoint, or a secondmate home.

The close is teardown's guarded exact-pane path: the named session's
presentation lock, then the focus-preserving pane close that refuses the
captain's active tab and never calls workspace close. It closes the pane view
only: the Herdr session, its other panes, transcripts, and every durable task
record stay intact.

Coverage: tests/fm-pane-autoclose.test.sh pins sixteen gate decisions against a
stateful fake herdr, including a rotated status identity that must not close a
replacement pane; tests/fm-pane-autoclose-herdr-e2e.test.sh drives a real
guarded lab session for close-after-receipt with the board row still In flight,
every keep exclusion, the active-tab refusal, transcript survival, and the
ambiguous-endpoint refusal.
A receipt recorded after teardown removed the task record has no live claim
left to judge, so the walk keeps silently instead of reporting a refusal on a
normal reconciliation path. An unsafe status path still refuses.
…vides

The previous assertion required Herdr to retain a closed pane in
session-history.json. Measured on 0.9.3 that is false: the file is written on a
periodic flush, a close leaves the existing text intact, and the next flush
rewrites it with live panes only, so a closed pane leaves the persisted
history.

The test now proves what closure does and does not touch: it waits for Herdr's
own capture of both the target and a sibling pane before the close, asserts
both survive the close immediately, then waits for one flush and asserts the
surviving pane's history persists and the session is still running. It does not
pin when a closed pane's text disappears.
dnth added 3 commits October 29, 2026 15:26
The recovered pipeline head carries the shipped inventory fix; merging the last
published head keeps its commits reachable for the private mirror without
changing the validated content.
@dnth dnth changed the title fix: close finished Herdr task panes after delivery feat: close finished Herdr task panes after delivery receipt Oct 29, 2026
@dnth
dnth merged commit 10d601d into main Oct 29, 2026
20 checks passed
@dnth
dnth deleted the fm/fm-pane-autoclose branch October 29, 2026 12:01
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