fix(session): drop the clarification option from the run-mode nudge and apply the completion contract to every agent in run mode
Issue for this PR
Closes #1640
Type of change
- Bug fix
- New feature
- Refactor / code improvement
- Documentation
What does this PR do?
Headless altimate-code run runs could end on a question to the user, who isn't there to answer. In an eight-pass headless evaluation, all six turn-ending questions came right after the post-compaction nudge, whose option (2) was "stop and ask for clarification". This PR fixes that and a second gap that let the same thing happen.
1. The run-mode nudge no longer offers a clarification stop (session/termination.ts).
COMPLETION_NUDGE is sent only when ALTIMATE_RUN_MODE is on (compaction.ts already branches on the flag; interactive compaction uses its own continuation string). It now reads:
Context was compacted; the summary above is the record of the work so far. No one can answer questions during this run, so do not end your turn with a question. Choose exactly one: (1) if concrete next steps remain toward the original task, continue with them; if you are unsure how to proceed, pick the most reasonable interpretation, state your assumption, and continue; (2) if the deliverable is complete and verified, reply with DONE alone on the final line and stop.
The interactive continuation ("Continue if you have next steps, or stop and ask for clarification if you are unsure how to proceed.") is not touched; it is now pinned byte-for-byte by tests.
2. Every agent gets the completion contract in run mode (session/termination.ts, session/prompt.ts).
completionInstruction() was gated by COMPLETION_CONTRACT_AGENTS = {"builder"}, so custom agents and subagents started by the task tool ran without the "end with DONE" contract. The allowlist and the agent input are removed; the function now depends only on run mode. Run mode is the existing Flag.ALTIMATE_RUN_MODE signal (set by run via cli/cmd/run/run-mode.ts); no new global. Task subagents are prompted through the same SessionPrompt loop in the same process, so they pick it up with no change to tool/task.ts. Interactive sessions still get no contract for any agent.
Behaviour change to review: in run mode, the subagent's final text now ends with a DONE line, and that text is what the task tool returns inside <task_result>. The parent's completion detection only reads the parent's own text parts, so this does not end the parent run, but the parent model will see the token in the tool result.
Not in this PR: a "blocked: report and stop" option for genuinely blocked runs (left out on purpose; the eval showed the escape option was the problem), and stripping DONE from task results.
How did you verify your code works?
termination.test.ts: the run-mode nudge has two options, no "clarification", says not to end on a question, and still instructsDONE;completionInstruction({ runMode: true })returns the contract and{ runMode: false }returns nothing; a source guard checks thatprompt.tspasses only the run-mode flag and that the agent allowlist is gone; the interactive continuation string incompaction.tsis pinned byte-for-byte.compaction-summarizer-integrity.test.ts: a run-mode compaction injects the new nudge with no "clarification"; an interactive compaction (ALTIMATE_RUN_MODE=0) injects the unchanged interactive string, checked byte-for-byte.- New loop-level test in
prompt.test.ts: a customreviewersubagent (configured as a subagent, started from a subtask part and run through thetasktool) is driven against the mock LLM server. In run mode every request, the child's and the parent's, carries the contract in its system messages; withALTIMATE_RUN_MODE=0none does. Neither mode's requests contain "stop and ask for clarification". The test does not force a compaction inside the subagent; the compaction nudge itself is covered at the session level by the integrity test above. - The run-mode tests fail against
main(checked by reverting the source files) and pass on this branch; the interactive pins pass on both. bun test(packages/opencode): 20,098 pass, 14 fail, 1 error between tests. All are unrelated: the 7mcp.headers/mcp HttpApistatus failures fail identically onmain; the 3--time-budgetcases injson-output.test.tsare the known timing-sensitive ones;budget-finalization,session-diff-missing-patch(2),v2 pty HttpApiand the between-tests error (database is lockedin session-diff) are load failures that pass when run alone.- Typecheck clean;
script/check-tracker-leaks.tsandanalyze.ts --markers --strictpass.
No real-gateway run; mock server only.
Cost side effects
Small increase per run-mode request for agents other than builder: the completion contract adds one fixed sentence (about 50 tokens) to the system prompt of custom agents and subagents. It is constant for the session, so it is cached with the rest of the prefix. Interactive sessions are unchanged. Runs that would have ended on an unanswerable question should now finish, which is the expected direction for cost per completed task.
Screenshots / recordings
N/A
Checklist
- I have tested my changes locally
- I have not included unrelated changes in this PR
- I checked for cost side effects (model calls, queries or cloud resources that grow with user data) and described them, or wrote "none"
š¤ Generated with Claude Code
Summary by cubic
Fixes headless altimate-code run sessions ending on a question nobody can answer, and makes the run-mode completion contract (DONE) apply to every agent instead of just builder.
The post-compaction nudge no longer offers a "stop and ask for clarification" option; it now instructs agents to pick the most reasonable interpretation and continue, or reply DONE. The completion contract is injected for every agent in run mode ā custom agents and task subagents included ā while interactive sessions still receive nothing. Behavior change to review: in run mode a subagent's final text now ends with a DONE line, which appears inside the <task_result> the parent sees; parent completion detection only reads the parent's own text parts, so a parent run still ends normally.