picocolors emits ANSI escape codes when FORCE_COLOR is set (GitHub
Actions default), so the literal toContain('status: active') assertion
fails against the wrapped 'status: \x1b[36mactive\x1b[39m' output.
Reuse the existing stripAnsi helper from src/cli/doctor/format-shared.ts
in the test before assertion.
Reproduced locally with FORCE_COLOR=1 bun test src/cli/boulder/formatter.test.ts.
Inserts blocking Oracle verification todos (plan-1b / plan-2b /
plan-6b in the canonical, plan-1b / plan-2b / plan-5b in the gpt and
gemini variants) between each major Prometheus phase. Each gate is a
single task(subagent_type=oracle) invocation that must return
VERDICT: GO; NO-GO is a directive to fix the cited issues and rerun on
the same Oracle session, not a license to skip.
Adds a new 'Oracle Verification (Phase Gates)' section to
plan-generation.ts with the concrete invocation prompts for each gate:
phase 1 verifies interview completeness, phase 2 verifies the generated
plan, phase 3 verifies plan readiness for execution before /start-work
handoff.
Also adds a plan-generation.test.ts smoke suite (9 cases) that pins
the new todo ids, the section name, the GO/NO-GO format, the
'fix the cited issues' fallback, and the relative ordering.
Drops 'Maximum 3 retries' / 'document and move on' across every Atlas
variant (default, opus-4-7, gpt, kimi, gemini). New text forbids the
'false positive' excuse explicitly and instructs Atlas to keep iterating
on the same task_id, attaching a diagnosis plan, until verification
passes — and to spawn a different-angle subagent only when the original
loops.
Adds a shared <boulder_completion_response> section composed by
shared-prompt.ts. When the hook injects the BOULDER COMPLETE nudge,
Atlas now knows to print TOTAL ELAPSED + per-task elapsed times in the
exact summary shape, confirm boulder.json state, and only mark
pass-final-wave after the Final Wave reviewers approve.
Three correctness fixes on top of the dispatch-before-commit invariant:
- ralph-loop-event-handler.ts: after idleSettleMs, also require state ownership and non-verification-pending to match the event source before dispatching. Applied to both the session.idle and session.error retry paths.
- verification-failure-handler.ts: if incrementIteration fails after a successful continuation injection, clear the loop state and emit a warning toast instead of returning success.
- session-reset-strategy.ts: catch thrown session.create errors so they route through the typed session_creation_rejected path instead of surfacing as an unhandled rejection.
Three additional invariant tests addressing the gaps surfaced by Cubic and the post-implementation review:
- idle path must not dispatch when state ownership changes during the idleSettleMs window
- verification-failure path must treat incrementIteration failure as a loud failure, not a success
- reset strategy must surface session.create rejections as session_creation_rejected even when the SDK throws instead of returning an error envelope
Split the verification-failure restart into clearVerificationState (clears\nthe verification flags so we cleanly transition back to the main loop)\nfollowed by injectContinuationPrompt, with incrementIteration only on\nsuccessful injection. On rejection: clear the loop state and emit a loud\nwarning toast. Mirrors the dispatch-before-commit contract enforced for\nthe idle and session.error paths.
Reorder the session.idle and session.error retry paths so the durable\niteration counter and the progress toast advance only when continueIteration\nreturns dispatched. On dispatch_rejected or session_creation_rejected,\nclear the loop state and emit a loud failure toast instead of silently\nlogging while the loop appears to make progress.\n\nAdds an explicit settle-window state check so a session.deleted firing\nduring the idleSettleMs sleep no longer feeds dispatch against a cleared\nloop. Keeps idleSettleMs intact for the original idle-settle race.
Replace silent returns in continueIteration with a discriminated union\n(dispatched | session_creation_rejected | dispatch_rejected). Wraps\ninjectContinuationPrompt in try/catch so reset-strategy createIterationSession\nreturning null and promptAsync rejections both surface as typed failures\nthe caller can react to.
Lock the contract that durable iteration state and visible UI must only\nadvance when the continuation dispatch is semantically accepted. Adds 4\npermanent invariant tests covering the idle, session.error retry, and\nverification-failure orchestration paths, plus the reset-strategy\nsilent-null path.
Extract isCmuxCompatEnvironment to shared module and fix
interactive_bash to resolve cmux executables with the __tmux-compat
prefix, matching all other tmux command paths in the codebase.
After SIGTERM/SIGKILL escalation, the stdout stream may not close immediately on all platforms. The unconditional await on outputPromise could then hang indefinitely, defeating the bounded process lifetime guarantee. Race outputPromise against a short follow-up timeout to ensure getOpenCodeVersion always returns within a bounded time.
Refs #3766