~bigbes/agents-dev · parade

main · last commit 13 days ago · 7g0stsfu

← Back to the parade

ah-1cx.2 Delegation persist failure orphans children: parent re-finalizes to In Review, never Blocked Past Stand

status: closed P3 bug milestone:v0.1.0
bd reopen ah-1cx.2
Created byEugene Blikh
Ownerbigbes@gmail.com
Created2026-07-19T23:37:32Z
Started2026-08-04T23:32:43Z
Updated2026-08-04T23:41:33Z
Closed2026-08-04T23:41:33Z
Description
When delegateAndBlock has already created child cards and recorded lineage but its UpsertTask(Blocked) fails (reconcile.go:921-930), the stored record stays in_progress while the run row is finished. Next tick adoptOrFail takes the interrupted-finalize path and re-finalizes with firstFinalize=false, so delegation is skipped and the card lands In Review — the children run to completion but the parent never joins or resumes to integrate them. The code comment claims the heal path converges to Blocked; it does not (only the sibling MoveToBucket-failure case converges via alignCardToRecord). Fix: re-attempt the Blocked persist (retry signal) instead of letting a finished-row re-finalize strand the children. Rare trigger (store write failure at exactly that step); confirmed by reading, core-runtime audit 2026-07-20.

Depends on

  • ah-1cx — Hardening: post-audit bug sweep (2026-07-20) parent-child open

Depended on by

Nothing depends on this issue.

No comments.

Close reason

Fixed in 67dcf3c. Confirmed by REPRODUCTION, not just reading: finalize computes firstFinalize from run.State != finished and the delegation branch was gated on it; when delegateAndBlock's UpsertTask(Blocked) failed, the run row was already finished (UpdateRun runs first) while the task record stayed in_progress, so the next tick's adoptOrFail interrupted-finalize path re-entered finalize with firstFinalize=false, skipped delegation entirely, and routed the parent to In Review behind live children. The old comment claiming the heal path converges to Blocked was WRONG — alignCardToRecord only covers the sibling MoveToBucket-failure case, where the record is already Blocked. Fix: delegateAndBlock arms a durable intent marker in the existing kv store (delegation_park/<task>/<attempt>, value armed:<n>/alarmed:<n> where n is the parent's child count BEFORE this delegation) ahead of createChildTasks, so the only write that can fail before any child exists is the marker itself, and that failure cancels the delegation outright instead of half-landing it. The park tail is extracted into an idempotent parkParentBlocked used by both the first finalization and a new refinalize-only reparkDelegatedParent, which re-attempts the flip from the marker and NEVER calls createChildTasks — so a heal cannot become a duplicate-child bug. The before-count is what distinguishes a real pending join from fire-and-forget children created by the mid-run ToolTaskCreate channel and from an armed-but-created-nothing delegation; both disarm and finalize to In Review as before. Failure is now loud and recoverable: error log, delegation_park_failed audit event, and a once-per-attempt card comment (guarded by the marker's alarmed flag so a broken store cannot spam the card), with the card held in In Progress so the loop retries each tick. Run-row immutability untouched. Cover: TestDelegationParkPersistFailure (4 subtests) + TestDelegationParkGuards (4 subtests).
  • Eugene Blikh added under epic ah-1cx · 2026-07-20T02:37:31Z
  • Eugene Blikh created the issue · 2026-07-20T02:37:31Z
  • Eugene Blikh added label milestone:hardening · 2026-07-20T03:08:20Z
  • Eugene Blikh removed label milestone:hardening · 2026-07-20T03:10:15Z
  • Eugene Blikh added label milestone:v0.1.0 · 2026-07-20T03:11:24Z
  • Eugene Blikh changed status to in_progress · 2026-08-05T02:32:43Z
  • Eugene Blikh closed the issue · 2026-08-05T02:41:33Z
    Fixed in 67dcf3c. Confirmed by REPRODUCTION, not just reading: finalize computes firstFinalize from run.State != finished and the delegation branch was gated on it; when delegateAndBlock's UpsertTask(Blocked) failed, the run row was already finished (UpdateRun runs first) while the task record stayed in_progress, so the next tick's adoptOrFail interrupted-finalize path re-entered finalize with firstFinalize=false, skipped delegation entirely, and routed the parent to In Review behind live children. The old comment claiming the heal path converges to Blocked was WRONG — alignCardToRecord only covers the sibling MoveToBucket-failure case, where the record is already Blocked. Fix: delegateAndBlock arms a durable intent marker in the existing kv store (delegation_park/<task>/<attempt>, value armed:<n>/alarmed:<n> where n is the parent's child count BEFORE this delegation) ahead of createChildTasks, so the only write that can fail before any child exists is the marker itself, and that failure cancels the delegation outright instead of half-landing it. The park tail is extracted into an idempotent parkParentBlocked used by both the first finalization and a new refinalize-only reparkDelegatedParent, which re-attempts the flip from the marker and NEVER calls createChildTasks — so a heal cannot become a duplicate-child bug. The before-count is what distinguishes a real pending join from fire-and-forget children created by the mid-run ToolTaskCreate channel and from an armed-but-created-nothing delegation; both disarm and finalize to In Review as before. Failure is now loud and recoverable: error log, delegation_park_failed audit event, and a once-per-attempt card comment (guarded by the marker's alarmed flag so a broken store cannot spam the card), with the card held in In Progress so the loop retries each tick. Run-row immutability untouched. Cover: TestDelegationParkPersistFailure (4 subtests) + TestDelegationParkGuards (4 subtests).
Stored rows — what this pane was built from, as read
issues 1 row
id ah-1cx.2
content_hash b631a20368ede9c76697a930cc68dfee0415035e0dd41ffb9b8b01666ce3ce31
title Delegation persist failure orphans children: parent re-finalizes to In Review, never Blocked
description When delegateAndBlock has already created child cards and recorded lineage but its UpsertTask(Blocked) fails (reconcile.go:921-930), the stored record stays in_progress while the run row is finished. Next tick adoptOrFail takes the interrupted-finalize path and re-finalizes with firstFinalize=false, so delegation is skipped and the card lands In Review — the children run to completion but the parent never joins or resumes to integrate them. The code comment claims the heal path converges to Blocked; it does not (only the sibling MoveToBucket-failure case converges via alignCardToRecord). Fix: re-attempt the Blocked persist (retry signal) instead of letting a finished-row re-finalize strand the children. Rare trigger (store write failure at exactly that step); confirmed by reading, core-runtime audit 2026-07-20.
design
acceptance_criteria
notes
status closed
priority 3
issue_type bug
assignee NULL
estimated_minutes NULL
created_at 2026-07-19T23:37:32Z
created_by Eugene Blikh
owner bigbes@gmail.com
updated_at 2026-08-04T23:41:33Z
closed_at 2026-08-04T23:41:33Z
closed_by_session
external_ref NULL
spec_id
compaction_level 0
compacted_at NULL
compacted_at_commit NULL
original_size NULL
sender
ephemeral 0
wisp_type
pinned 0
is_template 0
mol_type
work_type
source_system
metadata �{}
source_repo
close_reason Fixed in 67dcf3c. Confirmed by REPRODUCTION, not just reading: finalize computes firstFinalize from run.State != finished and the delegation branch was gated on it; when delegateAndBlock's UpsertTask(Blocked) failed, the run row was already finished (UpdateRun runs first) while the task record stayed in_progress, so the next tick's adoptOrFail interrupted-finalize path re-entered finalize with firstFinalize=false, skipped delegation entirely, and routed the parent to In Review behind live children. The old comment claiming the heal path converges to Blocked was WRONG — alignCardToRecord only covers the sibling MoveToBucket-failure case, where the record is already Blocked. Fix: delegateAndBlock arms a durable intent marker in the existing kv store (delegation_park/<task>/<attempt>, value armed:<n>/alarmed:<n> where n is the parent's child count BEFORE this delegation) ahead of createChildTasks, so the only write that can fail before any child exists is the marker itself, and that failure cancels the delegation outright instead of half-landing it. The park tail is extracted into an idempotent parkParentBlocked used by both the first finalization and a new refinalize-only reparkDelegatedParent, which re-attempts the flip from the marker and NEVER calls createChildTasks — so a heal cannot become a duplicate-child bug. The before-count is what distinguishes a real pending join from fire-and-forget children created by the mid-run ToolTaskCreate channel and from an armed-but-created-nothing delegation; both disarm and finalize to In Review as before. Failure is now loud and recoverable: error log, delegation_park_failed audit event, and a once-per-attempt card comment (guarded by the marker's alarmed flag so a broken store cannot spam the card), with the card held in In Progress so the loop retries each tick. Run-row immutability untouched. Cover: TestDelegationParkPersistFailure (4 subtests) + TestDelegationParkGuards (4 subtests).
event_kind
actor
target
payload
await_type
await_id
timeout_ns 0
waiters
hook_bead
role_bead
agent_state
last_activity NULL
role_type
rig
due_at NULL
defer_until NULL
no_history 0
started_at 2026-08-04T23:32:43Z
is_blocked 0
labels 1 row
issue_id ah-1cx.2
label milestone:v0.1.0
dependencies 1 row
id ade21fac-04c6-5276-aded-7071f2917cc2
issue_id ah-1cx.2
type parent-child
created_at 2026-07-20T02:37:31Z
created_by Eugene Blikh
metadata �{}
thread_id
depends_on_issue_id ah-1cx
depends_on_wisp_id NULL
depends_on_external NULL
events 6 rows
id 019f7cbd-f533-72d6-8f31-87f8a7699fbb
issue_id ah-1cx.2
event_type created
actor Eugene Blikh
old_value
new_value
comment NULL
created_at 2026-07-20T02:37:31Z
id 019f7cda-2a1c-7ad5-b06b-ba03c76c502d
issue_id ah-1cx.2
event_type label_added
actor Eugene Blikh
old_value NULL
new_value NULL
comment Added label: milestone:hardening
created_at 2026-07-20T03:08:20Z
id 019f7cdb-ebea-70d2-8fe2-43814c575bd6
issue_id ah-1cx.2
event_type label_removed
actor Eugene Blikh
old_value NULL
new_value NULL
comment Removed label: milestone:hardening
created_at 2026-07-20T03:10:15Z
id 019f7cdc-fae8-7d5d-aa9d-8cc0e5893f79
issue_id ah-1cx.2
event_type label_added
actor Eugene Blikh
old_value NULL
new_value NULL
comment Added label: milestone:v0.1.0
created_at 2026-07-20T03:11:24Z
id 019fcf1f-4f41-7897-a3e3-d9700e0fabb2
issue_id ah-1cx.2
event_type status_changed
actor Eugene Blikh
old_value {"id":"ah-1cx.2","title":"Delegation persist failure orphans children: parent re-finalizes to In Review, never Blocked","description":"When delegateAndBlock has already created child cards and recorded lineage but its UpsertTask(Blocked) fails (reconcile.go:921-930), the stored record stays in_progress while the run row is finished. Next tick adoptOrFail takes the interrupted-finalize path and re-finalizes with firstFinalize=false, so delegation is skipped and the card lands In Review — the children run to completion but the parent never joins or resumes to integrate them. The code comment claims the heal path converges to Blocked; it does not (only the sibling MoveToBucket-failure case converges via alignCardToRecord). Fix: re-attempt the Blocked persist (retry signal) instead of letting a finished-row re-finalize strand the children. Rare trigger (store write failure at exactly that step); confirmed by reading, core-runtime audit 2026-07-20.","status":"open","priority":3,"issue_type":"bug","owner":"bigbes@gmail.com","created_at":"2026-07-19T23:37:32Z","created_by":"Eugene Blikh","updated_at":"2026-07-19T23:37:32Z","labels":["milestone:v0.1.0"]}
new_value {"status":"in_progress"}
comment NULL
created_at 2026-08-05T02:32:43Z
id 019fcf27-643e-782d-a052-a620b8e39e4c
issue_id ah-1cx.2
event_type closed
actor Eugene Blikh
old_value
new_value Fixed in 67dcf3c. Confirmed by REPRODUCTION, not just reading: finalize computes firstFinalize from run.State != finished and the delegation branch was gated on it; when delegateAndBlock's UpsertTask(Blocked) failed, the run row was already finished (UpdateRun runs first) while the task record stayed in_progress, so the next tick's adoptOrFail interrupted-finalize path re-entered finalize with firstFinalize=false, skipped delegation entirely, and routed the parent to In Review behind live children. The old comment claiming the heal path converges to Blocked was WRONG — alignCardToRecord only covers the sibling MoveToBucket-failure case, where the record is already Blocked. Fix: delegateAndBlock arms a durable intent marker in the existing kv store (delegation_park/<task>/<attempt>, value armed:<n>/alarmed:<n> where n is the parent's child count BEFORE this delegation) ahead of createChildTasks, so the only write that can fail before any child exists is the marker itself, and that failure cancels the delegation outright instead of half-landing it. The park tail is extracted into an idempotent parkParentBlocked used by both the first finalization and a new refinalize-only reparkDelegatedParent, which re-attempts the flip from the marker and NEVER calls createChildTasks — so a heal cannot become a duplicate-child bug. The before-count is what distinguishes a real pending join from fire-and-forget children created by the mid-run ToolTaskCreate channel and from an armed-but-created-nothing delegation; both disarm and finalize to In Review as before. Failure is now loud and recoverable: error log, delegation_park_failed audit event, and a once-per-attempt card comment (guarded by the marker's alarmed flag so a broken store cannot spam the card), with the card held in In Progress so the loop retries each tick. Run-row immutability untouched. Cover: TestDelegationParkPersistFailure (4 subtests) + TestDelegationParkGuards (4 subtests).
comment NULL
created_at 2026-08-05T02:41:33Z