Handle OpenCode session events that carry the session ID under properties.info.id or properties.info.sessionID so background tasks and continuation hooks do not miss idle/error/delete events.
Add regression coverage for nested session.idle events completing background tasks and waking continuation hooks.
Oracle review of PR #3943 surfaced that endTaskTimer never fires for real Prometheus plans because their canonical path is .sisyphus/plans/ and the snapshot capture was nested inside the !isSisyphusPath branch intended for direct-work warning suppression. Move the snapshot/path tracking out of the warning gate so all plan-file edits are snapshotted regardless of .sisyphus prefix. Keep the warning branch isSisyphus-gated so Atlas does not yell at legitimate plan edits.
Regression test now uses a real .sisyphus/plans/ path and fails against HEAD before the fix.
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.