Skip to content

fix(broker): deliver spawn brief only after real prompt readiness - #1470

Merged
khaliqgant merged 9 commits into
mainfrom
fix/1469-spawn-brief-delivery
Aug 9, 2026
Merged

fix(broker): deliver spawn brief only after real prompt readiness#1470
khaliqgant merged 9 commits into
mainfrom
fix/1469-spawn-brief-delivery

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Aug 9, 2026

Copy link
Copy Markdown
Member

Fixes #1469.

What changed

  • remove the PTY worker's 25-second false-readiness fallback
  • retain the initial spawn task until the harness exposes a proven input prompt
  • report child-process liveness separately from input readiness, so a live slow or unrecognized prompt is not reaped at the startup deadline
  • scope every worker event to a unique wrapper generation so late same-name events cannot mutate a replacement worker
  • accept Codex's directory-trust interstitial before readiness, using the rendered terminal grid because Codex's differential TUI output does not preserve the full prompt in the raw byte stream
  • keep the 25-second threshold as a one-shot diagnostic
  • upgrade the Fleet E2E from registration/heartbeat evidence to an observable nonce action performed by the PTY child
  • run five consecutive spawn assertions across two nodes, with node-a delaying readiness to 27 seconds and discarding all pre-ready input

Root cause

The PTY wrapper emitted worker_ready after 25 seconds even when prompt detection had not succeeded. The broker then removed the sole queued initial task and wrote it to a still-booting TUI, where it could be consumed without action. The agent remained registered and heartbeating, so the previous E2E passed despite the lost brief.

The corrected flow emits harness_started with the child pid as soon as the PTY child is known, then emits worker_ready only after actual harness readiness. Liveness prevents a healthy slow or unrecognized prompt from being reaped, but only worker_ready releases the initial task. A slow harness gets one warning while its queued work remains intact. Worker events carry a unique wrapper-generation UUID and are ignored unless that generation is still current, preventing delayed frames from an older same-name process from affecting the replacement.

Real-harness validation also exposed Codex 0.144.5's directory-trust menu as a pre-prompt startup gate. The existing Claude and Gemini trust responders had no Codex equivalent. The final implementation detects the complete Codex menu in either accumulated raw output or the rendered terminal grid, accepts the pre-selected Yes, continue once, and still waits for the true input prompt before releasing the brief.

Regression and negative control

The unchanged final regression test was run against unmodified origin/main (7a42f3bd):

  • exit 1 after 47.60s
  • failure: waitFor timed out (worker-a acted on nonce-bearing brief); last=null

Against this branch:

  • focused five-spawn regression: exit 0, 5/5 acted, 89.07s
  • complete Fleet matrix: exit 0, 14/14 passed, 125.08s

Timestamped isolated E2E observations from a five-spawn run (no follow-up delivery of any kind):

Agent Node Nonce First observation (UTC)
worker-a node-a spawn-brief-1-mslmhzxf 2026-08-09T09:52:03.690Z
worker-b1 node-b spawn-brief-2-mslmil1m 2026-08-09T09:52:04.282Z
worker-a2 node-a spawn-brief-3-mslmilov 2026-08-09T09:52:31.899Z
worker-b2 node-b spawn-brief-4-mslmj6ta 2026-08-09T09:52:32.483Z
worker-a3 node-a spawn-brief-5-mslmj75n 2026-08-09T09:52:59.723Z

The approximately 27-second spacing across all three node-a observations independently crosses the historical 25-second race boundary.

Real product validation

A second real broker was built from this branch, started on its own port, state directory, and throwaway workspace, and read back live through /health before spawning:

  • node: relay-1469-live-final
  • live version: 1469-fixed-8e6ca134
  • port: 58149
  • workspace: 212177675260125184
  • real CLIs: Claude Code 2.1.153, Codex 0.144.5

Five consecutive real PTY spawns acted on their nonce-bearing initial briefs with zero follow-up input, DM, or message:

Agent Harness Nonce First observation (UTC)
sha-codex-1 Codex finalsha-1469-codex-1-8e6ca134 2026-08-09T11:59:18Z
sha-claude-2 Claude finalsha-1469-claude-2-8e6ca134 2026-08-09T12:00:46Z
sha-codex-3 Codex finalsha-1469-codex-3-8e6ca134 2026-08-09T12:01:27Z
sha-claude-4 Claude finalsha-1469-claude-4-8e6ca134 2026-08-09T12:02:45Z
sha-codex-5 Codex finalsha-1469-codex-5-8e6ca134 2026-08-09T12:03:24Z

The first real Codex attempt before the trust-menu fix was an explicit failed control: no nonce after 180 seconds (observer exit 1), and a read-only PTY snapshot showed it parked on Do you trust the contents of this directory?. A raw-stream-only responder also failed because Codex draws the menu with cursor motion; that observation led to the terminal-grid check in 32229bed. The final five-spawn run started from zero after rebuilding and restarting only this isolated broker.

Other validation

  • cargo test -p relay-pty: exit 0 (222 passed; doc test ignored)
  • cargo test -p agent-relay-broker: exit 0 (864 passed, 4 ignored; integration tests also passed)
  • cargo clippy -p agent-relay-broker -p relay-pty --lib -- -D warnings: exit 0
  • npm run typecheck: exit 0
  • Rustfmt, Prettier, git diff --check, and staged secret-pattern scan: exit 0

All validation used isolated E2E processes and ports. The resident broker at pid 99572 was not changed, restarted, or reconfigured. The isolated real-harness agents were released and their broker stopped after evidence capture.

Related injectionMode report

This is separate from the readiness race. The current MCP send_dm schema declares mode: "wait" | "steer" and forwards that field correctly. A camelCase injectionMode input is not declared, so schema parsing strips it and the request defaults to wait; the returned record therefore appears to silently ignore the caller. No compatibility-alias change is included here so the spawn-readiness fix stays isolated.

@cursor

cursor Bot commented Aug 9, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The broker preserves queued PTY work until a verified prompt is available. It emits harness_started for process liveness and worker_ready only after prompt readiness. Worker generations reject stale events. Fleet tests cover five delayed cross-node spawns.

Changes

PTY readiness and spawn validation

Layer / File(s) Summary
Broker readiness and liveness flow
crates/broker/src/pty_worker.rs, crates/broker/src/worker.rs
The broker tracks readiness, warns once during delayed startup, emits harness_started, and emits worker_ready only after prompt confirmation.
Worker generation event filtering
crates/broker/src/worker.rs, crates/broker/src/runtime/worker_events.rs, crates/broker/src/runtime/tests.rs
Worker messages carry generation UUIDs. Runtime handling validates generations and PTY PIDs, records liveness, and keeps liveness separate from readiness.
Delayed PTY harness behavior
tests/e2e/fleet/nodes/stub-agent.cjs, tests/e2e/fleet/nodes/node-a.ts, tests/e2e/fleet/nodes/node-b.ts
The test harness delays readiness, processes nonce-bearing input after readiness, and records node-specific observations.
Cross-node spawn validation
tests/e2e/fleet/fleet-e2e.test.ts, tests/e2e/fleet/README.md, CHANGELOG.md
The E2E scenario validates five targeted spawns, routing, completion, nonce observations, metadata, and heartbeats.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FleetE2E
  participant PTYWorker
  participant RuntimeEvents
  participant StubAgent
  participant ObservationFile

  FleetE2E->>PTYWorker: Spawn agent with nonce-bearing brief
  PTYWorker->>StubAgent: Start PTY and retain queued brief
  PTYWorker->>RuntimeEvents: Emit harness_started with child PID
  RuntimeEvents->>RuntimeEvents: Record PTY liveness
  StubAgent-->>PTYWorker: Emit ->pty:ready after delayed startup
  PTYWorker->>RuntimeEvents: Emit worker_ready
  PTYWorker->>StubAgent: Deliver queued brief
  StubAgent->>ObservationFile: Record nonce and node metadata
  FleetE2E->>ObservationFile: Validate observation
Loading

Possibly related PRs

Suggested reviewers: willwashburn

Poem

A rabbit waits beside the queue,
Until the PTY prompt rings true.
Liveness speaks; stale frames flee.
Five nonce notes confirm delivery.
Then queued briefs hop into flight.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR fixes spawn-brief delivery and adds liveness reporting, but it does not implement lifecycle states or launch-failure signaling required by issue #1469. Implement lifecycle states and launch-failure signaling, or split those requirements into a separate issue and link this PR only to the spawn-delivery scope.
Docstring Coverage ⚠️ Warning Docstring coverage is 61.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The code, changelog, and E2E changes support the spawn-readiness fix, liveness reporting, stale-event handling, and regression coverage.
Title check ✅ Passed The title clearly identifies the broker fix that delays spawn brief delivery until real PTY prompt readiness.
Description check ✅ Passed The description thoroughly explains the change, root cause, testing, validation results, and scope, although it uses headings different from the repository template.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1469-spawn-brief-delivery

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/e2e/fleet/README.md`:
- Line 24: Update the E2E duration documentation in the matrix timing entry near
the “spawn completes E2E” scenario to reflect that the five serial spawns
require at least about 81 seconds before overhead, replacing the outdated
approximately 30-second estimate.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: db2bab2b-a9a8-4346-8b93-599825e460d5

📥 Commits

Reviewing files that changed from the base of the PR and between 7a42f3b and 7ca5e2d.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • crates/broker/src/pty_worker.rs
  • crates/broker/src/worker.rs
  • tests/e2e/fleet/README.md
  • tests/e2e/fleet/fleet-e2e.test.ts
  • tests/e2e/fleet/nodes/node-a.ts
  • tests/e2e/fleet/nodes/node-b.ts
  • tests/e2e/fleet/nodes/stub-agent.cjs

Comment thread tests/e2e/fleet/README.md

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 potential issue.

Open in Devin Review

Comment thread crates/broker/src/pty_worker.rs

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 8 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread tests/e2e/fleet/fleet-e2e.test.ts Outdated
Comment thread tests/e2e/fleet/nodes/stub-agent.cjs Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/broker/src/runtime/worker_events.rs`:
- Around line 37-43: Update protocol_pid to reject pid values of zero before
returning the converted PID, while continuing to accept valid nonzero u32
values. Add or update the protocol PID tests to cover zero and ensure malformed
zero-PID frames do not suppress the never-ready deadline.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 62e31292-954f-4e97-8878-777c4a72ca6d

📥 Commits

Reviewing files that changed from the base of the PR and between 7ca5e2d and fc80ee7.

📒 Files selected for processing (4)
  • crates/broker/src/pty_worker.rs
  • crates/broker/src/runtime/worker_events.rs
  • crates/broker/src/worker.rs
  • tests/e2e/fleet/README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/e2e/fleet/README.md
  • crates/broker/src/worker.rs

Comment thread crates/broker/src/runtime/worker_events.rs

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 6 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread tests/e2e/fleet/nodes/stub-agent.cjs Outdated

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 6 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread crates/broker/src/runtime/worker_events.rs

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 4 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread crates/broker/src/wrap.rs
@khaliqgant
khaliqgant merged commit d4dc459 into main Aug 9, 2026
39 checks passed
@khaliqgant
khaliqgant deleted the fix/1469-spawn-brief-delivery branch August 9, 2026 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Spawned agents produce nothing and the control plane cannot say whether they ran

1 participant