refactor(core): carry typed job outcomes for stops - #46721
Open
kitlangton wants to merge 3 commits into
Open
kitlangton wants to merge 3 commits into
kitlangton wants to merge 3 commits into
Conversation
kitlangton
force-pushed
the
job-outcomes
branch
2 times, most recently
from
September 2, 2026 03:30
bd60185 to
db02999
Compare
Update the coordinator test to the value-based interruption contract, point the TUI fake servers at POST /api/shell/:id/stop, and include stop in the promise client's shell key list.
Complete accepted shell stops despite caller cancellation, distinguish unavailable results from user stops, and preserve mixed failure causes and legacy completion text. Use canonical Job recovery metadata and a single launch path for new subagents, with regression coverage for each boundary.
kitlangton
force-pushed
the
job-outcomes
branch
from
September 3, 2026 19:16
db02999 to
c40393c
Compare
6 tasks
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Stopping a background shell with Ctrl+D was reported as a failure, waking an idle agent that could then investigate or restart work the user intentionally stopped. Interrupting a foreground subagent likewise collapsed into a generic cancelled tool failure.
This supersedes #46439. Instead of synchronizing a stop reason between Job and its producers, Job carries their typed outcomes. Shell owns process termination; SessionExecution owns the child execution's terminal; Job owns observation and the recoverable notification marker.
What Changes
status: "stopped"and an instruction not to restart without the user's request. Shell output is preserved.resume: false; the idle parent reads it at its next step.!commandunavailable, not an invented user stop, exit code, or completion timestamp.outputtext; do not discard it or infer new outcome fields from rendered text.The
shell.stopendpoint retains the process result and capture;shell.removestill forgets them. The TUI kill action usesshell.stop. Schema, client, and OpenAPI surfaces include the unavailable-result state.Outcome Ownership
Job status describes observation:
completedmeans the producer returned an outcome,errormeans its run failed, andcancelledmeans observation was abandoned. An intentional stop, timeout, or nonzero shell exit belongs in the producer's typedresult, not in a separate Job stop-reason field.SubagentJob.Demo
comparison-outcomes.mp4
Original matched behavior comparison: base
5894e46688on the left, the initial implementation93f0807135on the right. Both use the same deterministicopencode-drivefixture, a realsleep 60process, and real Ctrl+D through the shell picker. Model responses are simulated; playback is normal speed and aligned on the stop. Before:! Shell failedand an unsolicited third model request. After:↳ Shell stopped by userand no further model request.The reviewed implementation (
c40393cedb) was also exercised with a fresh isolated three-flow run: foreground shell, background shell, and user-entered!sleep 60. It finished with four model requests total, two quiet notices pending, and zero running shells. Foreground continuation is expected; background and user-shell stops do not wake the idle parent.Scope
This PR does not introduce a generic Job stop registry or change the web app's killed-command error card. TUI busy detection still recognizes quiet stop notices through their metadata rather than a new durable scheduling flag.
Recovery limit: Session terminal publication and Job outcome persistence are not one atomic commit. A process death after a child records user interruption but before its Job persists that outcome can leave a running background marker that recovery resumes. The quiet-replay guarantee applies to persisted stopped Job outcomes; stronger cross-record reconciliation is not added here.
Verification
Local full suites: 5,139 core passed / 39 skipped; 1,196 TUI passed / 4 skipped; 147 client passed; 54 server passed / 3 skipped. Core, Server, and Simulation builds passed, including their TypeScript compiler checks. The unmodified pre-push hook passed all 33 typecheck tasks.
Regression coverage includes cancelled and overlapping stop callers, normal retention eviction without false user intent, literal pre-outcome shell/subagent markers, interruption combined with drain or settlement defects, canonical metadata when joining/backgrounding an existing Job, initial Job ownership before child execution, explicit continuation, quiet live/recovered notices, and unavailable-result rendering in narrow/light and wide/dark TUIs.