~bigbes/agents-dev · parade

main · last commit 13 days ago · 7g0stsfu

← Back to the parade

ah-1cx.11 [bug] createChildTasks swallows RecordChild failures, orphaning a child from the join Past Stand

status: closed P3 bug milestone:v0.2.0
bd reopen ah-1cx.11
Created byEugene Blikh
Ownerbigbes@gmail.com
Created2026-08-04T23:43:10Z
Started2026-08-04T23:54:11Z
Updated2026-08-05T00:23:05Z
Closed2026-08-05T00:23:05Z
Description
Found while fixing ah-1cx.2.

createChildTasks logs and continues when RecordChild fails (internal/reconcile/tools.go:376). A child created on the board whose lineage row is lost is invisible to both ChildIDsOf and the parent's join: the parent parks in Blocked waiting on a set that does not include it, so the child can complete without ever unblocking the parent — and a human sees a blocked card with no visible reason.

It also weakens the before-count guard ah-1cx.2 added, which distinguishes a real pending join from fire-and-forget children by comparing the parent's child count before and after delegation.
Acceptance criteria
A RecordChild failure either aborts the delegation loudly (card held, comment, audit event) or is retried durably. Either way the parent never parks on an incomplete child set. Covered by a test.

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

Done in 8716a2a. Confirmed as filed: createChildTasks logged and continued past a RecordChild failure, so a child could exist on the board with no task_parents row — invisible to ChildIDsOf, to the Blocked join, and to the per-parent lifetime cap — leaving the parent parked on a strictly smaller set than it created and undercounting ah-1cx.2's before/after guard. Took the DURABLE-RETRY option rather than a plain abort, because an abort that holds the card would hold it forever. createChildTasks now stops at the FIRST lost lineage row (creating more children a broken store cannot record only widens the hole) and returns the orphaned ids alongside created. delegateAndBlock refuses to park while any id is orphaned: it records them in the park marker's payload (armed:<before>,<id>,<id> — the no-orphan form is unchanged so older markers decode), holds the card in In Progress, and posts a comment naming every created card plus the untracked ones, stamped alarmed so a persistent fault does not spam. reparkDelegatedParent retries the lineage write before anything counts children (RecordChild is idempotent; the generation is re-derived from GenerationOf(parent)+1 rather than stored), then parks normally and records a delegation_lineage_recovered event. ToolTaskCreate reports the same condition as a 500 naming the card id — a fire-and-forget child deadlocks nobody, but a silent success would still corrupt the cap and any later join. Cover: TestDelegationLineageFailure, TestToolTaskCreateLineageFailureIsReported. FOLLOW-UP: nothing reconciles board reality against task_parents, so a child card created by hand or a lineage row whose card was deleted is still undetectable — a periodic lineage audit would close that.
  • Eugene Blikh added under epic ah-1cx · 2026-08-05T02:43:10Z
  • Eugene Blikh created the issue · 2026-08-05T02:43:10Z
  • Eugene Blikh changed status to in_progress · 2026-08-05T02:54:11Z
  • Eugene Blikh added label milestone:v0.2.0 · 2026-08-05T03:10:43Z
  • Eugene Blikh closed the issue · 2026-08-05T03:23:05Z
    Done in 8716a2a. Confirmed as filed: createChildTasks logged and continued past a RecordChild failure, so a child could exist on the board with no task_parents row — invisible to ChildIDsOf, to the Blocked join, and to the per-parent lifetime cap — leaving the parent parked on a strictly smaller set than it created and undercounting ah-1cx.2's before/after guard. Took the DURABLE-RETRY option rather than a plain abort, because an abort that holds the card would hold it forever. createChildTasks now stops at the FIRST lost lineage row (creating more children a broken store cannot record only widens the hole) and returns the orphaned ids alongside created. delegateAndBlock refuses to park while any id is orphaned: it records them in the park marker's payload (armed:<before>,<id>,<id> — the no-orphan form is unchanged so older markers decode), holds the card in In Progress, and posts a comment naming every created card plus the untracked ones, stamped alarmed so a persistent fault does not spam. reparkDelegatedParent retries the lineage write before anything counts children (RecordChild is idempotent; the generation is re-derived from GenerationOf(parent)+1 rather than stored), then parks normally and records a delegation_lineage_recovered event. ToolTaskCreate reports the same condition as a 500 naming the card id — a fire-and-forget child deadlocks nobody, but a silent success would still corrupt the cap and any later join. Cover: TestDelegationLineageFailure, TestToolTaskCreateLineageFailureIsReported. FOLLOW-UP: nothing reconciles board reality against task_parents, so a child card created by hand or a lineage row whose card was deleted is still undetectable — a periodic lineage audit would close that.
Stored rows — what this pane was built from, as read
issues 1 row
id ah-1cx.11
content_hash 39a21e11c8e682fefa03a720759e6bc1b9c78a3b57baf86938f388843b7556e3
title [bug] createChildTasks swallows RecordChild failures, orphaning a child from the join
description Found while fixing ah-1cx.2. createChildTasks logs and continues when RecordChild fails (internal/reconcile/tools.go:376). A child created on the board whose lineage row is lost is invisible to both ChildIDsOf and the parent's join: the parent parks in Blocked waiting on a set that does not include it, so the child can complete without ever unblocking the parent — and a human sees a blocked card with no visible reason. It also weakens the before-count guard ah-1cx.2 added, which distinguishes a real pending join from fire-and-forget children by comparing the parent's child count before and after delegation.
design
acceptance_criteria A RecordChild failure either aborts the delegation loudly (card held, comment, audit event) or is retried durably. Either way the parent never parks on an incomplete child set. Covered by a test.
notes
status closed
priority 3
issue_type bug
assignee NULL
estimated_minutes NULL
created_at 2026-08-04T23:43:10Z
created_by Eugene Blikh
owner bigbes@gmail.com
updated_at 2026-08-05T00:23:05Z
closed_at 2026-08-05T00:23:05Z
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 Done in 8716a2a. Confirmed as filed: createChildTasks logged and continued past a RecordChild failure, so a child could exist on the board with no task_parents row — invisible to ChildIDsOf, to the Blocked join, and to the per-parent lifetime cap — leaving the parent parked on a strictly smaller set than it created and undercounting ah-1cx.2's before/after guard. Took the DURABLE-RETRY option rather than a plain abort, because an abort that holds the card would hold it forever. createChildTasks now stops at the FIRST lost lineage row (creating more children a broken store cannot record only widens the hole) and returns the orphaned ids alongside created. delegateAndBlock refuses to park while any id is orphaned: it records them in the park marker's payload (armed:<before>,<id>,<id> — the no-orphan form is unchanged so older markers decode), holds the card in In Progress, and posts a comment naming every created card plus the untracked ones, stamped alarmed so a persistent fault does not spam. reparkDelegatedParent retries the lineage write before anything counts children (RecordChild is idempotent; the generation is re-derived from GenerationOf(parent)+1 rather than stored), then parks normally and records a delegation_lineage_recovered event. ToolTaskCreate reports the same condition as a 500 naming the card id — a fire-and-forget child deadlocks nobody, but a silent success would still corrupt the cap and any later join. Cover: TestDelegationLineageFailure, TestToolTaskCreateLineageFailureIsReported. FOLLOW-UP: nothing reconciles board reality against task_parents, so a child card created by hand or a lineage row whose card was deleted is still undetectable — a periodic lineage audit would close that.
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:54:11Z
is_blocked 0
labels 1 row
issue_id ah-1cx.11
label milestone:v0.2.0
dependencies 1 row
id 2bb7505d-7292-5fd9-999b-5e8616f32fd8
issue_id ah-1cx.11
type parent-child
created_at 2026-08-05T02:43:10Z
created_by Eugene Blikh
metadata �{}
thread_id
depends_on_issue_id ah-1cx
depends_on_wisp_id NULL
depends_on_external NULL
events 4 rows
id 019fcf28-df25-7a9e-b4b7-d7ca76c37a41
issue_id ah-1cx.11
event_type created
actor Eugene Blikh
old_value
new_value
comment NULL
created_at 2026-08-05T02:43:10Z
id 019fcf32-f633-76ab-94a3-e781f2fb1972
issue_id ah-1cx.11
event_type status_changed
actor Eugene Blikh
old_value {"id":"ah-1cx.11","title":"[bug] createChildTasks swallows RecordChild failures, orphaning a child from the join","description":"Found while fixing ah-1cx.2.\n\ncreateChildTasks logs and continues when RecordChild fails (internal/reconcile/tools.go:376). A child created on the board whose lineage row is lost is invisible to both ChildIDsOf and the parent's join: the parent parks in Blocked waiting on a set that does not include it, so the child can complete without ever unblocking the parent — and a human sees a blocked card with no visible reason.\n\nIt also weakens the before-count guard ah-1cx.2 added, which distinguishes a real pending join from fire-and-forget children by comparing the parent's child count before and after delegation.","acceptance_criteria":"A RecordChild failure either aborts the delegation loudly (card held, comment, audit event) or is retried durably. Either way the parent never parks on an incomplete child set. Covered by a test.","status":"open","priority":3,"issue_type":"bug","owner":"bigbes@gmail.com","created_at":"2026-08-04T23:43:10Z","created_by":"Eugene Blikh","updated_at":"2026-08-04T23:43:10Z"}
new_value {"status":"in_progress"}
comment NULL
created_at 2026-08-05T02:54:11Z
id 019fcf42-1930-73ad-8d9b-b7f5ce1cc570
issue_id ah-1cx.11
event_type label_added
actor Eugene Blikh
old_value NULL
new_value NULL
comment Added label: milestone:v0.2.0
created_at 2026-08-05T03:10:43Z
id 019fcf4d-6b0c-7226-84bf-6954423c12ef
issue_id ah-1cx.11
event_type closed
actor Eugene Blikh
old_value
new_value Done in 8716a2a. Confirmed as filed: createChildTasks logged and continued past a RecordChild failure, so a child could exist on the board with no task_parents row — invisible to ChildIDsOf, to the Blocked join, and to the per-parent lifetime cap — leaving the parent parked on a strictly smaller set than it created and undercounting ah-1cx.2's before/after guard. Took the DURABLE-RETRY option rather than a plain abort, because an abort that holds the card would hold it forever. createChildTasks now stops at the FIRST lost lineage row (creating more children a broken store cannot record only widens the hole) and returns the orphaned ids alongside created. delegateAndBlock refuses to park while any id is orphaned: it records them in the park marker's payload (armed:<before>,<id>,<id> — the no-orphan form is unchanged so older markers decode), holds the card in In Progress, and posts a comment naming every created card plus the untracked ones, stamped alarmed so a persistent fault does not spam. reparkDelegatedParent retries the lineage write before anything counts children (RecordChild is idempotent; the generation is re-derived from GenerationOf(parent)+1 rather than stored), then parks normally and records a delegation_lineage_recovered event. ToolTaskCreate reports the same condition as a 500 naming the card id — a fire-and-forget child deadlocks nobody, but a silent success would still corrupt the cap and any later join. Cover: TestDelegationLineageFailure, TestToolTaskCreateLineageFailureIsReported. FOLLOW-UP: nothing reconciles board reality against task_parents, so a child card created by hand or a lineage row whose card was deleted is still undetectable — a periodic lineage audit would close that.
comment NULL
created_at 2026-08-05T03:23:05Z