Give /codex:rescue a wait contract that does not depend on a background-subagent re-invocation - #106
Conversation
…nd-subagent re-invocation `rescue.md` promised that "Claude Code re-invokes you when a background subagent finishes", while `status.md` records that exact re-invocation as observed not to arrive for subagent callers. These are executable instructions, so `/codex:rescue --background` ended its turn waiting on a callback the project had already documented as unreliable, with no recovery step — a completed Codex run could sit unread with no signal. `--background` now backgrounds the Codex run rather than the subagent: the subagent is always invoked in the foreground and enqueues the tracked job with `task --background --json`, and the command blocks on it in bounded foreground steps, then presents the persisted result in the same turn. That is the same contract #94 gave the review flows, and it removes the failure mode rather than documenting it. `task --background` already supported this; no runtime changes were needed. Also corrects `rescue.md`'s claim that there is "nothing to fetch afterwards" — a rescue's `task` run persists a tracked job record either way — and the README paragraph describing the old shape. Closes #99
|
Deep review round 1/3 —
The finding is real but pre-existing and repo-wide, not introduced here. Filed as #107 to fix uniformly across all four commands. Not addressed in this PR. No other findings. Full suite green at 347/347. |
Closes #99
Problem
plugins/codex/commands/rescue.mdstated as a guarantee:plugins/codex/commands/status.mdrecords the opposite from observation — that re-invocation "has been observed not to arrive for subagent callers, leaving a completed Codex result unread with no signal." These are executable instructions, not prose, so/codex:rescue --backgroundwas told to end its turn and wait for a callback the project had already documented as unreliable for exactly this caller shape, with no recovery step.README.mdwas corrected in #97;rescue.mdstill promised it.Fix — option 2 from the issue, not option 1
The issue offered documenting the recovery path (cheap) or giving rescue the same wait contract as the reviews (the real fix, "if the subagent boundary allows it"). It does:
handleTask'sif (options.background)branch incodex-companion.mjsalready enqueues viaenqueueBackgroundJoband returns the same{ jobId, workspaceRoot, ... }payload the review flows consume. No runtime changes were needed.codex:codex-rescuesubagent is now always invoked in the foreground. A foregroundAgentcall returns inside the current turn — the same property Give the background Codex flows a wait contract that does not depend on harness re-invocation #94 relied on for foregroundBash. Backgrounding the subagent is what created the failure mode, so it is gone.--backgroundnow backgrounds the Codex run: the subagent enqueues withtask --background --jsonand returns the enqueue JSON; the command readsjobId/workspaceRoot, blocks in bounded foreground steps (status … --wait --timeout-ms 240000 --json, PID-aware re-arm), then presents.storedJob.renderedverbatim in the same turn.--wait/ no flag: unchanged foreground behavior.rescue.md's "There is no job to poll and nothing to fetch afterwards" is corrected — a rescue'staskrun persists a tracked job record either way.Changed:
commands/rescue.md,agents/codex-rescue.md,skills/codex-cli-runtime/SKILL.md,README.md, plus the version bump to 1.0.43.Verification
The regression test was written first and observed failing against
main(1 fail / 31 pass), then made to pass:tests/commands.test.mjs— new testrescue never depends on a background-subagent re-invocation to present its resultpins both halves: the stale claims are gone, and the bounded-wait/fetch contract is present inrescue.md.commands/rescue.mdis now covered by the two existing cross-command contract tests — "the foreground wait mechanism is provisioned on every command that requires it" and "dispatch is never a stopping point across every async surface" — so rescue is held to the same wait contract as the review flows rather than to rescue-only assertions.Notes
plugins/codex/scripts/**;status.mdwas left alone as the accurate side of the contradiction.🤖 Generated with Claude Code