Repository navigation
fix(bin): release live captain-held work on a keyed answer instead of closing it - #83
Merged
Merged
Conversation
…ing it A keyed answer with no close mode (or done) completed a captain-held work item while its worker was still running. The answers intake now releases any task a worker still owns (live runtime record or In flight row), records the captain's words, and keeps a late replay a release.
…ing it A keyed answer with no close mode (or done) completed a captain-held work item while its worker was still running. The answers intake now releases any task a worker still owns (live runtime record or In flight row), records the captain's words, and keeps a late replay a release.
…serve release replay
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
I would like all bugs to be fixed so tomorrow can be focused entirely to Vernant and not problems preventning Vernant from getting built.
A recommended architecture should come with proof.
Context (the backlog record for this bug, as filed):
A board answer on a captain-held work item closes the item while its worker is still running (repo: firstmate) (kind: ship) (priority: 2) (since 2026-10-08)
Observed 2026-10-08: board batch lavish-82c7cf68f2a2600b.110 answered decisions held on the live work items fm-lifecycle-phase3, fm-jev-retire-invented, fm-supervision-watchdog-outside-session and fm-task-copy-reassociate; each was closed to Done (fm-captain-hold answer's question-shaped close) while its worker was still validating; retention then pruned fm-task-copy-reassociate entirely. Main reopened three and re-created one. Policy (captain-hold-lifecycle skill): a hold on the work item a question gates must release the item to proceed, never record completion before landing. Fix the answers intake so a keyed answer on an in-flight or spawned work item releases it (records the words) instead of closing it; behavioral test with a live task meta. mode=no-mistakes yolo=off.
What Changed
bin/fm-captain-hold.sh:answersnow sends the empty anddonemodes through a new internalanswer --auto-releasepath. Under the task control lock, that path releases the hold (and records the captain's words) when the task is In flight or still has a$STATE/<id>.metaruntime record, and completes the task only when no worker owns it. Resolution records now carry aResolves hold set:line. An interrupted release can then finish safely after teardown, a re-held task gets its own new record, and a legacy release with no hold association is refused for automatic completion.holdnow takes the task control lock before it picks its stamp, and it refuses aFM_CAPTAIN_HOLD_NOWvalue that collides with the newest resolved hold. Keyed replays re-check the task under the lock and skip a snapshot that a concurrent hold changed.bin/fm-backlog-transition-lib.sh: close-marker replay now selects the transition from the current row. Amode=retainrecord whose hold was released before replay closes the finished row instead of reopening unheld work, and it drops only a known-unsupported relocated report artifact.tests/fm-captain-hold-lifecycle.test.shadds behavioral regressions, includingtest_keyed_answer_releases_a_live_work_itemwith a live task meta, plus cases for cleanup ordering, interrupted and completed release replay, a concurrent hold, a repeated answer on a re-held task, and the legacy refusal.docs/captain-hold-lifecycle.mdanddocs/verification/process-event-sources.mddescribe automatic resolution and point to the script help as the owner of keyed syntax and replay rules.Risk Assessment
Testing
I drove the real firstmate commands (fm-captain-hold.sh, fm-teardown.sh, fm-procevent.sh, tasks-axi) in a marked throwaway lab home with plain FM_HOME. Only the external tools tmux, treehouse, gh, gh-axi, no-mistakes and herdr were stubs, so the real fleet was safe. Eight live scenarios passed. Six were on the fixed code. Two reproduced the bug on the base commit. On base commit 924f479, the same batch closed all live work items to Done. That is the reported bug. Three race and crash scenarios need fault injection, so they are marked untested. The existing targeted tests for them passed, but those tests do not count as live runs. I did not start a Claude primary or a Herdr session, because this change is a command-line change with no agent or screen surface. There is no screenshot for the same reason; the evidence is command transcripts. One behavior did not change, by owner decision: a direct
answerwithout--releasestill closes the item. I removed both labs and the base copy. The worktree is clean.fm-captain-hold.sh answersin a lab home; default anddonemodes)git archive, run in a second lab home)fm-procevent.sh register,bind,start; answers-fed; task in_flight, released). The board source was a script that prints a poll result; no real Lavish server was used.fm-teardown.shin the lab; state done, record still released, exit 0 on both replays)FM_CAPTAIN_HOLD_NOW collides ...exit 1; resolution records now: 2; record 1 text UNCHANGED)releasemode releases, an unknown mode is skipped, the reserved valuereconcileis refused, and held items stay held when skippedEvidence: S1 fixed code: board batch releases live work
Source: S1 fixed code: board batch releases live work
$ fm-captain-hold.sh answers --source "lab board batch" < batch.tsv closed: lab-live-scout closed: lab-live-ship closed: lab-row-only closed: lab-plain-question answers: closed=4 skipped=0 [exit 0] --- task lab-live-scout --- state: in_flight held: no Resolution mode: released Answer: go-b / Option B worker record: PRESENT --- task lab-live-ship --- state: queued held: no Resolution mode: released worker record: PRESENT --- task lab-row-only --- state: in_flight held: no Resolution mode: released worker record: absent --- task lab-plain-question --- state: done held: no Resolution mode: answeredEvidence: S1 base commit 924f479: same batch closes live work (the bug)
Source: S1 base commit 924f479c: same batch closes live work (the bug)
--- task lab-live-scout --- state: done held: no Resolution mode: answered worker record: PRESENT --- task lab-live-ship --- state: done held: no Resolution mode: answered worker record: PRESENT --- task lab-row-only --- state: done held: no Resolution mode: answered --- task lab-plain-question --- state: done held: no Resolution mode: answeredEvidence: S2 fixed code: cleanup completes released work; replays are safe
Source: S2 fixed code: cleanup completes released work; replays are safe
Evidence: S3 fixed code: late answer on finished held work closes it with its report
Source: S3 fixed code: late answer on finished held work closes it with its report
Evidence: S4 fixed code: same-second re-hold refused; repeated answer writes record 2
Source: S4 fixed code: same-second re-hold refused; repeated answer writes record 2
(pinned FM_CAPTAIN_HOLD_NOW=2026-07-14T12:00:00Z) $ cap hold lab-rehold --reason second question fm-captain-hold: FM_CAPTAIN_HOLD_NOW collides with the newest resolved hold for task lab-rehold; choose a later timestamp [exit 1] ... resolution records now: 2 record 1 text: UNCHANGED inside the new bodyEvidence: S5 fixed code: board result through fm-procevent releases live work
Source: S5 fixed code: board result through fm-procevent releases live work
$ pe start lavish-b0a4d0000000f1e2 reconciles-fed: lavish-b0a4d0000000f1e2 answers-fed: lavish-b0a4d0000000f1e2 --- task lab-board-work --- state: in_flight held: no Resolution mode: released Captain answered this call through the captured result lavish-b0a4d0000000f1e2 sequence 1. Answer: north worker record: PRESENTEvidence: S5 base commit 924f479: board result closes live work (the bug)
Source: S5 base commit 924f479c: board result closes live work (the bug)
--- task lab-board-work --- state: done held: no Resolution mode: answered worker record: PRESENTEvidence: S6 fixed code: explicit release, unknown mode, reconcile, direct answer
Source: S6 fixed code: explicit release, unknown mode, reconcile, direct answer
closed: lab-m-release skipped: lab-m-bogus (unknown close mode bogus) refused: lab-m-reconcile (reconcile requests require a bound captured source) answers: closed=1 skipped=2 [exit 1]Evidence: Targeted keyed-answer tests (race and crash cases)
Source: Targeted keyed-answer tests (race and crash cases)
Evidence: Lab driver script used for all live scenarios
Source: Lab driver script used for all live scenarios
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ No issues found.
✅ **Test** - passed
✅ No issues found.
fm-captain-hold.sh answersin a lab home; default anddonemodes)git archive, run in a second lab home)fm-procevent.sh register,bind,start; answers-fed; task in_flight, released). The board source was a script that prints a poll result; no real Lavish server was used.fm-teardown.shin the lab; state done, record still released, exit 0 on both replays)FM_CAPTAIN_HOLD_NOW collides ...exit 1; resolution records now: 2; record 1 text UNCHANGED)releasemode releases, an unknown mode is skipped, the reserved valuereconcileis refused, and held items stay held when skippedbin/fm-lab-home.sh create "$LAB"to make a marked throwaway lab home (two labs: one for the fixed code, one for the base commit)drive-lab.sh <worktree> $LAB s1: realbin/fm-captain-hold.sh holdx4, thenbin/fm-captain-hold.sh answers --source "lab board batch"with one 4-row keyed batch, thentasks-axi show --fullon each itemdrive-lab.sh <worktree> $LAB s2: replay of the same answer while the worker runs,fm-captain-hold.sh complete --none, realbin/fm-teardown.sh lab-live-scout, replay again after cleanupdrive-lab.sh <worktree> $LAB s3: hold finished work, realbin/fm-teardown.sh, then a late keyed answerdrive-lab.sh <worktree> $LAB s4: release, same-second re-hold withFM_CAPTAIN_HOLD_NOWpinned, later re-hold, identical answer againdrive-lab.sh <worktree> $LAB s5:bin/fm-procevent.sh register lavish,fm-captain-hold.sh bind,bin/fm-procevent.sh startwith a board result in the Lavish poll shape for a live work itemdrive-lab.sh <worktree> $LAB s6:answerswith modesrelease,bogus, and the reserved valuereconcile; directfm-captain-hold.sh answerwithout--releasegit archive 924f479c bin .tasks.tomlinto a temp directory, thendrive-lab.sh <base> $LAB2 s1ands5to reproduce the bug on the base commitbash tests/fm-captain-hold-lifecycle.test.sh test_keyed_answer_releases_a_live_work_item test_keyed_answer_waits_for_cleanup_before_selecting_its_mode test_interrupted_keyed_release_closes_after_teardown test_completed_keyed_release_replays_after_publication_failure test_stale_keyed_replay_preserves_a_concurrent_hold test_repeated_keyed_answer_resolves_its_own_hold test_legacy_keyed_release_requires_explicit_closure test_bound_channel_answers_close_at_answer_time test_board_answer_reaches_the_keyed_answer_intake test_relocated_report_does_not_wedge_released_cleanup_replay test_answer_before_cleanup_replay_preserves_the_retained_report(12 ok lines, 0 failures)rm -rfof both lab homes and the base copy;git status --shortshows a clean worktree✅ **Document** - passed
✅ No issues found.
🔧 **Lint** - 1 issue found → auto-fixed ✅
🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Push** - passed
✅ No issues found.