fix(hold): project hold state onto task status; alive-by-task reconciliation; dashboard telemetry by lane snapshot (#651) - #659
Merged
Conversation
…econciliation; dashboard telemetry follows the lane snapshot (#651) Field report penster 20260911T234647: after a post-hold-timeout resume released TP-2104's hold, batch-state kept it 'held' (stale hold-timeout exitReason) while the not-yet-started successor TP-2105 showed 'running' and the dashboard rendered TP-2104's live telemetry on TP-2105's row. 1. Release projection: createHoldStore's persist callback now receives the transitioned record; engine and resume call projectHoldTransitionOntoOutcomes() before the strict write, so open → 'held' and released → 'running' (stale hold reason cleared, endTime null) land in the same durable write. Terminal outcomes are never downgraded. 2. Successor: resume reconciliation builds aliveTaskIds from registry manifests and decides 'session alive' per TASK when manifests carry task ids — two tasks on one serial lane share the lane sessionName, so lane liveness must not promote a successor to reconnect/running. Legacy registries without task ids keep session semantics. 3. Dashboard: worker-stats block attaches lane telemetry only when the lane snapshot's taskId names the row (held rows included), mirroring the reviewer sub-row guard; a sibling badged 'running' can no longer borrow it. Tests: +4 (projection matrix incl. idempotency and terminal guard; store callback wiring; alive-by-task vs legacy reconcile; dashboard guard). 4122.
… is transactional with the hold write (#651, Sage review) - A unit with two open holds stayed 'running' after only one ruling: the projection now derives status from ALL holds binding the task after the transition (any open → held; none open + released-unacknowledged → running), not from the single transitioned record. - A failed release persist rolled back the hold table but left the outcome projected as running: projection now happens inside createHoldStore's commit and is restored together with the table when the write throws. Callers pass { outcomes: () => <live array> } instead of projecting themselves. Idempotent on identical status+reason. Tests: two-holds-one-ruled, failed-persist rollback + later checkpoint.
HenryLach
added a commit
that referenced
this pull request
Sep 16, 2026
chore: remove stray patch script committed with #659
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.
Closes #651 (penster field report 20260911T234647, #627 item A).
Symptom: after a post-
hold-timeoutresume released TP-2104's hold,batch-state.jsonkept itheldwith the stale hold-timeout reason while the not-yet-started successor TP-2105 showedrunning; the dashboard rendered TP-2104's live telemetry (271 tool calls, $46) on TP-2105's row.Three writers fixed:
createHoldStore(owner, persist, { outcomes })now projects the bound task's status from the post-transition hold table inside the same strict write (any open hold →held; none open + released-unacknowledged →running, stale reason cleared), and rolls the projection back with the table if the write fails. Terminal outcomes never downgraded.sessionNamestayspending. Legacy registries without task ids keep session semantics.Sage blockers-only review: 2 blockers in round 1 (partial release with a second open hold projected
running; projection not rolled back on persist failure) → fixed in780a2839→ sign-off. Suite 4122/0, lint 283, typecheck/format clean.