Skip to content

fix(orchestration): deliver the bootstrap prompt to a child created after the deadline (#1370) - #1375

Merged
Juliusolsson05 merged 8 commits into
integration/batch-2026-09-27-pfrom
fix/orchestration-late-bootstrap
Sep 27, 2026
Merged

Juliusolsson05 merged 8 commits into
integration/batch-2026-09-27-pfrom
fix/orchestration-late-bootstrap

Conversation

@Juliusolsson05

@Juliusolsson05 Juliusolsson05 commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Closes #1370.

Problem

orchestration_create_agent waits at most 30 s for the renderer's create-agent answer, and the renderer answers only after the child's provider has started. A healthy but slow start exceeds that: the bundled OpenCode took 16.7, 18.3 and 43.1 s under CPU contention (#1367 review a).

When it does:

  • the caller gets OrchestrationOutcomeUnknownError ("do not repeat");
  • the tool handler returns without delivering the prompt;
  • the late answer is adopted with bootstrapPromptDelivered: false.

The parent ends up with an idle child that never got its brief.

Change (the issue's option a; final head 7956d889)

  • The bridge runs the create's bootstrap delivery when a late answer is adopted.

    • createAgent accepts an onLateCreate continuation, kept main-side (a function cannot cross into the renderer) while the request is outstanding.
    • adoptLateResponse runs it once, with the enriched child. An in-time answer or a failed late answer never runs it.
    • A throw becomes a prompt_delivery_failed incident (create_agent_late_bootstrap), and the adoption incident records bootstrapFollows.
    • Adoption keeps a promptDeliveries record that already exists, so a submission recorded before adoption is not lost.
  • One delivery for both paths. The handler's bootstrap block is one function (deliverBootstrap) that both the punctual and late paths call, so they cannot drift. That block delivers, arms armPromptWhenReady when the composer is not ready, and marks the bootstrap delivered.

  • Bounded retry while the late child is merely not ready (deliverLateBootstrap). The retry delays are 2, 4, 8, 16 and 30 s, about a minute in all. Before every attempt it checks two things:

    • A prompt already landed: if promptSubmissionCount > 0, it stops. A parent that checked and sent the brief itself never gets a second copy.
    • The parent lease: if isParentAttached is false, it stops and records a create_agent_late_bootstrap_parent_gone incident. A parent that closed during a delay must not have its brief start an ownerless child.

    A reservation collision on the late path also counts as "not ready" and is retried. When the retries run out, it records a create_agent_late_bootstrap_never_ready incident.

  • The late readiness waiter is guarded too. SessionManager.deliverPromptWhenReady takes a shouldDeliver() check that runs right before the write. The late path passes the parent-lease check through armPromptWhenReady. A refusal writes nothing and returns do-not-retry.

  • The parent lease follows the alias chain. OrchestrationBridge.isParentAttached resolves the parent through fix(orchestration): closed children follow their parent's replacement #1369's replacement chain (currentParentId), so a parent that was reloaded or replaced still counts as attached.

  • q85 (W2's patch, applied after fix(orchestration): closed children follow their parent's replacement #1369 merged). If marking the late bootstrap delivered fails, an orchestration.bootstrap_mark_failed incident is recorded, because the journal is the late path's only reader. A test covers the replaced-parent case.

  • The timeout reply promises an attempt, not a delivery. When a prompt was given, the outcome-unknown message now says: "If the renderer later confirms the child, Agent Code tries to deliver its bootstrap prompt automatically, retrying for about a minute while it starts. That can fail (the child never answers, is never ready, or you close first), so check orchestration_list_agents and orchestration_read_agent (promptSubmitted) before sending the brief yourself, or it may arrive twice."

  • Rejected: option b (a longer create deadline). MCP clients have their own tool-call timeouts, and a Codex parent's are shorter than 120 s.

  • Plan: docs/plans/2026-09-27-orchestration-late-bootstrap.md.

Tests

  • src/main/orchestration/timeoutOutcome.test.ts:
    • the continuation runs once with the adopted child and is never sent to the renderer;
    • it never runs for a failed late answer or an in-time answer;
    • a throwing continuation becomes an incident;
    • the reply carries the attempt wording.
  • src/mcp/runtime/orchestrationLateBootstrap.system.test.ts (the real MCP server, tool handler and bridge):
    • a create held past the deadline and then answered gets its bootstrap delivered and marked;
    • each retry delay is pinned exactly;
    • the retry stops on an existing submission, on a gone parent and on a replaced parent (through the alias chain);
    • a reservation collision is retried;
    • send_prompt from the parent stops the retry;
    • the mark failure records its incident.
  • src/main/sessionManager.pendingPrompt.test.ts: a refused shouldDeliver writes nothing and returns do-not-retry.
  • Fail-first and mutations: the original tests are red on origin/main, and each review fix was red at the head before it. Killed mutations include:
    • adoption skipping the continuation;
    • the handler not registering it;
    • the waiter guard;
    • a pre-recorded submission being dropped;
    • the alias-chain lookup.
  • Survives (hygiene only, stated): removing the continuation-map cleanup. An in-time create is never adopted, so a stale entry cannot run; it only occupies memory until the bridge is gone.
  • Totals: the touched suites pass (the manager's check: 28/28) and npx tsc -b prints 0 lines. Review rounds 1 and 2 (a/b/c) are fixed, and the manager verified the code MERGE-READY at 7956d889.

🤖 Generated with Claude Code

Juliusolsson05 and others added 2 commits September 26, 2026 23:49
…ion child (#1370)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fter the deadline (#1370)

A healthy provider can take longer than the bridge's 30 s create deadline
to start (the bundled OpenCode took 43.1 s under load). The caller got
"outcome unknown - do not repeat", the tool handler returned without
delivering the prompt, and the late answer was adopted as an idle child
with no brief and nothing telling the parent.

- The tool handler hands the bridge a late-create continuation; the
  bridge keeps it for the outstanding request and runs it from
  adoptLateResponse (never for an in-time or failed answer). A throw is
  recorded as a prompt_delivery_failed incident.
- The handler's bootstrap delivery is one function shared by the
  punctual and late paths (deliver, armPromptWhenReady, mark delivered).
- The outcome-unknown reply says a late child receives its prompt
  automatically and must not be sent it again.

Fail-first: bridge and real-MCP-handler tests are red on origin/main.
Mutations killed: adoption skipping the continuation, the handler not
registering it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05 Juliusolsson05 added type:bug Something works wrong class:C3-silent-failure The app knows it failed and does not say sev:P2 Real bug with a workaround labels Sep 27, 2026
Juliusolsson05 added a commit that referenced this pull request Sep 27, 2026
…ts successor

q85: the mark is the last step of a delivery that can land long after the
create began (a prompt waiting for the composer, or #1370's late
adoption). It was addressed with the parent id the tool call captured;
after a replacement that id has no window and owns no children, so the
mark was lost. Resolved through the alias chain; the combined late-create
+ replacement path was reproduced against #1375 and passes with this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Parked (waiting on #1369). Steering q86: W2's staged patch and the combined #1369→#1375 test are applied here after #1369 merges. #1369 is still open. No work until then.

…dy; skip it when the parent is gone (#1375 review a)

Review a: (1) on a provider with no readiness gate a late delivery refused as
not-ready left the child idle although the parent was told delivery was
automatic; the late path now retries on a bounded backoff (about a minute),
stops as soon as any prompt lands on the child, and records never-ready; the
timeout reply now tells the parent to check orchestration_read_agent before
sending. (2) the late continuation could start the brief after the parent
closed mid-spawn; adoptLateResponse now checks the parent's lease and skips
with an incident. Red at the previous head; the no-landed-check and
no-parent-check mutants are red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05

Juliusolsson05 commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner Author

Review disposition (round 1: a, codex at 2e4bd384: FIX-BEFORE-MERGE)

Fixed at cacd7ec0. Detail is in the plan's review table.

Finding Verdict Disposition
a1, major: a late delivery refused as not-ready (no readiness gate: OpenCode, Grok) left the child idle despite "delivered automatically" valid Fixed: bounded late retries (about a minute), stopping once any prompt has landed on the child, with a never-ready incident. The timeout reply now says to check orchestration_read_agent before sending. Three new system tests, red at the previous head; the no-landed-check mutant is red.
a2, major: the late brief could start after the parent closed mid-spawn valid Fixed: the parent's lease is checked before the late delivery, which is skipped with an incident. The child is left for the user. Red at the previous head; the mutant is red.
a survivor: supersedesPendingPrompt noted a judges false correct; the late race it would decide is covered by the landed-prompt check.

Every orchestration suite passes (bridge 15, runtime 5, timeout 14, bootstrap-pending 20, duplicate-create 16, late-bootstrap 4), and npx tsc -b prints 0 lines. W2's late-warning patch (temp/q85) still applies after #1369 merges.
b (codex at cacd7ec0): FIX-BEFORE-MERGE, fixed at 5f54f8c0:

Finding Verdict Disposition
b1, major: a parent closing during a retry delay still got its brief delivered valid Fixed: isParentAttached is checked before every late attempt, with an incident. Red with the check removed.
b2, minor: the FAILED fixtures lacked type, masking the !response.ok guard valid Fixed in both fixtures; removing the guard is red.
b survivor: removing every lateCreates.delete passed valid Pinned: the map empties in time and late; the mutant is red.

c (codex at cacd7ec0): FIX-BEFORE-MERGE, fixed at ed86637b:

Finding Verdict Disposition
c1, major: a parent closing during a retry delay valid Already fixed in 5f54f8c0 (the same as b1).
c2, minor: the timeout reply overpromised delivery valid Fixed: it says delivery is attempted if the renderer later confirms the child, names how it can fail, and tells the parent how to check before sending.
c survivor: the last retry delay could shrink to 1 ms valid Pinned: every wait is checked exactly; the first-delay and last-delay mutants are red.

Round 1 (a, b, c) is complete.

Round 2 (a, b, c codex at ed86637b): all FIX-BEFORE-MERGE, the final round. Fixed at 24066860.

The round-1 fixes are verified: every targeted mutant is red (the per-attempt parent check, the adoption check, the landed-prompt stop, the late retry, the exact delays).

Finding Verdict Disposition
a + b, major: a late Claude/Codex readiness waiter could deliver after the parent detached valid Fixed: deliverPromptWhenReady takes a shouldDeliver guard, asked when the gate opens, before any write; the late path arms it with the parent-lease check. Red at ed86637b.
c, major: a parent send landing BEFORE the late create answer was erased by adoption, so a second brief followed valid Fixed: adoption keeps an existing record. The test goes through the real send_prompt; red at ed86637b.
c, minor: a reservation collision ended the late loop valid Fixed: it retries, re-checking the landed count and the parent. Red at ed86637b.
b, minor: the stop test bypassed send_prompt's own bookkeeping valid Fixed: it uses the real tool; the bookkeeping mutant is red.

Every orchestration suite passes, sessionManager.pendingPrompt passes 17/17, and npx tsc -b prints 0 lines. ## After #1369 merged (q85 / q86): 7956d889

Every orchestration suite passes, and npx tsc -b prints 0 lines. Both review rounds are spent: NEED-MANAGER (manager-verify a/b/c of 7956d889; the round-2 fixes are in 24066860, and the q85 integration is new since).

Manager verification and READY

B6 verified the code at 7956d889 (manager-verify a/b/c, MERGE-READY): a1/b1 (the shouldDeliver guard before the write), c1, c2, the b2 survivor and q85 are fixed, with 3+ mutants killed; 28/28 pass. The body is rewritten: the removed "delivered automatically: do not send it again" quote is replaced by the attempt wording, and the retry, parent-lease, alias-chain and q85 behaviour are described.

merge-gate.sh agent-code 1375 --member --dry: GATE PASS (7956d889, 0 behind main, checks green). READY.

Juliusolsson05 and others added 5 commits September 27, 2026 10:54
…ttempt; pin the late-answer guard and continuation cleanup (#1375 review b)

Review b: the parent was checked once at adoption, so a parent that closed
during a retry delay still had its brief delivered; OrchestrationBridge now
exposes isParentAttached and the late loop checks it before every attempt.
The FAILED late-answer fixtures lacked type, masking the !response.ok guard;
and removing every lateCreates.delete survived. All three mutants are red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…very; pin every late retry delay (#1375 review c)

Review c: its major (a parent closing during a retry) was already fixed in
5f54f8c. The reply now says delivery is attempted only if the renderer
later confirms the child, and names how to check before sending; each late
backoff wait is pinned exactly, so shrinking any delay is red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…; keep a submission recorded before adoption; retry after a reservation collision (#1375 review round 2)

Round 2: (a, b) a late Claude/Codex child's readiness waiter could deliver
after the parent detached; deliverPromptWhenReady takes a shouldDeliver guard
asked when the gate opens, and the late path passes the parent-lease check.
(c) the renderer files the child before its create answer reaches main, so a
parent's own send could land first and adoption reset the record, causing a
second brief; adoption now keeps an existing record. (c) a reservation
collision ended the late loop; it now retries. (b) the stop test goes
through the real orchestration_send_prompt. All red at ed86637; the
send_prompt bookkeeping mutant is red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e-bootstrap

# Conflicts:
#	src/main/orchestration/OrchestrationBridge.ts
…esolve the parent lease through its alias chain

W2's patch (temp/q85): orchestration.bootstrap_mark_failed on the late path
only, and the combined late-create + parent-replacement case. isParentAttached
now resolves the parent through #1369's currentParentId, so a parent replaced
during the late window keeps its brief flowing to the successor; only a
parent with no live successor is gone. Removing the alias resolution fails
the replaced-parent test. Orchestration suites green; tsc 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Juliusolsson05
Juliusolsson05 changed the base branch from main to integration/batch-2026-09-27-p September 27, 2026 20:35
@Juliusolsson05
Juliusolsson05 merged commit 2c41ab6 into integration/batch-2026-09-27-p Sep 27, 2026
2 checks passed
@Juliusolsson05
Juliusolsson05 deleted the fix/orchestration-late-bootstrap branch September 27, 2026 20:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

class:C3-silent-failure The app knows it failed and does not say sev:P2 Real bug with a workaround type:bug Something works wrong

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(orchestration): a child whose provider starts in >30 s is adopted without its bootstrap prompt

1 participant