@bastani/atomic 0.9.6 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +12 -0
  3. package/dist/builtin/cursor/package.json +2 -2
  4. package/dist/builtin/intercom/CHANGELOG.md +20 -0
  5. package/dist/builtin/intercom/README.md +1 -1
  6. package/dist/builtin/intercom/contact-supervisor-tool.ts +19 -32
  7. package/dist/builtin/intercom/index-heavy.ts +8 -51
  8. package/dist/builtin/intercom/index.ts +13 -1
  9. package/dist/builtin/intercom/intercom-tool.ts +24 -23
  10. package/dist/builtin/intercom/package.json +1 -1
  11. package/dist/builtin/intercom/reply-waiter.ts +115 -0
  12. package/dist/builtin/intercom/skills/intercom/SKILL.md +9 -2
  13. package/dist/builtin/intercom/subagent-relay.ts +11 -1
  14. package/dist/builtin/mcp/CHANGELOG.md +12 -0
  15. package/dist/builtin/mcp/package.json +1 -1
  16. package/dist/builtin/subagents/CHANGELOG.md +12 -0
  17. package/dist/builtin/subagents/package.json +1 -1
  18. package/dist/builtin/subagents/skills/subagent/SKILL.md +3 -1
  19. package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +3 -2
  20. package/dist/builtin/web-access/CHANGELOG.md +12 -0
  21. package/dist/builtin/web-access/package.json +1 -1
  22. package/dist/builtin/workflows/CHANGELOG.md +40 -0
  23. package/dist/builtin/workflows/README.md +5 -5
  24. package/dist/builtin/workflows/builtin/goal-artifacts.ts +17 -4
  25. package/dist/builtin/workflows/builtin/goal-prompts.ts +32 -22
  26. package/dist/builtin/workflows/builtin/goal-reducer.ts +29 -5
  27. package/dist/builtin/workflows/builtin/goal-review.ts +6 -11
  28. package/dist/builtin/workflows/builtin/goal-runner.ts +10 -11
  29. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +2 -2
  30. package/dist/builtin/workflows/builtin/ralph-core.ts +5 -54
  31. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +103 -0
  32. package/dist/builtin/workflows/builtin/ralph-models.ts +10 -10
  33. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +27 -24
  34. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +16 -9
  35. package/dist/builtin/workflows/builtin/ralph-runner.ts +47 -21
  36. package/dist/builtin/workflows/builtin/review-convergence.ts +118 -0
  37. package/dist/builtin/workflows/builtin/shared-prompts.ts +40 -0
  38. package/dist/builtin/workflows/package.json +1 -1
  39. package/dist/builtin/workflows/src/extension/workflow-prompts.ts +1 -0
  40. package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +4 -5
  41. package/dist/core/agent-session-retry.d.ts.map +1 -1
  42. package/dist/core/agent-session-retry.js +2 -2
  43. package/dist/core/agent-session-retry.js.map +1 -1
  44. package/docs/settings.md +1 -1
  45. package/docs/subagents.md +5 -1
  46. package/docs/workflows.md +111 -5
  47. package/npm-shrinkwrap.json +23 -23
  48. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/mcp",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
4
4
  "private": true,
5
5
  "description": "Atomic extension that adapts MCP (Model Context Protocol) servers into the coding agent. Fork of: https://github.com/nicobailon/pi-mcp-adapter",
6
6
  "contributors": [
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.7] - 2026-07-12
6
+
7
+ ### Fixed
8
+
9
+ - Fixed subagent model fallback to classify provider usage-limit exhaustion (for example `Codex error: The usage limit has been reached`, plus `usage_limit`/`usage_limit_reached`/`usage_limit_exceeded`/`insufficient_quota` codes) as a retryable quota/rate-limit failure, so configured `fallbackModels` advance to the next candidate provider/model instead of failing the run. The message matcher tolerates the same space/underscore/hyphen/joined separators as the code path, so provider errors that flatten the token into free text (for example `usage_limit_reached` or `usage-limit`) also advance the chain. Nested cause/diagnostic and session-shaped errors classify the same way, while cancellations, safety refusals, task/tool failures, and unrelated errors remain non-retryable; the shared conformance corpus keeps this rule in lockstep with the workflows classifier.
10
+
11
+ ## [0.9.7-alpha.1] - 2026-07-12
12
+
13
+ ### Fixed
14
+
15
+ - Fixed subagent model fallback to classify provider usage-limit exhaustion (for example `Codex error: The usage limit has been reached`, plus `usage_limit`/`usage_limit_reached`/`usage_limit_exceeded`/`insufficient_quota` codes) as a retryable quota/rate-limit failure, so configured `fallbackModels` advance to the next candidate provider/model instead of failing the run. The message matcher tolerates the same space/underscore/hyphen/joined separators as the code path, so provider errors that flatten the token into free text (for example `usage_limit_reached` or `usage-limit`) also advance the chain. Nested cause/diagnostic and session-shaped errors classify the same way, while cancellations, safety refusals, task/tool failures, and unrelated errors remain non-retryable; the shared conformance corpus keeps this rule in lockstep with the workflows classifier.
16
+
5
17
  ## [0.9.6] - 2026-07-12
6
18
 
7
19
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/subagents",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
4
4
  "private": true,
5
5
  "description": "Atomic extension for delegating tasks to subagents with chains, parallel execution, and background runs. Fork of: https://github.com/nicobailon/pi-subagents",
6
6
  "contributors": [
@@ -745,7 +745,9 @@ subagent({ action: "doctor" })
745
745
  **Intercom "Already waiting for a reply"**
746
746
 
747
747
  ```typescript
748
- // Resolve the current outbound ask before starting another one.
748
+ // Only one blocking intercom request (ask or contact_supervisor) can wait per
749
+ // session. Concurrent attempts lose the reservation with this normal tool
750
+ // error; resolve or await the current outbound ask, then retry or use send.
749
751
  ```
750
752
 
751
753
  **Parallel output-path conflict**
@@ -67,6 +67,7 @@ const RETRYABLE_MODEL_FAILURE_PATTERNS: readonly RegExp[] = [
67
67
  /too many requests/i,
68
68
  /\b429\b/,
69
69
  /quota/i,
70
+ /usage[\s_-]*limit/i, // usage-limit exhaustion is a quota condition (accept space/underscore/hyphen/joined forms); next candidate may have headroom
70
71
  /billing/i,
71
72
  /credit/i,
72
73
  /auth(?:entication)?/i,
@@ -275,7 +276,7 @@ function refusalKindFromCode(code: string | number | undefined): ModelFallbackFa
275
276
  const CODE_KINDS_BY_KIND: ReadonlyArray<readonly [ModelFallbackFailureKind, ReadonlySet<string>]> = [
276
277
  ["auth_on_candidate_provider", new Set(["auth", "auth_required", "authentication_required", "unauthorized", "forbidden", "invalid_api_key", "missing_api_key", "invalid_key"])],
277
278
  ["network_timeout", new Set(["etimedout", "econnreset", "econnrefused", "enotfound", "eai_again", "fetch_failed", "network_error", "timeout", "timeout_error", "und_err_connect_timeout"])],
278
- ["rate_limit", new Set(["rate_limit", "rate_limit_exceeded", "too_many_requests", "quota_exceeded"])],
279
+ ["rate_limit", new Set(["rate_limit", "rate_limit_exceeded", "too_many_requests", "quota_exceeded", "insufficient_quota", "usage_limit", "usage_limit_reached", "usage_limit_exceeded"])],
279
280
  ["cancelled", new Set(["aborterror", "aborted", "cancelled", "canceled"])],
280
281
  ["model_unavailable", new Set(["model_not_found", "model_unavailable", "model_disabled", "unknown_model"])],
281
282
  ["provider_unavailable", new Set(["provider_error", "api_error", "service_unavailable", "temporarily_unavailable", "overloaded"])],
@@ -326,7 +327,7 @@ function fallbackKindFromMessage(message: string, name: string | undefined): Mod
326
327
  const nameKind = kindFromCode(name);
327
328
  if (nameKind !== undefined) return nameKind;
328
329
  if (!RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return undefined;
329
- if (/rate\s*limit|too many requests|\b429\b|quota|billing|credit/i.test(message)) return "rate_limit";
330
+ if (/rate\s*limit|too many requests|\b429\b|quota|usage[\s_-]*limit|billing|credit/i.test(message)) return "rate_limit";
330
331
  if (/auth|unauthori[sz]ed|\b40[13]\b|api key|token expired|forbidden|invalid key/i.test(message)) return "auth_on_candidate_provider";
331
332
  if (/model.*(?:unavailable|disabled|not found|unknown)|(?:unavailable|disabled|not found|unknown).*model/i.test(message)) return "model_unavailable";
332
333
  if (/network|fetch failed|socket|connection refused|timeout|timed? out/i.test(message)) return "network_timeout";
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.7] - 2026-07-12
8
+
9
+ ### Changed
10
+
11
+ - Published the stable Atomic 0.9.7 release for the web-access extension; no functional web-access changes were made after 0.9.6.
12
+
13
+ ## [0.9.7-alpha.1] - 2026-07-12
14
+
15
+ ### Changed
16
+
17
+ - Published a synchronized Atomic 0.9.7-alpha.1 prerelease for the web-access extension; no functional web-access changes were made after 0.9.6.
18
+
7
19
  ## [0.9.6] - 2026-07-12
8
20
 
9
21
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
4
4
  "private": true,
5
5
  "description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
6
6
  "contributors": [
@@ -6,6 +6,46 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.7] - 2026-07-12
10
+
11
+ ### Added
12
+
13
+ - Added a shared convergence contract for the builtin `goal` and `ralph` workflows (`builtin/shared-prompts.ts` + deterministic gates in `builtin/review-convergence.ts`): workers/orchestrators must derive an observable acceptance/contract matrix from the literal objective/acceptance criteria before implementing (one row per clause mapped to the concrete check that proves it) and must model states, transitions, and invariants explicitly for stateful work; reviewers must independently derive adversarial checks (boundary/edge/negative and state/transition/invariant probes) from the literal contract before relying on the worker receipt, implementation notes, or worker-authored tests; reproduced findings require durable regression evidence (a persisted test or exact re-runnable check) before they count as resolved; and the evidence-closure policy is stated to both roles so approval semantics are visible in-prompt. All contracts inherit the literal-contract scope controls, so no behavior beyond the objective/acceptance criteria is required.
14
+ - Added deduplicated cross-reviewer `consolidated_findings` batches to the Goal and Ralph `review-round-latest.json` artifacts (`consolidateFindingsBatch` in `builtin/review-convergence.ts`), with blocking entries sorted first and reviewer attribution merged per finding. Goal's next worker turn reads the round artifact first and is instructed to plan and repair the whole batch — grouped by root cause, with regression evidence — instead of fixing one finding per turn; Ralph's research/orchestrator stages receive the same batch through the round artifact.
15
+
16
+ ### Changed
17
+
18
+ - Changed the built-in `goal` and `ralph` reviewer model defaults for GPT-5.6 Sol from the `:max` reasoning suffix to `:xhigh` across every provider form (`openai-codex`, `github-copilot`, `openai`, `cursor`, and `openrouter/openai`), including Ralph's `reviewer-b` primary model (`openai-codex/gpt-5.6-sol:xhigh`).
19
+ - Aligned all built-in Goal reviewer stages with Ralph's clean, non-forked reviewer context behavior and exact `reviewer-a` model configuration, making Claude Fable 5 the shared primary reviewer while keeping one canonical fallback chain.
20
+ - Changed builtin `goal` completion to evidence closure rather than reviewer agreement alone: the deterministic reducer now completes only when reviewer quorum is met AND no objective-relevant blocking finding from any reviewer in the current round remains unresolved. A quorum-met round with unresolved findings continues with an inspectable decision reason listing the findings, and the bounded loop still stops at `max_turns` as `needs_human` (with the closure gap recorded) so vetoes cannot drift indefinitely.
21
+ - Changed the shared Goal/Ralph review gate so severity labels alone can no longer dismiss objective-relevant findings: `required_by_objective` findings now block at any priority (P3 included) via the shared `findingBlocksClosure` predicate, while `consistent_with_objective` P3 nice-to-haves remain non-blocking (preserving the dummy-finding anti-spin behavior) and `beyond_objective`/`contradicts_objective` findings remain non-blocking scope controls. Goal's `reviewApproved` and Ralph's `isBlockingFinding`/`reviewDecisionApproved` now share this single deterministic predicate.
22
+ - Slimmed the builtin `goal`, `ralph`, and `open-claude-design` stage prompts to remove context-mode bloat: prompts no longer describe the stage's own context window ("previous conversation context", "previous workflow reasoning", or sibling parallel passes that a fresh stage cannot see), and forked continuation prompts now send only the per-iteration delta. Goal's forked worker turns send just the ledger pointer, latest receipts, and latest review artifacts; Ralph's forked orchestrator sends the rewritten research path and notes reminder; and Ralph's forked research-prompt-refinement and research continuations reference the skill, request, and contracts already present in their forked histories instead of re-injecting them (new `renderForkedResearchPromptRefinementPrompt`/`renderForkedResearchPrompt`). Each forked prompt carries a one-line pointer that previously established guidance still applies unchanged.
23
+
24
+ ### Fixed
25
+
26
+ - Fixed workflow stage model fallback to classify provider usage-limit exhaustion (for example `Codex error: The usage limit has been reached`, plus `usage_limit`/`usage_limit_reached`/`usage_limit_exceeded`/`insufficient_quota` codes) as a retryable quota/rate-limit failure, so configured `fallbackModels` advance to the next candidate provider/model instead of failing the stage. The message matcher tolerates the same space/underscore/hyphen/joined separators as the code path, so provider errors that flatten the token into free text (for example `usage_limit_reached` or `usage-limit`) also advance the chain. Nested cause/diagnostic and session-shaped errors classify the same way, while cancellations, safety refusals, task/tool failures, and unrelated errors remain non-retryable.
27
+ - Fixed model orchestration guidance so every workflow launch from interactive chat stays in the background: named runs remain automatically detached, direct `task`/`tasks`/`chain` launches require top-level `async: true`, inspection and control calls are excluded, and foreground launches require an explicit user request or technical necessity plus advance notice ([#1765](https://github.com/bastani-inc/atomic/issues/1765)).
28
+
29
+ ## [0.9.7-alpha.1] - 2026-07-12
30
+
31
+ ### Added
32
+
33
+ - Added a shared convergence contract for the builtin `goal` and `ralph` workflows (`builtin/shared-prompts.ts` + deterministic gates in `builtin/review-convergence.ts`): workers/orchestrators must derive an observable acceptance/contract matrix from the literal objective/acceptance criteria before implementing (one row per clause mapped to the concrete check that proves it) and must model states, transitions, and invariants explicitly for stateful work; reviewers must independently derive adversarial checks (boundary/edge/negative and state/transition/invariant probes) from the literal contract before relying on the worker receipt, implementation notes, or worker-authored tests; reproduced findings require durable regression evidence (a persisted test or exact re-runnable check) before they count as resolved; and the evidence-closure policy is stated to both roles so approval semantics are visible in-prompt. All contracts inherit the literal-contract scope controls, so no behavior beyond the objective/acceptance criteria is required.
34
+ - Added deduplicated cross-reviewer `consolidated_findings` batches to the Goal and Ralph `review-round-latest.json` artifacts (`consolidateFindingsBatch` in `builtin/review-convergence.ts`), with blocking entries sorted first and reviewer attribution merged per finding. Goal's next worker turn reads the round artifact first and is instructed to plan and repair the whole batch — grouped by root cause, with regression evidence — instead of fixing one finding per turn; Ralph's research/orchestrator stages receive the same batch through the round artifact.
35
+
36
+ ### Changed
37
+
38
+ - Changed the built-in `goal` and `ralph` reviewer model defaults for GPT-5.6 Sol from the `:max` reasoning suffix to `:xhigh` across every provider form (`openai-codex`, `github-copilot`, `openai`, `cursor`, and `openrouter/openai`), including Ralph's `reviewer-b` primary model (`openai-codex/gpt-5.6-sol:xhigh`).
39
+ - Aligned all built-in Goal reviewer stages with Ralph's clean, non-forked reviewer context behavior and exact `reviewer-a` model configuration, making Claude Fable 5 the shared primary reviewer while keeping one canonical fallback chain.
40
+ - Changed builtin `goal` completion to evidence closure rather than reviewer agreement alone: the deterministic reducer now completes only when reviewer quorum is met AND no objective-relevant blocking finding from any reviewer in the current round remains unresolved. A quorum-met round with unresolved findings continues with an inspectable decision reason listing the findings, and the bounded loop still stops at `max_turns` as `needs_human` (with the closure gap recorded) so vetoes cannot drift indefinitely.
41
+ - Changed the shared Goal/Ralph review gate so severity labels alone can no longer dismiss objective-relevant findings: `required_by_objective` findings now block at any priority (P3 included) via the shared `findingBlocksClosure` predicate, while `consistent_with_objective` P3 nice-to-haves remain non-blocking (preserving the dummy-finding anti-spin behavior) and `beyond_objective`/`contradicts_objective` findings remain non-blocking scope controls. Goal's `reviewApproved` and Ralph's `isBlockingFinding`/`reviewDecisionApproved` now share this single deterministic predicate.
42
+ - Slimmed the builtin `goal`, `ralph`, and `open-claude-design` stage prompts to remove context-mode bloat: prompts no longer describe the stage's own context window ("previous conversation context", "previous workflow reasoning", or sibling parallel passes that a fresh stage cannot see), and forked continuation prompts now send only the per-iteration delta. Goal's forked worker turns send just the ledger pointer, latest receipts, and latest review artifacts; Ralph's forked orchestrator sends the rewritten research path and notes reminder; and Ralph's forked research-prompt-refinement and research continuations reference the skill, request, and contracts already present in their forked histories instead of re-injecting them (new `renderForkedResearchPromptRefinementPrompt`/`renderForkedResearchPrompt`). Each forked prompt carries a one-line pointer that previously established guidance still applies unchanged.
43
+
44
+ ### Fixed
45
+
46
+ - Fixed workflow stage model fallback to classify provider usage-limit exhaustion (for example `Codex error: The usage limit has been reached`, plus `usage_limit`/`usage_limit_reached`/`usage_limit_exceeded`/`insufficient_quota` codes) as a retryable quota/rate-limit failure, so configured `fallbackModels` advance to the next candidate provider/model instead of failing the stage. The message matcher tolerates the same space/underscore/hyphen/joined separators as the code path, so provider errors that flatten the token into free text (for example `usage_limit_reached` or `usage-limit`) also advance the chain. Nested cause/diagnostic and session-shaped errors classify the same way, while cancellations, safety refusals, task/tool failures, and unrelated errors remain non-retryable.
47
+ - Fixed model orchestration guidance so every workflow launch from interactive chat stays in the background: named runs remain automatically detached, direct `task`/`tasks`/`chain` launches require top-level `async: true`, inspection and control calls are excluded, and foreground launches require an explicit user request or technical necessity plus advance notice ([#1765](https://github.com/bastani-inc/atomic/issues/1765)).
48
+
9
49
  ## [0.9.6] - 2026-07-12
10
50
 
11
51
  ### Changed
@@ -326,7 +326,7 @@ Atomic registers the canonical `structured_output` tool only for schema-enabled
326
326
 
327
327
  ### Model fallbacks
328
328
 
329
- Stages and high-level task helpers can retry transient provider/model failures with an ordered `fallbackModels` list. The primary `model` is tried first, then each fallback, and finally the current Atomic-selected model when available. Fallbacks are only used for retryable model/provider failures such as rate limits, quota/auth/provider outages, unavailable models, network timeouts, context-window overflows that Atomic's auto-compaction cannot resolve on the current model, and 5xx errors — ordinary tool, shell, validation, cancellation, and workflow-code failures are not retried.
329
+ Stages and high-level task helpers can retry transient provider/model failures with an ordered `fallbackModels` list. The primary `model` is tried first, then each fallback, and finally the current Atomic-selected model when available. Fallbacks are only used for retryable model/provider failures such as rate limits, quota/usage-limit exhaustion (provider messages such as `The usage limit has been reached` and codes such as `usage_limit_reached`/`insufficient_quota` classify as retryable rate-limit failures so the chain advances to a candidate with remaining headroom), auth/provider outages, unavailable models, network timeouts, context-window overflows that Atomic's auto-compaction cannot resolve on the current model, and 5xx errors — ordinary tool, shell, validation, cancellation, and workflow-code failures are not retried.
330
330
 
331
331
  ```typescript
332
332
  import { workflow } from "@bastani/workflows";
@@ -542,7 +542,7 @@ Tradeoff: `Type.Unsafe<T>()` does not deeply validate at runtime — it trusts t
542
542
 
543
543
  Input overrides are bare `key=value` tokens (no leading `--`). Values are JSON-parsed when possible, so numbers, booleans, and quoted strings work as expected (e.g. `count=3`, `flag=true`, `prompt="multi word value"`). A whole-object override can be passed as a single JSON token (e.g. `{"prompt":"...","count":3}`). Runtime validation is strict: unknown input keys, missing required values, type mismatches, and invalid `select` choices fail before a named workflow run starts.
544
544
 
545
- Workflows always run as **background tasks** in interactive sessions the chat editor stays free while a run executes. Press **F2** (or `/workflow connect <run-id>`) to attach to the live graph viewer; HIL prompts (`ctx.ui.input/confirm/select/editor/custom`) appear as awaiting-input graph nodes. Press Enter on a focused node, or click a visible graph node directly, to open that stage and answer locally, never as a modal dialog over the chat. Attached stage chats capture mouse/trackpad wheel events by default so scrolling stays inside the active stage transcript or prompt instead of falling through to terminal/main-chat scrollback. Press `ctrl+t` to toggle **copy mode**: copy mode disables workflow-chat mouse reporting so normal terminal/tmux text selection can work; press `ctrl+t` again to leave copy mode and restore workflow-chat scrolling. Archived read-only stage transcripts show the same copy-mode footer/status, allowing their transcript text to be selected and copied while preserving `esc` close and `ctrl+d` graph navigation. While copy mode is on, wheel/trackpad gestures are handled by the terminal/tmux and may scroll terminal scrollback, so leave copy mode before using the wheel again. Human input is detected when those runtime `ctx.ui.*` calls execute; workflows no longer have a declaration-time HIL flag.
545
+ Named workflow launches always run as **background tasks** in interactive sessions. Model-launched direct `task`, `tasks`, and `chain` calls must set top-level `async: true` so the chat editor stays free; inspection and control calls are unaffected. Foreground launches are reserved for explicit user requests or technical requirements, with notice before launch. Press **F2** (or `/workflow connect <run-id>`) to attach to the live graph viewer; HIL prompts (`ctx.ui.input/confirm/select/editor/custom`) appear as awaiting-input graph nodes. Press Enter on a focused node, or click a visible graph node directly, to open that stage and answer locally, never as a modal dialog over the chat. Attached stage chats capture mouse/trackpad wheel events by default so scrolling stays inside the active stage transcript or prompt instead of falling through to terminal/main-chat scrollback. Press `ctrl+t` to toggle **copy mode**: copy mode disables workflow-chat mouse reporting so normal terminal/tmux text selection can work; press `ctrl+t` again to leave copy mode and restore workflow-chat scrolling. Archived read-only stage transcripts show the same copy-mode footer/status, allowing their transcript text to be selected and copied while preserving `esc` close and `ctrl+d` graph navigation. While copy mode is on, wheel/trackpad gestures are handled by the terminal/tmux and may scroll terminal scrollback, so leave copy mode before using the wheel again. Human input is detected when those runtime `ctx.ui.*` calls execute; workflows no longer have a declaration-time HIL flag.
546
546
 
547
547
  Durable resume preserves the original completed-stage shape: replayed stage/task/parallel/child checkpoints retain summaries, durations, session/model metadata, and parallel branch parentage in the graph instead of flattening fanout branches into a sequential replay chain.
548
548
 
@@ -662,7 +662,7 @@ Child workflow outputs: `result`, `findings`, `research_doc_path`, `artifact_dir
662
662
 
663
663
  ### `goal`
664
664
 
665
- Goal Runner workflow: initialize a persisted goal ledger with a per-run goal id, immutable `acceptance_criteria`, and lifecycle events, render goal-continuation context, run bounded worker LM turns, append receipts, run three independent reviewers with objective-alignment findings and clause-by-clause requirements traceability, let a TypeScript reducer decide `complete`, `continue`, `blocked`, or `needs_human`, and optionally run a final-stage PR handoff after approval. Workers and reviewers are prompted to verify user-visible behavior end-to-end when practical with `playwright-cli`-skilled subagents for web/frontend flows that may depend on backend/API behavior and tmux-skilled subagents for TUI or terminal-app scenarios; they must assume credentials/auth/environment access exists until concrete checks plus an actual app/flow launch attempt prove otherwise, and skipped E2E must cite exact attempted commands and observed failure output. Reviewers also look for any QA E2E video referenced by the ledger or receipt and inspect the actual video before treating it as proof. Token budget behavior is intentionally excluded. Goal skips PR creation by default; prompt text alone does not opt in. Pass `create_pr=true` to authorize only the final `pull-request` stage to inspect provider credentials and attempt provider-appropriate PR/MR/review creation after Goal reaches `complete` within the turn budget.
665
+ Goal Runner workflow: initialize a persisted goal ledger with a per-run goal id, immutable `acceptance_criteria`, and lifecycle events, render goal-continuation context, run bounded worker LM turns, append receipts, run three independent reviewers with objective-alignment findings and clause-by-clause requirements traceability, let a TypeScript reducer decide `complete`, `continue`, `blocked`, or `needs_human`, and optionally run a final-stage PR handoff after approval. All three reviewers start in clean, non-forked contexts like Ralph's reviewers and use Ralph's exact `reviewer-a` model chain, led by Claude Fable 5. Workers begin from an observable acceptance/contract matrix derived from the literal objective/acceptance criteria and are prompted to model states, transitions, and invariants explicitly for stateful work; each review round's findings are consolidated into a deduplicated cross-reviewer batch (`consolidated_findings` in the round artifact) that the next worker turn repairs together, with durable regression evidence required for reproduced findings. Reviewers independently derive adversarial checks from the literal contract before relying on the worker receipt or worker-authored tests. Workers and reviewers are prompted to verify user-visible behavior end-to-end when practical with `playwright-cli`-skilled subagents for web/frontend flows that may depend on backend/API behavior and tmux-skilled subagents for TUI or terminal-app scenarios; they must assume credentials/auth/environment access exists until concrete checks plus an actual app/flow launch attempt prove otherwise, and skipped E2E must cite exact attempted commands and observed failure output. Reviewers also look for any QA E2E video referenced by the ledger or receipt and inspect the actual video before treating it as proof. Token budget behavior is intentionally excluded. Goal skips PR creation by default; prompt text alone does not opt in. Pass `create_pr=true` to authorize only the final `pull-request` stage to inspect provider credentials and attempt provider-appropriate PR/MR/review creation after Goal reaches `complete` within the turn budget.
666
666
 
667
667
  ```text
668
668
  /workflow goal objective="Migrate the database layer to Drizzle ORM" base_branch=develop
@@ -677,13 +677,13 @@ Goal Runner workflow: initialize a persisted goal ledger with a per-run goal id,
677
677
  | `base_branch` | `string` | — | `origin/main` | Branch reviewers and the optional final stage compare the current delta with. |
678
678
  | `create_pr` | `boolean` | — | `false` | Safe-by-default PR creation flag. Omitted or `false` skips the final `pull-request` stage and omits `pr_report`; prompt text alone does not opt in, and only strict `true` authorizes the final `pull-request` stage to attempt provider-appropriate PR/MR/review creation after Goal reaches `complete`. |
679
679
 
680
- `goal` defaults to 10 worker/review turns. Reviewer quorum is fixed internally at 2 reviewer `complete` votes. The repeated-blocker threshold defaults to 3 consecutive same-blocker turns and is clamped to `max_turns` when you run fewer than 3 turns.
680
+ `goal` defaults to 10 worker/review turns. Reviewer quorum is fixed internally at 2 reviewer `complete` votes, but approval is evidence closure rather than reviewer agreement alone: the reducer completes the run only when quorum is met and no objective-relevant blocking finding from any reviewer in the current round remains unresolved (`required_by_objective` findings block at any priority, P3 included; `consistent_with_objective` P3 nice-to-haves do not block). When closure fails, the decision reason records the unresolved findings, and the bounded loop stops inspectably at `max_turns` as `needs_human`. The repeated-blocker threshold defaults to 3 consecutive same-blocker turns and is clamped to `max_turns` when you run fewer than 3 turns.
681
681
 
682
682
  Child workflow outputs: `result`, `status`, `approved`, `goal_id`, `objective`, `acceptance_criteria`, `ledger_path`, `turns_completed`, `iterations_completed`, `receipts`, `remaining_work`, `review_report`, and `review_report_path`. `pr_report` is included only when `create_pr=true`, Goal reaches `complete`, and the final `pull-request` stage runs.
683
683
 
684
684
  ### `ralph`
685
685
 
686
- Raw prompt → prompt-engineering research → orchestrate → review workflow with optional final-stage PR handoff: use the raw prompt as the operative objective, keep optional `acceptance_criteria` as the immutable original task contract (defaulting to `prompt`), transform the prompt into a codebase and online research question with `/skill:prompt-engineer`, run `/skill:research-codebase` against it, write findings under `research/`, delegate implementation through sub-agents from that research, run parallel reviewers across Claude Fable 5 and GPT-5.5 Codex model families, and iterate until approval or the loop limit. Ralph's research, orchestrator, and reviewer prompts receive the objective next to the literal acceptance contract; when launching follow-up Ralph runs from reviewer findings, pass the ORIGINAL task text as `acceptance_criteria` so deltas cannot drift from the contract. Ralph's orchestrator and reviewers are prompted to verify user-visible behavior end-to-end when practical with `playwright-cli`-skilled subagents for web/frontend flows that may depend on backend/API behavior and tmux-skilled subagents for TUI or terminal-app scenarios. They must assume credentials/auth/environment access exists until concrete non-destructive checks plus an actual launch/flow attempt prove otherwise; skipped E2E is valid only when exact attempted commands and observed failure output are recorded. For UI-applicable or full-stack changes, the orchestrator runs a `playwright-cli` end-to-end QA pass and records a reviewable proof video, references it in the implementation notes, and exposes it as the `qa_video_path` output; reviewers receive that path and inspect the actual video before treating it as proof. Review decisions include `requirements_traceability`, a non-empty clause-by-clause map over every prompt/acceptance-criteria requirement, and Ralph approval requires every entry to be `proven`; worker-authored tests/snapshots passing are circular evidence unless tied to independent current-state proof. When `create_pr=true`, the final `pull-request` stage attaches or links that video to the created PR/MR/review. Follow-up iterations pass unresolved review artifacts into prompt-engineering/research and fork research from prior research session data when available. Ralph skips PR creation by default; prompt text alone does not opt in. Pass `create_pr=true` to authorize only the final `pull-request` stage to inspect provider credentials and attempt provider-appropriate PR/MR/review creation (for example GitHub `gh`, Azure Repos `az repos pr create`, or Sapling/Phabricator tooling). Ralph's own PR-creation instructions live in that final stage. Reviewers inspect repository infrastructure directly as needed; Ralph no longer runs separate `infra-*` discovery stages.
686
+ Raw prompt → prompt-engineering research → orchestrate → review workflow with optional final-stage PR handoff: use the raw prompt as the operative objective, keep optional `acceptance_criteria` as the immutable original task contract (defaulting to `prompt`), transform the prompt into a codebase and online research question with `/skill:prompt-engineer`, run `/skill:research-codebase` against it, write findings under `research/`, delegate implementation through sub-agents from that research, run parallel reviewers across Claude Fable 5 and GPT-5.5 Codex model families, and iterate until approval or the loop limit. Ralph's research, orchestrator, and reviewer prompts receive the objective next to the literal acceptance contract; when launching follow-up Ralph runs from reviewer findings, pass the ORIGINAL task text as `acceptance_criteria` so deltas cannot drift from the contract. The orchestrator begins from an observable acceptance/contract matrix derived from the literal prompt/acceptance criteria, models states/transitions/invariants explicitly for stateful work, and repairs unresolved reviewer findings as one consolidated batch (the round artifact carries a deduplicated cross-reviewer `consolidated_findings` list) with durable regression evidence for reproduced findings. Reviewers independently derive adversarial checks from the literal contract before relying on the implementation notes, orchestrator report, or worker-authored tests, and `required_by_objective` findings block at any priority (P3 included) while `consistent_with_objective` P3 nice-to-haves stay non-blocking. Ralph's orchestrator and reviewers are prompted to verify user-visible behavior end-to-end when practical with `playwright-cli`-skilled subagents for web/frontend flows that may depend on backend/API behavior and tmux-skilled subagents for TUI or terminal-app scenarios. They must assume credentials/auth/environment access exists until concrete non-destructive checks plus an actual launch/flow attempt prove otherwise; skipped E2E is valid only when exact attempted commands and observed failure output are recorded. For UI-applicable or full-stack changes, the orchestrator runs a `playwright-cli` end-to-end QA pass and records a reviewable proof video, references it in the implementation notes, and exposes it as the `qa_video_path` output; reviewers receive that path and inspect the actual video before treating it as proof. Review decisions include `requirements_traceability`, a non-empty clause-by-clause map over every prompt/acceptance-criteria requirement, and Ralph approval requires every entry to be `proven`; worker-authored tests/snapshots passing are circular evidence unless tied to independent current-state proof. When `create_pr=true`, the final `pull-request` stage attaches or links that video to the created PR/MR/review. Follow-up iterations pass unresolved review artifacts into prompt-engineering/research and fork research from prior research session data when available. Ralph skips PR creation by default; prompt text alone does not opt in. Pass `create_pr=true` to authorize only the final `pull-request` stage to inspect provider credentials and attempt provider-appropriate PR/MR/review creation (for example GitHub `gh`, Azure Repos `az repos pr create`, or Sapling/Phabricator tooling). Ralph's own PR-creation instructions live in that final stage. Reviewers inspect repository infrastructure directly as needed; Ralph no longer runs separate `infra-*` discovery stages.
687
687
 
688
688
  ```text
689
689
  /workflow ralph prompt="Migrate the database layer to Drizzle ORM" max_loops=3 base_branch=develop
@@ -1,7 +1,10 @@
1
1
  import { writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import type { ReviewDecision, ReviewRecord } from "./goal-types.js";
4
- import type { ReviewConvergenceSummary } from "./review-convergence.js";
4
+ import {
5
+ consolidateFindingsBatch,
6
+ type ReviewConvergenceSummary,
7
+ } from "./review-convergence.js";
5
8
 
6
9
  export function artifactSafeName(value: string): string {
7
10
  const safe = value
@@ -42,9 +45,19 @@ export async function writeReviewRoundArtifact(
42
45
  ): Promise<string> {
43
46
  const artifactPath = join(artifactDir, "review-round-latest.json");
44
47
  const visibleReviews = reviews.map(withoutTurn);
45
- await writeFile(artifactPath, `${JSON.stringify({ reviews: visibleReviews }, null, 2)}\n`, {
46
- encoding: "utf8",
47
- });
48
+ // Deduplicated cross-reviewer findings batch so the next worker turn can
49
+ // plan and repair the round's findings together instead of one at a time.
50
+ const consolidatedFindings = consolidateFindingsBatch(
51
+ reviews.map((review) => ({
52
+ reviewer: review.reviewer,
53
+ findings: review.findings,
54
+ })),
55
+ );
56
+ await writeFile(
57
+ artifactPath,
58
+ `${JSON.stringify({ reviews: visibleReviews, consolidated_findings: consolidatedFindings }, null, 2)}\n`,
59
+ { encoding: "utf8" },
60
+ );
48
61
  return artifactPath;
49
62
  }
50
63
 
@@ -1,6 +1,11 @@
1
1
  import {
2
+ ACCEPTANCE_MATRIX_CONTRACT,
2
3
  E2E_VERIFICATION_GUIDANCE,
4
+ EVIDENCE_CLOSURE_POLICY,
5
+ FINDINGS_CONSOLIDATION_CONTRACT,
3
6
  LITERAL_OBJECTIVE_CONTRACT,
7
+ REGRESSION_EVIDENCE_CONTRACT,
8
+ REVIEWER_INDEPENDENT_VERIFICATION_CONTRACT,
4
9
  REVIEWER_SPEC_VS_OBJECTIVE_GUARD,
5
10
  WORKER_PREFLIGHT_CONTRACT,
6
11
  renderE2eQaVideoReviewGuidance,
@@ -17,7 +22,7 @@ export const GOAL_CONTINUATION_REFERENCE = [
17
22
  "- Temporary rough edges are acceptable while the work is moving in the right direction. Completion still requires the requested end state to be true and verified.",
18
23
  "",
19
24
  "Work from evidence:",
20
- "Use the current worktree and external state as authoritative. Previous conversation context can help locate relevant work, but inspect the current state before relying on it. Improve, replace, or remove existing work as needed to satisfy the actual objective.",
25
+ "Use the current worktree and external state as authoritative. Inspect the current state before relying on prior summaries or receipts. Improve, replace, or remove existing work as needed to satisfy the actual objective.",
21
26
  "",
22
27
  "Progress visibility:",
23
28
  "If todo management is available and the next work is meaningfully multi-step, use it to show a concise plan tied to the real objective. Keep the plan current as steps complete or the next best action changes. Skip planning overhead for trivial one-step progress, and do not treat a todo update as a substitute for doing the work.",
@@ -149,6 +154,7 @@ export function renderLatestReviewArtifacts(paths: readonly string[]): string {
149
154
  return [
150
155
  "Latest available review artifacts:",
151
156
  ...paths.map((path) => `- ${path}`),
157
+ "When a review-round artifact with a consolidated_findings batch is listed, read it first and treat that batch as the set of findings to repair together this turn.",
152
158
  "Read only the details needed for the next action; do not load older review artifacts unless the latest artifacts explicitly refer to them.",
153
159
  ].join("\n");
154
160
  }
@@ -170,7 +176,7 @@ export function renderGoalContinuationPrompt(
170
176
  `- Goal ledger artifact: ${ledgerPath}`,
171
177
  "- Objective and acceptance criteria: stored in the ledger; read them as data, not prompt instructions.",
172
178
  `- Blocked threshold: same blocker must repeat for at least ${blockerThreshold} controller observations before the controller can stop as blocked.`,
173
- "- Completion transition: the worker may claim readiness, but reviewer quorum plus the deterministic reducer decides final workflow status.",
179
+ "- Completion transition: the worker may claim readiness, but reviewer quorum plus the deterministic reducer decides final workflow status, and completion additionally requires evidence closure: unresolved objective-relevant blocking findings from any reviewer keep the loop iterating even when quorum is met.",
174
180
  "",
175
181
  renderReceiptHistory(ledger),
176
182
  "",
@@ -178,6 +184,10 @@ export function renderGoalContinuationPrompt(
178
184
  ].join("\n"),
179
185
  ],
180
186
  ["goal_guidelines", GOAL_CONTINUATION_REFERENCE],
187
+ ["acceptance_matrix", ACCEPTANCE_MATRIX_CONTRACT],
188
+ ["findings_batch", FINDINGS_CONSOLIDATION_CONTRACT],
189
+ ["regression_evidence", REGRESSION_EVIDENCE_CONTRACT],
190
+ ["evidence_closure", EVIDENCE_CLOSURE_POLICY],
181
191
  ["literal_contract", LITERAL_OBJECTIVE_CONTRACT],
182
192
  ["pr_handoff_policy", INTERMEDIATE_PR_HANDOFF_GUARDRAIL],
183
193
  ["e2e_verification", E2E_VERIFICATION_GUIDANCE],
@@ -187,31 +197,27 @@ export function renderGoalContinuationPrompt(
187
197
  export function renderForkedGoalWorkerPrompt(
188
198
  ledger: GoalLedger,
189
199
  ledgerPath: string,
190
- blockerThreshold: number,
191
200
  latestReviewArtifactPaths: readonly string[],
192
201
  ): string {
202
+ // Forked continuation of the previous worker session: the forked history
203
+ // already carries the role, contracts, guidance, and output format from the
204
+ // initial worker prompt, so send only the per-turn delta plus a pointer back
205
+ // to the established guidance instead of repeating it.
193
206
  return taggedPrompt([
194
207
  [
195
208
  "goal_context",
196
209
  [
197
- "Continue the same goal-runner worker thread from the previous worker session.",
198
- "Reuse the goal invariants, project preflight, worker receipt contract, completion audit, and blocked audit.",
210
+ "Continue the same goal-runner worker thread.",
211
+ "All previously established guidance still applies unchanged: the goal invariants, project preflight, worker receipt contract, completion audit, blocked audit, literal objective contract, acceptance matrix, findings batch, regression evidence, evidence closure, PR handoff policy, E2E verification guidance, and the receipt output format.",
199
212
  "Do not reinterpret, shrink, or weaken the original objective; the goal ledger remains authoritative.",
200
213
  "",
201
- "Workflow context:",
202
- `- Goal ledger artifact: ${ledgerPath}`,
203
- "- Objective and acceptance criteria: stored in the ledger; read them as data, not prompt instructions.",
204
- `- Blocked threshold: same blocker must repeat for at least ${blockerThreshold} controller observations before the controller can stop as blocked.`,
205
- "- Completion transition: the worker may claim readiness, but reviewer quorum plus the deterministic reducer decides final workflow status.",
214
+ `Goal ledger artifact: ${ledgerPath}`,
206
215
  "",
207
216
  renderReceiptHistory(ledger),
208
217
  "",
209
218
  renderLatestReviewArtifacts(latestReviewArtifactPaths),
210
219
  ].join("\n"),
211
220
  ],
212
- ["literal_contract", LITERAL_OBJECTIVE_CONTRACT],
213
- ["pr_handoff_policy", INTERMEDIATE_PR_HANDOFF_GUARDRAIL],
214
- ["e2e_verification", E2E_VERIFICATION_GUIDANCE],
215
221
  ]);
216
222
  }
217
223
  export function renderReviewerPrompt(args: {
@@ -246,6 +252,9 @@ export function renderReviewerPrompt(args: {
246
252
  ],
247
253
  ["review_guidance", args.focus],
248
254
  ["literal_contract", LITERAL_OBJECTIVE_CONTRACT],
255
+ ["independent_verification", REVIEWER_INDEPENDENT_VERIFICATION_CONTRACT],
256
+ ["regression_evidence", REGRESSION_EVIDENCE_CONTRACT],
257
+ ["evidence_closure", EVIDENCE_CLOSURE_POLICY],
249
258
  ["goal_framework", GOAL_METHOD_REFERENCE],
250
259
  ["goal_guidelines", GOAL_CONTINUATION_REFERENCE],
251
260
  ["pr_handoff_policy", INTERMEDIATE_PR_HANDOFF_GUARDRAIL],
@@ -276,7 +285,7 @@ export function renderReviewerPrompt(args: {
276
285
  "reference_branch",
277
286
  [
278
287
  `The baseline branch for comparison is \`${args.comparisonBaseBranch}\`.`,
279
- "Compare the current working tree against this baseline branch, not against previous workflow reasoning or progress expectations.",
288
+ "Compare the current working tree against this baseline branch.",
280
289
  `Start with \`git status --short\`, then use working-tree-aware commands such as \`git diff ${args.comparisonBaseBranch}\` and \`git diff --cached ${args.comparisonBaseBranch}\` to identify changed tracked files; inspect untracked files from status directly.`,
281
290
  ].join("\n"),
282
291
  ],
@@ -343,7 +352,7 @@ export function renderReviewerPrompt(args: {
343
352
  "review_stage_contract",
344
353
  [
345
354
  "The structured review decision is only valid after you inspect the actual repository state and compare it against the stated baseline branch.",
346
- "Do not approve based solely on workflow stage summaries or prior agent reasoning.",
355
+ "Do not approve based solely on summaries in the provided context artifacts.",
347
356
  "Treat this review as the completion audit for the current repository and goal state: approval means receipts and current evidence prove the original owner outcome against the full objective.",
348
357
  "Do not approve when proof only shows planning, discovery, task selection, helper documents, or a narrow slice while the broader requested outcome still has required work remaining.",
349
358
  "The tool call is the final verdict after review work, not a shortcut around review work.",
@@ -353,12 +362,13 @@ export function renderReviewerPrompt(args: {
353
362
  "required_actions_before_tool_call",
354
363
  [
355
364
  "1. Identify the changed files or diff under review.",
356
- "2. Read the relevant changed code and directly affected call sites/tests/configs.",
357
- "3. Read the goal ledger and worker receipt, then map receipts to the inferred verification oracle and original owner outcome.",
358
- "4. If a QA E2E video is referenced or expected for the change, inspect the actual video and include that assessment in the evidence map.",
359
- "5. Run or delegate focused validation when needed to resolve uncertainty.",
360
- "6. Decide whether the receipt/evidence map proves completion; if evidence is uncertain, indirect, stale, missing, or narrower than the requested outcome, set goal_oracle_satisfied=false and stop_review_loop=false.",
361
- "7. If you cannot inspect receipts, video evidence, or validate enough to approve safely, populate reviewer_error and set stop_review_loop=false.",
365
+ "2. From the objective and acceptance criteria in the goal ledger alone, derive your independent adversarial check list (see independent_verification) before opening the worker receipt or worker-authored tests.",
366
+ "3. Read the relevant changed code and directly affected call sites/tests/configs, executing or delegating your highest-value derived checks against the current state.",
367
+ "4. Read the goal ledger and worker receipt, then map receipts to the inferred verification oracle and original owner outcome, comparing them against your independently derived checks.",
368
+ "5. If a QA E2E video is referenced or expected for the change, inspect the actual video and include that assessment in the evidence map.",
369
+ "6. Run or delegate focused validation when needed to resolve uncertainty, and check that fixes for previously reproduced findings carry durable regression evidence.",
370
+ "7. Decide whether the receipt/evidence map proves completion; if evidence is uncertain, indirect, stale, missing, or narrower than the requested outcome, set goal_oracle_satisfied=false and stop_review_loop=false.",
371
+ "8. If you cannot inspect receipts, video evidence, or validate enough to approve safely, populate reviewer_error and set stop_review_loop=false.",
362
372
  ].join("\n"),
363
373
  ],
364
374
  [
@@ -395,7 +405,7 @@ export function renderReviewerPrompt(args: {
395
405
  [
396
406
  "Set stop_review_loop=true only when there are no blocking findings, overall_correctness is patch is correct, goal_oracle_satisfied is true, requirements_traceability is non-empty and every non-final-action entry is proven, no objective-relevant implementation or validation remains, and reviewer_error is null/omitted.",
397
407
  "Enumerate every explicit requirement clause from the objective and acceptance criteria in requirements_traceability, including clauses about existing tests/snapshots and expected behavior. Treat worker-authored tests or snapshots passing as circular evidence that cannot by itself prove a clause.",
398
- "P3 nice-to-have findings are non-blocking when the rest of the approval contract is satisfied; do not use P3 for work required by the objective or verification oracle. Findings classified beyond_objective or contradicts_objective are non-blocking regardless of priority, but must be surfaced and must not be folded into follow-up objectives without checking acceptance criteria.",
408
+ "P3 findings are non-blocking only when classified consistent_with_objective and the rest of the approval contract is satisfied; findings classified required_by_objective block at any priority (P3 included) because severity labels alone never dismiss objective-relevant findings. Do not use P3 for work required by the objective or verification oracle. Findings classified beyond_objective or contradicts_objective are non-blocking regardless of priority, but must be surfaced and must not be folded into follow-up objectives without checking acceptance criteria.",
399
409
  "If you hit a reviewer/tool/validation error, set stop_review_loop=false and populate reviewer_error instead of pretending the patch is approved.",
400
410
  ].join("\n"),
401
411
  ],
@@ -1,5 +1,10 @@
1
1
  import type { BlockerObservation, GoalLedger, ReducerOutcome, ReviewRecord } from "./goal-types.js";
2
- import { summarizeReviewConvergence, type ReviewNextAction } from "./review-convergence.js";
2
+ import {
3
+ closureGapSummary,
4
+ summarizeReviewConvergence,
5
+ unresolvedClosureFindings,
6
+ type ReviewNextAction,
7
+ } from "./review-convergence.js";
3
8
 
4
9
  function reducerSummary(
5
10
  reviews: readonly ReviewRecord[],
@@ -89,8 +94,25 @@ export function reduceGoalDecision(
89
94
  const completeVotes = turnReviews.filter(
90
95
  (review) => review.decision === "complete",
91
96
  ).length;
97
+ const quorumMet = completeVotes >= options.reviewQuorum;
92
98
 
93
- if (completeVotes >= options.reviewQuorum) {
99
+ // Evidence closure: reviewer agreement alone cannot complete the run. Any
100
+ // objective-relevant blocking finding from ANY reviewer in the current round
101
+ // vetoes completion until it is resolved with evidence or reclassified
102
+ // against the literal contract, even when quorum is met. The loop stays
103
+ // bounded by max_turns and stops inspectably below.
104
+ const unresolvedFindings = unresolvedClosureFindings(
105
+ turnReviews.map((review) => ({
106
+ reviewer: review.reviewer,
107
+ findings: review.findings,
108
+ })),
109
+ );
110
+ const closureVeto = quorumMet && unresolvedFindings.length > 0;
111
+ const closureVetoReason = closureVeto
112
+ ? `Reviewer quorum met (${completeVotes}/${options.reviewQuorum}) without evidence closure: ${closureGapSummary(unresolvedFindings)}. Severity labels alone cannot dismiss objective-relevant findings; resolve them with evidence or reclassify them against the literal contract.`
113
+ : undefined;
114
+
115
+ if (quorumMet && unresolvedFindings.length === 0) {
94
116
  const summary = reducerSummary(turnReviews, true, options.nextActionOnComplete);
95
117
  return {
96
118
  status: "complete",
@@ -98,7 +120,7 @@ export function reduceGoalDecision(
98
120
  ...summary,
99
121
  turn: options.turn,
100
122
  decision: "complete",
101
- reason: `Reviewer quorum met: ${completeVotes}/${options.reviewQuorum} reviewers marked complete.`,
123
+ reason: `Reviewer quorum met with evidence closure: ${completeVotes}/${options.reviewQuorum} reviewers marked complete and no objective-relevant blocking findings remain.`,
102
124
  complete_votes: completeVotes,
103
125
  review_quorum: options.reviewQuorum,
104
126
  },
@@ -138,7 +160,9 @@ export function reduceGoalDecision(
138
160
  ...reducerSummary(turnReviews, false, "needs_human"),
139
161
  turn: options.turn,
140
162
  decision: "needs_human",
141
- reason: `Worker attempt budget reached without reviewer quorum. Remaining work: ${collectRemainingWork(turnReviews)}`,
163
+ reason: closureVetoReason !== undefined
164
+ ? `Worker attempt budget reached without evidence closure. ${closureVetoReason}`
165
+ : `Worker attempt budget reached without reviewer quorum. Remaining work: ${collectRemainingWork(turnReviews)}`,
142
166
  complete_votes: completeVotes,
143
167
  review_quorum: options.reviewQuorum,
144
168
  ...(observation ? { blocker: observation.blocker } : {}),
@@ -153,7 +177,7 @@ export function reduceGoalDecision(
153
177
  ...reducerSummary(turnReviews, false, "implementation"),
154
178
  turn: options.turn,
155
179
  decision: "continue",
156
- reason: `Reviewer quorum not met. Remaining work: ${collectRemainingWork(turnReviews)}`,
180
+ reason: closureVetoReason ?? `Reviewer quorum not met. Remaining work: ${collectRemainingWork(turnReviews)}`,
157
181
  complete_votes: completeVotes,
158
182
  review_quorum: options.reviewQuorum,
159
183
  ...(observation ? { blocker: observation.blocker } : {}),
@@ -2,6 +2,7 @@ import type { WorkflowTaskResult } from "../src/shared/types.js";
2
2
  import type { ReviewDecision, ReviewRecord } from "./goal-types.js";
3
3
  import {
4
4
  finalActionRemaining,
5
+ findingBlocksClosure,
5
6
  parseFailureDiagnostics,
6
7
  summarizeReviewConvergence,
7
8
  traceabilityProvenExceptFinalAction,
@@ -28,18 +29,12 @@ export function parsedReviewDecisionFromResult(
28
29
  };
29
30
  }
30
31
 
31
- const NON_BLOCKING_ALIGNMENTS = new Set([
32
- "beyond_objective",
33
- "contradicts_objective",
34
- ]);
35
-
36
32
  function findingBlocksApproval(finding: ReviewDecision["findings"][number]): boolean {
37
- const alignment = finding.objective_alignment;
38
- if (NON_BLOCKING_ALIGNMENTS.has(alignment)) return false;
39
- if (alignment !== "required_by_objective" && alignment !== "consistent_with_objective") {
40
- return true;
41
- }
42
- return finding.priority !== 3;
33
+ // Shared evidence-closure predicate: required_by_objective findings block at
34
+ // any priority (severity labels alone never dismiss objective-relevant
35
+ // findings); consistent_with_objective P3 nits stay non-blocking;
36
+ // beyond/contradicts_objective findings never block.
37
+ return findingBlocksClosure(finding);
43
38
  }
44
39
 
45
40
  function traceabilityApproves(
@@ -144,19 +144,21 @@ export async function runGoalWorkflow(ctx: GoalRunnerContext, options: GoalWorkf
144
144
  excludedTools: ["ask_user_question"],
145
145
  };
146
146
 
147
+ // Keep this model list identical to Ralph reviewer-a while preserving an
148
+ // independent Goal configuration and Goal's richer decision schema.
147
149
  const reviewerModelConfig = {
148
150
  model: "anthropic/claude-fable-5:high",
149
151
  fallbackModels: [
150
- "openai-codex/gpt-5.6-sol:max",
151
- "github-copilot/gpt-5.6-sol:max",
152
- "openai/gpt-5.6-sol:max",
152
+ "openai-codex/gpt-5.6-sol:xhigh",
153
+ "github-copilot/gpt-5.6-sol:xhigh",
154
+ "openai/gpt-5.6-sol:xhigh",
153
155
  "openai-codex/gpt-5.5:xhigh",
154
156
  "github-copilot/gpt-5.5:xhigh",
155
157
  "openai/gpt-5.5:xhigh",
156
158
  "github-copilot/claude-opus-4.8 (1m):high",
157
159
  "anthropic/claude-opus-4-8:high",
158
160
  "cursor/claude-fable-5:high",
159
- "cursor/gpt-5.6-sol:max",
161
+ "cursor/gpt-5.6-sol:xhigh",
160
162
  "cursor/gpt-5.5:high",
161
163
  "cursor/claude-opus-4-8-thinking:high",
162
164
  "cursor/grok-4.5",
@@ -164,7 +166,7 @@ export async function runGoalWorkflow(ctx: GoalRunnerContext, options: GoalWorkf
164
166
  "zai-coding-cn/glm-5.2:xhigh",
165
167
  "cursor/glm-5.2",
166
168
  "openrouter/anthropic/claude-fable-5:high",
167
- "openrouter/openai/gpt-5.6-sol:max",
169
+ "openrouter/openai/gpt-5.6-sol:xhigh",
168
170
  "openrouter/sakana/fugu-ultra:high",
169
171
  "openrouter/openai/gpt-5.5:xhigh",
170
172
  "openrouter/anthropic/claude-opus-4-8:high",
@@ -207,7 +209,6 @@ export async function runGoalWorkflow(ctx: GoalRunnerContext, options: GoalWorkf
207
209
  : renderForkedGoalWorkerPrompt(
208
210
  ledger,
209
211
  ledgerPath,
210
- blockerThreshold,
211
212
  latestReviewArtifactPaths,
212
213
  );
213
214
 
@@ -341,11 +342,9 @@ export async function runGoalWorkflow(ctx: GoalRunnerContext, options: GoalWorkf
341
342
  );
342
343
  return record;
343
344
  }));
344
- latestReviewArtifactPaths = latestReviews.map((review) => review.artifact_path);
345
- latestReviewReportPath = await writeReviewRoundArtifact(
346
- artifactDir,
347
- latestReviews,
348
- );
345
+ latestReviewReportPath = await writeReviewRoundArtifact(artifactDir, latestReviews);
346
+ // Consolidated round artifact leads so the next worker turn plans the full findings batch first.
347
+ latestReviewArtifactPaths = [latestReviewReportPath, ...latestReviews.map((review) => review.artifact_path)];
349
348
  ledger.reviews.push(...latestReviews);
350
349
  appendLifecycleEvent(
351
350
  ledger,
@@ -162,7 +162,7 @@ export async function runOpenClaudeDesignWorkflow(ctx: OpenClaudeDesignContext):
162
162
  ["role", "You are an opinionated staff design engineer."],
163
163
  [
164
164
  "objective",
165
- `Audit the project UI constraints that must shape: ${designBrief}. Independently scan the repository and evaluate the evidence you find against impeccable's six dimensions of design quality. Also capture/parse any user-provided references in this same pass. This runs in PARALLEL with the locator and pattern passes, so do your own scan rather than relying on their output.`,
165
+ `Audit the project UI constraints that must shape: ${designBrief}. Independently scan the repository and evaluate the evidence you find against impeccable's six dimensions of design quality. Also capture/parse any user-provided references in this same pass. Do your own scan; do not assume any other stage's output is available.`,
166
166
  ],
167
167
  [
168
168
  "impeccable_skill",
@@ -203,7 +203,7 @@ export async function runOpenClaudeDesignWorkflow(ctx: OpenClaudeDesignContext):
203
203
  ["role", "You are an opinionated staff design engineer."],
204
204
  [
205
205
  "objective",
206
- `Extract reusable patterns and anti-patterns for: ${designBrief}. Apply the impeccable \`extract\` sub-skill to find design patterns to reuse and anti-patterns to avoid. Also parse/capture user references inside this same pass, translating them into reusable generation patterns. This runs in PARALLEL with the locator and auditor passes, so scan the codebase yourself rather than depending on their output.`,
206
+ `Extract reusable patterns and anti-patterns for: ${designBrief}. Apply the impeccable \`extract\` sub-skill to find design patterns to reuse and anti-patterns to avoid. Also parse/capture user references inside this same pass, translating them into reusable generation patterns. Do your own scan; do not assume any other stage's output is available.`,
207
207
  ],
208
208
  ["user_references", userReferenceContext],
209
209
  ["reference_handling", referenceHandlingRules],