Skip to content

fix(core): recover parent wakes after subagent report acknowledgement - #63416

Open
jlongster wants to merge 1 commit into
devfrom
parent-notice-recovery
Open

jlongster wants to merge 1 commit into
devfrom
parent-notice-recovery

Conversation

@jlongster

@jlongster jlongster commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Follow-up to #63209.

Type of change

  • Bug fix

What does this PR do?

A crash after acknowledging a child report can leave the parent idle with one pending notice, an empty outbox, and no execution claim. This also occurs if a second crash interrupts startup replay before its deferred wake runs.

Acknowledgement now marks the pending inbox row in the same SQLite transaction that removes the outbox row. Startup can then wake the parent after restart-budget accounting. Promotion or inbox cancellation consumes the marker with the row. Existing rows default to unmarked: their metadata alone cannot distinguish a lost wake from an intentional admit-only notice. No feature flag is needed for this fix.

How did you verify your code works?

The regression cases take a SQLite snapshot immediately after acknowledgement during normal completion and during replay, then boot a fresh core graph from it. Both cases promoted zero notices on 6b8c796. On this branch each promoted the original notification ID once and left an unrelated admit-only session parked.

From packages/core:

bun run script/test.ts test/subagent-idle-e2e.test.ts -t 'recovers the parent wake' --rerun-each 10

Both regressions passed 10 consecutive runs. The subagent, job, and migration files passed (78 tests), as did 24 focused execution tests including user stop, conflicts, and budget exhaustion. The migration check and core TypeScript check also passed.

Screenshots / recordings

Core only.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #36349

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix works and I found nothing blocking. I copied the PR's tests onto the base, where both "recovers the parent wake" cases fail: the parent delivers nothing after the reboot. On this branch they pass, including when repeated. The nearby subagent, execution, job and migration tests pass, and the core tsc build is clean.

The marker is set in the same transaction that removes the outbox row, so a crash can't lose the wake. Suspended sessions are left out of the startup wakes, so budget accounting still runs first. Marks are also cleared when an execution completes and on user stop, so a finished or exhausted session isn't woken again on the next boot. Running user stop inside a database transaction is safe because coordinator.interrupt only forks the fiber interruption.

Public API: unchanged. The only addition is an internal SQLite column, session_inbox.wake_on_restart, whose migration defaults it to false.

Related: #36349

@jlongster

Copy link
Copy Markdown
Contributor Author

Before: the server restarts, but the parent stays idle

flowchart TD
    A["Child finishes its work"] --> B["Server saves the child's report in the parent's inbox"]
    B --> C["Server removes the completed background job record"]
    C -->|Server keeps running| D["Server wakes the parent"]
    D --> E["Parent processes the report"]
    C -->|Server restarts before the wake call| X["❌ Old process exits with the wake still owed"]
    X --> F["New server scans for pending jobs and interrupted sessions"]
    F --> G["The job is gone. The parent was idle and has no execution claim."]
    G --> H["Server is running. Parent stays idle. Report stays in the inbox."]
    style X fill:#ffe3e3,stroke:#c62828,stroke-width:3px,color:#701515
Loading

With this PR: startup can see which parent needs a wake

flowchart TD
    A["Child finishes its work"] --> B["Server saves the child's report in the parent's inbox"]
    B --> C["One SQLite transaction: mark the inbox row wake_on_restart and remove the completed job record"]
    C -->|Server keeps running| D["Server wakes the parent"]
    C -->|Same restart before the wake call| R["New server finds the marked inbox row"]
    R --> W["After accounting for resume attempts, recovery wakes the eligible parent"]
    W --> E["Parent promotes the original report into its conversation"]
    D --> E
    E --> F["Promotion consumes the inbox row and its wake marker"]
    style R fill:#e4f5e8,stroke:#287040,stroke-width:2px,color:#143820
Loading

The regression cases cover a restart at acknowledgment during normal delivery and a second restart during recovery. In both cases the parent receives the original notification ID exactly once; an unrelated idle session stays parked.

User decisions:
- Make parent notifications durable across restart for live completion and startup replay.
- Preserve cancellation, idempotency, conflict handling and restart limits.
- Cover both crash paths with isolated core regression tests.
@jlongster
jlongster force-pushed the parent-notice-recovery branch from 9e0abe2 to 640c535 Compare November 12, 2026 19:13
@jlongster
jlongster enabled auto-merge (squash) November 12, 2026 19:18
@jlongster
jlongster disabled auto-merge November 12, 2026 19:18

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new push is a rebase onto newer dev, and the fix itself is unchanged. The only difference in the PR's diff is one context line: the SubagentMetadata.notify check that #63413 removed upstream. On the new base, both "recovers the parent wake" cases still fail. On this branch they pass, along with the nearby subagent, execution, job and migration tests. The core tsc build is clean, and the new migration is still the latest one.

Public API: unchanged. Nothing new to raise.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant