Skip to content

fix(hold): project hold state onto task status; alive-by-task reconciliation; dashboard telemetry by lane snapshot (#651) - #659

Merged
HenryLach merged 2 commits into
mainfrom
fix/hold-status-projection
Sep 16, 2026
Merged

HenryLach merged 2 commits into
mainfrom
fix/hold-status-projection

Conversation

@HenryLach

Copy link
Copy Markdown
Owner

Closes #651 (penster field report 20260911T234647, #627 item A).

Symptom: after a post-hold-timeout resume released TP-2104's hold, batch-state.json kept it held with the stale hold-timeout reason while the not-yet-started successor TP-2105 showed running; the dashboard rendered TP-2104's live telemetry (271 tool calls, $46) on TP-2105's row.

Three writers fixed:

  1. Status projection in the hold writecreateHoldStore(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.
  2. Alive-by-task reconciliation — resume decides "session alive" per task from the registry manifests' task ids; a successor sharing the lane's sessionName stays pending. Legacy registries without task ids keep session semantics.
  3. Dashboard — worker telemetry attaches only to the task the lane snapshot names (held rows included), mirroring the reviewer sub-row guard.

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 in 780a2839 → sign-off. Suite 4122/0, lint 283, typecheck/format clean.

…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
HenryLach merged commit 204feb7 into main Sep 16, 2026
1 check passed
@HenryLach
HenryLach deleted the fix/hold-status-projection branch September 16, 2026 03:57
HenryLach added a commit that referenced this pull request Sep 16, 2026
chore: remove stray patch script committed with #659
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant