@bastani/atomic 0.9.14-alpha.4 → 0.9.14-alpha.5

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 (141) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/package.json +1 -1
  4. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  5. package/dist/builtin/subagents/package.json +1 -1
  6. package/dist/builtin/subagents/src/extension/schemas.ts +5 -0
  7. package/dist/builtin/subagents/src/runs/shared/long-running-guard.ts +3 -1
  8. package/dist/builtin/subagents/src/runs/shared/progress-trend.ts +69 -0
  9. package/dist/builtin/subagents/src/runs/shared/subagent-control.ts +12 -1
  10. package/dist/builtin/subagents/src/shared/types-results.ts +2 -0
  11. package/dist/builtin/web-access/package.json +1 -1
  12. package/dist/builtin/workflows/CHANGELOG.md +41 -1
  13. package/dist/builtin/workflows/README.md +6 -5
  14. package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +13 -5
  15. package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +376 -89
  16. package/dist/builtin/workflows/builtin/adversarial-verification.d.ts +30 -6
  17. package/dist/builtin/workflows/builtin/adversarial-verification.ts +14 -9
  18. package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +26 -3
  19. package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +18 -14
  20. package/dist/builtin/workflows/builtin/goal-artifacts.ts +9 -8
  21. package/dist/builtin/workflows/builtin/goal-convergence.ts +87 -0
  22. package/dist/builtin/workflows/builtin/goal-ledger.ts +4 -0
  23. package/dist/builtin/workflows/builtin/goal-prompts.ts +2 -0
  24. package/dist/builtin/workflows/builtin/goal-reducer.ts +6 -1
  25. package/dist/builtin/workflows/builtin/goal-reverify.ts +305 -0
  26. package/dist/builtin/workflows/builtin/goal-runner.ts +75 -10
  27. package/dist/builtin/workflows/builtin/goal-schemas.ts +7 -0
  28. package/dist/builtin/workflows/builtin/goal-types.ts +6 -0
  29. package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +94 -6
  30. package/dist/builtin/workflows/builtin/loop-until-done.d.ts +8 -0
  31. package/dist/builtin/workflows/builtin/loop-until-done.ts +15 -0
  32. package/dist/builtin/workflows/builtin/progress-scoring.ts +230 -0
  33. package/dist/builtin/workflows/builtin/ralph-core.ts +11 -0
  34. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +1 -0
  35. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +2 -0
  36. package/dist/builtin/workflows/builtin/ralph-runner.ts +60 -10
  37. package/dist/builtin/workflows/builtin/selection-math.ts +156 -0
  38. package/dist/builtin/workflows/builtin/shared-prompts.ts +5 -0
  39. package/dist/builtin/workflows/builtin/tournament-prompts.ts +57 -75
  40. package/dist/builtin/workflows/builtin/tournament-runner.ts +384 -178
  41. package/dist/builtin/workflows/builtin/tournament.d.ts +46 -17
  42. package/dist/builtin/workflows/builtin/tournament.ts +66 -32
  43. package/dist/builtin/workflows/builtin/verification-criteria.ts +330 -0
  44. package/dist/builtin/workflows/builtin/verification-prompts.ts +206 -0
  45. package/dist/builtin/workflows/builtin/verification-usage.ts +44 -0
  46. package/dist/builtin/workflows/package.json +1 -1
  47. package/dist/builtin/workflows/skills/create-spec/SKILL.md +90 -30
  48. package/dist/builtin/workflows/skills/show-me/LICENSE.txt +21 -0
  49. package/dist/builtin/workflows/skills/show-me/SKILL.md +143 -0
  50. package/dist/builtin/workflows/src/authoring/workflow.ts +8 -0
  51. package/dist/builtin/workflows/src/authoring.d.ts +1 -1
  52. package/dist/builtin/workflows/src/durable/completed-catalog.ts +5 -2
  53. package/dist/builtin/workflows/src/durable/dbos-envelope.ts +1 -1
  54. package/dist/builtin/workflows/src/durable/resume-eligibility.ts +5 -3
  55. package/dist/builtin/workflows/src/durable/run-timing.ts +41 -10
  56. package/dist/builtin/workflows/src/durable/tool-primitive.ts +24 -2
  57. package/dist/builtin/workflows/src/engine/options.ts +1 -0
  58. package/dist/builtin/workflows/src/engine/primitives/workflow.ts +12 -3
  59. package/dist/builtin/workflows/src/engine/run-budget.ts +308 -0
  60. package/dist/builtin/workflows/src/engine/run-returned-status.ts +8 -0
  61. package/dist/builtin/workflows/src/engine/run-tool-node-lifecycle.ts +6 -0
  62. package/dist/builtin/workflows/src/engine/run.ts +124 -2
  63. package/dist/builtin/workflows/src/engine/runtime.ts +9 -0
  64. package/dist/builtin/workflows/src/extension/config-file-loader.ts +6 -0
  65. package/dist/builtin/workflows/src/extension/config-loader.ts +24 -1
  66. package/dist/builtin/workflows/src/extension/dispatcher.ts +6 -5
  67. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +2 -0
  68. package/dist/builtin/workflows/src/extension/index.bundle.mjs +2975 -843
  69. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +51 -4
  70. package/dist/builtin/workflows/src/extension/public-types.ts +3 -1
  71. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +7 -1
  72. package/dist/builtin/workflows/src/extension/runtime.ts +22 -10
  73. package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +5 -0
  74. package/dist/builtin/workflows/src/extension/workflow-prompts.ts +1 -0
  75. package/dist/builtin/workflows/src/extension/workflow-schema.ts +16 -0
  76. package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +44 -1
  77. package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +10 -1
  78. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +21 -9
  79. package/dist/builtin/workflows/src/runs/foreground/executor-continuation.ts +14 -0
  80. package/dist/builtin/workflows/src/runs/foreground/executor-lifecycle.ts +15 -4
  81. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +62 -5
  82. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +4 -0
  83. package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +2 -0
  84. package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +3 -1
  85. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +10 -1
  86. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +1 -0
  87. package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +1 -0
  88. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +7 -0
  89. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +7 -0
  90. package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +1 -0
  91. package/dist/builtin/workflows/src/shared/budget-meter.ts +34 -0
  92. package/dist/builtin/workflows/src/shared/budget.d.ts +67 -0
  93. package/dist/builtin/workflows/src/shared/budget.ts +127 -0
  94. package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +92 -8
  95. package/dist/builtin/workflows/src/shared/persistence-restore.ts +11 -1
  96. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +15 -3
  97. package/dist/builtin/workflows/src/shared/returned-run-status.ts +35 -2
  98. package/dist/builtin/workflows/src/shared/store-public-types.ts +4 -1
  99. package/dist/builtin/workflows/src/shared/store-run-methods.ts +8 -1
  100. package/dist/builtin/workflows/src/shared/store-stage-methods.ts +1 -0
  101. package/dist/builtin/workflows/src/shared/store-types.ts +24 -0
  102. package/dist/builtin/workflows/src/shared/types.ts +3 -0
  103. package/dist/builtin/workflows/src/shared/workflow-artifacts.ts +1 -0
  104. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +3 -0
  105. package/dist/builtin/workflows/src/shared/workflow-authoring-types.ts +3 -0
  106. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  107. package/dist/core/atomic-guide-command.js +1 -0
  108. package/dist/core/atomic-guide-command.js.map +1 -1
  109. package/dist/core/extensions/ui-types.d.ts +13 -3
  110. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  111. package/dist/core/extensions/ui-types.js +15 -3
  112. package/dist/core/extensions/ui-types.js.map +1 -1
  113. package/dist/core/slash-commands.d.ts.map +1 -1
  114. package/dist/core/slash-commands.js +33 -3
  115. package/dist/core/slash-commands.js.map +1 -1
  116. package/dist/main-deferred-startup.d.ts.map +1 -1
  117. package/dist/main-deferred-startup.js +6 -2
  118. package/dist/main-deferred-startup.js.map +1 -1
  119. package/dist/modes/interactive/interactive-startup.js +4 -0
  120. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  121. package/dist/modes/interactive/interactive-tui.d.ts.map +1 -1
  122. package/dist/modes/interactive/interactive-tui.js +19 -1
  123. package/dist/modes/interactive/interactive-tui.js.map +1 -1
  124. package/dist/modes/interactive-engine/isolated-runtime.d.ts +7 -0
  125. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  126. package/dist/modes/interactive-engine/isolated-runtime.js +94 -37
  127. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  128. package/dist/modes/rpc/rpc-client.d.ts +1 -0
  129. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  130. package/dist/modes/rpc/rpc-client.js +15 -2
  131. package/dist/modes/rpc/rpc-client.js.map +1 -1
  132. package/dist/modes/rpc/rpc-input-scheduler.d.ts +3 -2
  133. package/dist/modes/rpc/rpc-input-scheduler.d.ts.map +1 -1
  134. package/dist/modes/rpc/rpc-input-scheduler.js +5 -2
  135. package/dist/modes/rpc/rpc-input-scheduler.js.map +1 -1
  136. package/docs/extensions.md +1 -1
  137. package/docs/quickstart.md +1 -0
  138. package/docs/skills.md +4 -0
  139. package/docs/workflows.md +74 -10
  140. package/npm-shrinkwrap.json +29 -29
  141. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.14-alpha.5] - 2026-08-19
6
+
7
+ ### Added
8
+
9
+ - Added HumanLayer's bundled `show-me` skill for visual explanations, diagrams, code-shape sketches, and focused HTML artifacts. Sourced from https://github.com/humanlayer/skills and distributed under the MIT License.
10
+
11
+ ### Fixed
12
+
13
+ - Interactive startup now exits quietly when Ctrl+C stops the engine during first-paint binding, without leaking expected in-flight RPC transport failures.
14
+ - Bare `/` and slash-command drafts typed while explicitly loaded extension and workflow packages finish loading now remain in the interactive editor until Enter is pressed.
15
+ - Queue-control RPC frames now remain reachable during long-running prompts. When all overlapping remote `clear_queue` calls fail, Atomic restores the pre-clear host queue only if no later `queue_update` has supplied engine truth, including an empty queue ([#2516](https://github.com/bastani-inc/atomic/issues/2516)).
16
+ - The `ask_user_question` dialog no longer draws one preview row at the wrong horizontal offset. In side-by-side layout the preview box border landed 11-13 columns early on the row carrying the `❯` cursor, cutting into the option label, and the damage followed the cursor between options. The active-row mark that caused it now terminates its APC string with ST as ECMA-48 requires instead of BEL, and both interactive renderers strip the mark centrally before painting, so it no longer reaches the terminal from any host — including workflow stage chats and other custom-UI mounts that do not go through a reserving bottom overlay.
17
+
5
18
  ## [0.9.14-alpha.4] - 2026-08-18
6
19
 
7
20
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.9.14-alpha.4",
3
+ "version": "0.9.14-alpha.5",
4
4
  "private": true,
5
5
  "description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
6
6
  "contributors": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/mcp",
3
- "version": "0.9.14-alpha.4",
3
+ "version": "0.9.14-alpha.5",
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,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.14-alpha.5] - 2026-08-19
6
+
7
+ ### Added
8
+
9
+ - Added pure progress-trend evidence and injected control score-series support that can raise wall-clock attention priority without creating failure states, suppressing signals, or scheduling model calls ([#2489](https://github.com/bastani-inc/atomic/issues/2489)).
10
+
5
11
  ## [0.9.14-alpha.3] - 2026-08-17
6
12
 
7
13
  ### Changed
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/subagents",
3
- "version": "0.9.14-alpha.4",
3
+ "version": "0.9.14-alpha.5",
4
4
  "private": true,
5
5
  "description": "Atomic extension for delegating tasks to subagents with parallel execution. Fork of: https://github.com/nicobailon/pi-subagents",
6
6
  "contributors": [
@@ -93,6 +93,11 @@ const ControlOverrides = Type.Object({
93
93
  description: "Consecutive mutating-tool failures before escalating to needs_attention (default: 3)",
94
94
  }),
95
95
  ),
96
+ progressScores: Type.Optional(
97
+ Type.Array(Type.Number(), {
98
+ description: "Optional injected progress scores used only to raise attention priority; never a run outcome.",
99
+ }),
100
+ ),
96
101
  notifyOn: Type.Optional(
97
102
  Type.Array(Type.String({ enum: ["active_long_running", "needs_attention"] }), {
98
103
  description:
@@ -1,4 +1,5 @@
1
1
  import type { ResolvedControlConfig } from "../../shared/types.js";
2
+ import { progressAwareThreshold } from "./progress-trend.js";
2
3
 
3
4
  interface LongRunningNoticeMetrics {
4
5
  startedAt: number;
@@ -121,7 +122,8 @@ export function nextLongRunningTrigger(
121
122
  config: ResolvedControlConfig,
122
123
  metrics: LongRunningNoticeMetrics,
123
124
  ): LongRunningTriggerReason | undefined {
124
- if (metrics.now - metrics.startedAt >= config.activeNoticeAfterMs) return "time_threshold";
125
+ const activeNoticeAfterMs = progressAwareThreshold(config.activeNoticeAfterMs, config.progressScores);
126
+ if (metrics.now - metrics.startedAt >= activeNoticeAfterMs) return "time_threshold";
125
127
  if (config.activeNoticeAfterTurns !== undefined && metrics.turns >= config.activeNoticeAfterTurns)
126
128
  return "turn_threshold";
127
129
  if (config.activeNoticeAfterTokens !== undefined && metrics.tokens >= config.activeNoticeAfterTokens)
@@ -0,0 +1,69 @@
1
+ /** Pure progress-trend evidence shared by subagent control decisions. */
2
+
3
+ export const DEFAULT_TREND_WINDOW = 3;
4
+ export const DEFAULT_RISE_DELTA = 1.5;
5
+ export const DEFAULT_FALL_DELTA = -1.5;
6
+ export const FLAT_LOW_SCORE_CEILING = 8;
7
+ export const PROGRESS_ATTENTION_THRESHOLD_FRACTION = 0.5;
8
+
9
+ export type TrendConfig = {
10
+ window?: number;
11
+ riseDelta?: number;
12
+ fallDelta?: number;
13
+ };
14
+
15
+ export type Trend = "rising" | "flat" | "regressing";
16
+
17
+ export type TrendResult = {
18
+ trend: Trend;
19
+ evidence: {
20
+ series: readonly number[];
21
+ window: number;
22
+ delta: number;
23
+ };
24
+ };
25
+
26
+ function mean(values: readonly number[]): number {
27
+ if (values.length === 0) return 0;
28
+ return values.reduce((total, value) => total + value, 0) / values.length;
29
+ }
30
+
31
+ /**
32
+ * Classify a score series using the same deterministic hysteresis as workflows.
33
+ * The result is evidence only: it carries no run action or terminal status.
34
+ */
35
+ export function classify_trend(series: readonly number[], config: TrendConfig = {}): TrendResult {
36
+ const window = config.window ?? DEFAULT_TREND_WINDOW;
37
+ const riseDelta = config.riseDelta ?? DEFAULT_RISE_DELTA;
38
+ const fallDelta = config.fallDelta ?? DEFAULT_FALL_DELTA;
39
+ if (series.length < window + 1) {
40
+ return { trend: "flat", evidence: { series, window, delta: 0 } };
41
+ }
42
+ const sample = series.slice(-2 * window);
43
+ const usableLength = sample.length % 2 === 1 ? sample.length - 1 : sample.length;
44
+ const halfLength = usableLength / 2;
45
+ const leading = sample.slice(0, halfLength);
46
+ const trailing = sample.slice(sample.length - halfLength);
47
+ const delta = halfLength === 0 ? 0 : mean(trailing) - mean(leading);
48
+ const trend: Trend = delta >= riseDelta ? "rising" : delta <= fallDelta ? "regressing" : "flat";
49
+ return { trend, evidence: { series, window, delta } };
50
+ }
51
+
52
+ function hasFiniteProgressScores(series: readonly number[] | undefined): series is readonly number[] {
53
+ return series !== undefined && series.length > 0 && series.every((score) => Number.isFinite(score));
54
+ }
55
+
56
+ export function hasProgressAttentionSignal(series: readonly number[] | undefined): boolean {
57
+ if (!hasFiniteProgressScores(series)) return false;
58
+ const result = classify_trend(series);
59
+ const latest = series.at(-1);
60
+ return (
61
+ result.trend === "regressing" ||
62
+ (result.trend === "flat" && latest !== undefined && latest <= FLAT_LOW_SCORE_CEILING)
63
+ );
64
+ }
65
+
66
+ /** Lower a wall-clock threshold only when progress evidence raises priority. */
67
+ export function progressAwareThreshold(baseMs: number, series: readonly number[] | undefined): number {
68
+ return hasProgressAttentionSignal(series) ? Math.max(1, baseMs * PROGRESS_ATTENTION_THRESHOLD_FRACTION) : baseMs;
69
+ }
@@ -6,6 +6,7 @@ import type {
6
6
  ControlNotificationChannel,
7
7
  ResolvedControlConfig,
8
8
  } from "../../shared/types.js";
9
+ import { progressAwareThreshold } from "./progress-trend.js";
9
10
 
10
11
  const CONTROL_EVENT_TYPES: ControlEventType[] = ["active_long_running", "needs_attention"];
11
12
  const CONTROL_NOTIFICATION_CHANNELS: ControlNotificationChannel[] = ["event", "intercom"];
@@ -34,6 +35,12 @@ function parseControlList<T extends string>(value: unknown, allowed: readonly T[
34
35
  return parsed.length > 0 ? Array.from(new Set(parsed)) : undefined;
35
36
  }
36
37
 
38
+ function parseProgressScores(value: unknown): number[] | undefined {
39
+ if (!Array.isArray(value)) return undefined;
40
+ const parsed = value.filter((entry): entry is number => typeof entry === "number" && Number.isFinite(entry));
41
+ return parsed.length > 0 || value.length === 0 ? parsed : undefined;
42
+ }
43
+
37
44
  export function resolveControlConfig(globalConfig?: ControlConfig, override?: ControlConfig): ResolvedControlConfig {
38
45
  const enabled = override?.enabled ?? globalConfig?.enabled ?? DEFAULT_CONTROL_CONFIG.enabled;
39
46
  const needsAttentionAfterMs =
@@ -52,6 +59,8 @@ export function resolveControlConfig(globalConfig?: ControlConfig, override?: Co
52
59
  parsePositiveInt(override?.failedToolAttemptsBeforeAttention) ??
53
60
  parsePositiveInt(globalConfig?.failedToolAttemptsBeforeAttention) ??
54
61
  DEFAULT_CONTROL_CONFIG.failedToolAttemptsBeforeAttention;
62
+ const progressScores =
63
+ parseProgressScores(override?.progressScores) ?? parseProgressScores(globalConfig?.progressScores);
55
64
  const notifyOn =
56
65
  parseControlList(override?.notifyOn, CONTROL_EVENT_TYPES) ??
57
66
  parseControlList(globalConfig?.notifyOn, CONTROL_EVENT_TYPES) ??
@@ -67,6 +76,7 @@ export function resolveControlConfig(globalConfig?: ControlConfig, override?: Co
67
76
  activeNoticeAfterTurns,
68
77
  activeNoticeAfterTokens,
69
78
  failedToolAttemptsBeforeAttention,
79
+ ...(progressScores === undefined ? {} : { progressScores: [...progressScores] }),
70
80
  notifyOn: [...notifyOn],
71
81
  notifyChannels: [...notifyChannels],
72
82
  };
@@ -82,7 +92,8 @@ export function deriveActivityState(input: {
82
92
  const now = input.now ?? Date.now();
83
93
  const lastActivity = input.lastActivityAt ?? input.startedAt;
84
94
  const ageMs = Math.max(0, now - lastActivity);
85
- return ageMs > input.config.needsAttentionAfterMs ? "needs_attention" : undefined;
95
+ const attentionThreshold = progressAwareThreshold(input.config.needsAttentionAfterMs, input.config.progressScores);
96
+ return ageMs > attentionThreshold ? "needs_attention" : undefined;
86
97
  }
87
98
 
88
99
  export function buildControlEvent(input: {
@@ -55,6 +55,7 @@ export interface ControlConfig {
55
55
  activeNoticeAfterTurns?: number;
56
56
  activeNoticeAfterTokens?: number;
57
57
  failedToolAttemptsBeforeAttention?: number;
58
+ progressScores?: number[];
58
59
  notifyOn?: ControlEventType[];
59
60
  notifyChannels?: ControlNotificationChannel[];
60
61
  }
@@ -66,6 +67,7 @@ export interface ResolvedControlConfig {
66
67
  activeNoticeAfterTurns?: number;
67
68
  activeNoticeAfterTokens?: number;
68
69
  failedToolAttemptsBeforeAttention: number;
70
+ progressScores?: number[];
69
71
  notifyOn: ControlEventType[];
70
72
  notifyChannels: ControlNotificationChannel[];
71
73
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.9.14-alpha.4",
3
+ "version": "0.9.14-alpha.5",
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,11 +6,51 @@ 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.14-alpha.4] - 2026-08-18
9
+ ## [0.9.14-alpha.5] - 2026-08-19
10
+
11
+ ### Added
12
+
13
+ - Added optional run-budget declarations on workflow config, authored definitions, and `workflow` tool runs or resumes. `maxDurationMs`, `maxTokens`, `maxCost`, and `warnAtPercent` resolve per field with run overrides taking precedence over definition and config values; `0` disables a dimension. Invalid declarations now fail validation, and `budget_exceeded` is reserved as a resumable returned blocked status; these declarations provide the resolution core for the duration enforcement below ([#2212](https://github.com/bastani-inc/atomic/issues/2212)).
14
+ - Added duration enforcement for workflow budgets at stage and durable-tool boundaries. Runs emit one user-visible `budget_warning` lifecycle notice at the default 80% threshold or configured threshold, receive one wrap-up turn only when a frontier stage turn is already live, and stop on a resumable `budget_exceeded` blocked rail with a duration report; no new stage is created for wrap-up, no-live-turn boundaries leave the allowance unused, paused time is excluded, elapsed time carries across resume, a raised resume budget continues prior spend, the wrap-up turn's own usage is recorded as `wrapUpUsage` when available without tree-wide token/cost aggregation, root duration scope is enforced inside child workflows and wins simultaneous child exhaustion, and child-scoped exhaustion returns to the parent without stopping it ([#2212](https://github.com/bastani-inc/atomic/issues/2212)).
15
+ - Added tree-wide token and cost budget metering across nested workflow runs and stage retries. Uncached input and output tokens feed `maxTokens`, cache reads and writes remain reported counters, `usage.cost` feeds `maxCost`, and persisted accounting baselines prevent duplicate charges across boundaries and resume ([#2212](https://github.com/bastani-inc/atomic/issues/2212)).
16
+ - Added HumanLayer's bundled `show-me` skill for visual explanations, diagrams, code-shape sketches, and focused HTML artifacts. Sourced from https://github.com/humanlayer/skills and distributed under the MIT License.
17
+ - Updated the bundled `create-spec` skill to choose context-driven Path A or clarify-first Path B, inspect local precedent before design decisions, compare materially different alternatives, and use show-me shape-first visuals (pseudocode, call trees, component/file trees, Mermaid, diffs, and optional focused HTML). Working-method guidance is drawn from Dillon Mulroy's [tech-spec skill](https://github.com/dmmulroy/skills/blob/main/tech-spec/SKILL.md).
18
+ ### Breaking Changes
19
+
20
+ - `adversarial-verification` now accepts optional per-criterion `criteria`, `accept_mean`, and bounded `reask_limit` inputs, and returns deterministic graded mean/veto results through `mean_score` and `score_table_path`; callers must migrate from the removed `result`, `verifier_artifact_paths`, and `artifact_dir` outputs. Invalid verifier reports are recorded as invalid markers and re-asked instead of becoming fail votes ([#2487](https://github.com/bastani-inc/atomic/issues/2487)).
21
+ - Replaced `bracket_path`/`bracket.json` and the knockout tournament schedule with `comparisons_path`/`comparisons.json` on the soft-scored pivot-pairing schedule. Judge stages now use deterministic `judge-<a>-<b>-<criterion>-r<rep>` identities, and the reducer stage is named `comparisons-reducer`.
22
+ - Replaced `bracket_path`/`bracket.json` and the knockout tournament schedule with `comparisons_path`/`comparisons.json` on the soft-scored pivot-pairing schedule. Judge stages now use deterministic `judge-<a>-<b>-<criterion>-r<rep>` identities, and the reducer stage is named `comparisons-reducer`.
23
+ - `adversarial-verification` now accepts optional per-criterion `criteria`, `accept_mean`, and bounded `reask_limit` inputs, and returns deterministic graded mean/veto results through `mean_score` and `score_table_path`; callers must migrate from the removed `result`, `verifier_artifact_paths`, and `artifact_dir` outputs. Invalid verifier reports are recorded as invalid markers and re-asked instead of becoming fail votes ([#2487](https://github.com/bastani-inc/atomic/issues/2487)).
24
+
25
+ ### Added
26
+
27
+ - Added a shared `verification-criteria` module (`parse_rubric`, `normalize_criteria`, `select_criteria`, `VERIFICATION_SCALE`, `decide_verification`) so verification builtins can score one criterion at a time on an anchored 1–20 scale and accept only when a quorum mean clears the threshold with no veto finding. Unparseable reports cannot become scores and cannot shift the mean ([#2487](https://github.com/bastani-inc/atomic/issues/2487)).
28
+ - Added prefix-cache-aware verification prompts with a byte-identical shared head, UTF-8 bounded candidate inlining with whole-family read fallback, and warm-first verifier fan-out scheduling that preserves input order while releasing later phases after warm failures ([#2493](https://github.com/bastani-inc/atomic/issues/2493)).
29
+ - Added optional run-budget declarations on workflow config, authored definitions, and `workflow` tool runs or resumes. `maxDurationMs`, `maxTokens`, `maxCost`, and `warnAtPercent` resolve per field with run overrides taking precedence over definition and config values; `0` disables a dimension. Invalid declarations now fail validation, and `budget_exceeded` is reserved as a resumable returned blocked status; these declarations provide the resolution core for the duration enforcement below ([#2212](https://github.com/bastani-inc/atomic/issues/2212)).
30
+ - Added duration enforcement for workflow budgets at stage and durable-tool boundaries. Runs emit one user-visible `budget_warning` lifecycle notice at the default 80% threshold or configured threshold, receive one wrap-up turn only when a frontier stage turn is already live, and stop on a resumable `budget_exceeded` blocked rail with a duration report; no new stage is created for wrap-up, no-live-turn boundaries leave the allowance unused, paused time is excluded, elapsed time carries across resume, a raised resume budget continues prior spend, the wrap-up turn's own usage is recorded as `wrapUpUsage` when available without tree-wide token/cost aggregation, root duration scope is enforced inside child workflows and wins simultaneous child exhaustion, and child-scoped exhaustion returns to the parent without stopping it ([#2212](https://github.com/bastani-inc/atomic/issues/2212)).
31
+ - Added optional run-budget declarations on workflow config, authored definitions, and `workflow` tool runs. `maxDurationMs`, `maxTokens`, `maxCost`, and `warnAtPercent` resolve per field with run overrides taking precedence over definition and config values; `0` disables a dimension. Invalid declarations now fail validation, and `budget_exceeded` is reserved as a resumable returned blocked status. This core slice only resolves and validates budgets; it does not meter or stop runs yet ([#2212](https://github.com/bastani-inc/atomic/issues/2212)).
32
+ - Added a shared `verification-criteria` module (`parse_rubric`, `normalize_criteria`, `select_criteria`, `VERIFICATION_SCALE`, `decide_verification`) so verification builtins can score one criterion at a time on an anchored 1–20 scale and accept only when a quorum mean clears the threshold with no veto finding. Unparseable reports cannot become scores and cannot shift the mean ([#2487](https://github.com/bastani-inc/atomic/issues/2487)).
33
+ - Added prefix-cache-aware verification prompts with a byte-identical shared head, UTF-8 bounded candidate inlining with whole-family read fallback, and warm-first verifier fan-out scheduling that preserves input order while releasing later phases after warm failures ([#2493](https://github.com/bastani-inc/atomic/issues/2493)).
34
+ - Added Goal and Ralph review-round re-verification for low-confidence, single-reviewer blocking findings, using fresh 1–20 scoring stages, two-tier demotion bars, and durable per-repeat audit evidence while leaving `stop_review_loop` authoritative.
35
+ - Added pure, seeded selection math for probabilistic pivot tournaments: deterministic Hamiltonian ring and deduplicated pivot planning, criterion/repeat slot-swap jobs, normalized Bradley–Terry soft wins, count-normalized pivot selection, and complete candidate rankings ([#2488](https://github.com/bastani-inc/atomic/issues/2488)).
36
+ - Added tournament `n_evaluations`, `pivots`, `seed`, `criteria`, and heterogeneous `models` inputs, plus `ranking` and `seed` outputs and the auditable `comparisons.json` score ledger with invalid-report and budget records ([#2488](https://github.com/bastani-inc/atomic/issues/2488)).
37
+ - `adversarial-verification` now fans out one schema-validated 1–20 score for every criterion/verifier pair, applies mean-plus-veto acceptance, bounds invalid-report re-asks, and consolidates confirmed findings into repair guidance ([#2487](https://github.com/bastani-inc/atomic/issues/2487)).
38
+ - Added pure `fold_usage` accounting over every model attempt (including retries), returning `UsageTotals { calls, input, output, cacheRead, cacheWrite, cost, turns, cacheHitRate }` with a derived cache-hit rate; tournament comparisons and adversarial verification round summaries now record per-phase and total usage blocks ([#2494](https://github.com/bastani-inc/atomic/issues/2494)).
39
+ - Added observation-grounded progress scoring with batched checkpoint repeats, null-safe invalid-repeat handling, and a pure hysteretic trend classifier that reports evidence without an action ([#2489](https://github.com/bastani-inc/atomic/issues/2489)).
40
+ - Added Goal and Ralph per-round convergence evidence series with V7 trend classification, folded usage totals, and escalation-only `needs_human` evidence that never approves or terminates a review loop ([#2490](https://github.com/bastani-inc/atomic/issues/2490)).
41
+ - Added reviewer calibration rules that prioritize observed output over agent narration, plus audit-only 1–20 `criterion_scores` in Goal and Ralph review decisions without changing the authoritative closure gate ([#2494](https://github.com/bastani-inc/atomic/issues/2494)).
42
+ - Added loop-until-done progress consumers that record per-iteration advisory score curves, final trends, and calibration disclaimers in completion and exhaustion reports without changing stop decisions ([#2489](https://github.com/bastani-inc/atomic/issues/2489)).
43
+ - Added observation-grounded progress scoring with batched checkpoint repeats, null-safe invalid-repeat handling, and a pure hysteretic trend classifier that reports evidence without an action ([#2489](https://github.com/bastani-inc/atomic/issues/2489)).
44
+ - Added loop-until-done progress consumers that record per-iteration advisory score curves, final trends, and calibration disclaimers in completion and exhaustion reports without changing stop decisions ([#2489](https://github.com/bastani-inc/atomic/issues/2489)).
45
+ - Added pure, seeded selection math for probabilistic pivot tournaments: deterministic Hamiltonian ring and deduplicated pivot planning, criterion/repeat slot-swap jobs, normalized Bradley–Terry soft wins, count-normalized pivot selection, and complete candidate rankings ([#2488](https://github.com/bastani-inc/atomic/issues/2488)).
46
+ - Added tournament `n_evaluations`, `pivots`, `seed`, `criteria`, and heterogeneous `models` inputs, plus `ranking` and `seed` outputs and the auditable `comparisons.json` score ledger with invalid-report and budget records ([#2488](https://github.com/bastani-inc/atomic/issues/2488)).
10
47
 
11
48
  ### Fixed
12
49
 
13
50
  - Opened `ctx.tool` detail now matches the main-chat tool block: `Box(1,1)` inner padding, a blank row under the `$` header, full-width alignment with the orchestrator bars, unpainted canvas above and below the card, host `toolTitle`/`toolOutput` colors, a dim `ctrl+o Expand` hint, and second-resolution `Took`/`Elapsed` timing.
51
+ - Tournament criteria now accepts every documented V1 shape, including markdown rubrics, records, string lists, and CriterionInput objects with optional ids and names.
52
+ - Criterion ids are preserved in the ledger while judge artifact filenames are sanitized, collision-free, and contained under the artifact directory.
53
+ - Corrected the planned comparison budget for oversized pivot counts and recorded each comparison row's own soft preference separately from pair aggregates.
14
54
 
15
55
  ## [0.9.14-alpha.3] - 2026-08-17
16
56
 
@@ -677,10 +677,11 @@ Raw stage-chat prompt answer replay is live-memory only. `StageSnapshot.promptAn
677
677
  ```json
678
678
  {
679
679
  "name": "workflow",
680
- "description": "Run named builtin, project, user, or package workflows; custom definitions may import reusable project/package workflows or builtin definitions from @bastani/workflows/builtin and nest them with ctx.workflow(...), including deeper composition within the configured maxDepth; when workflow execution fits but another shape would better achieve the task, author a custom TypeScript workflow({...}) inline with normal coding tools, reload it, and run it; after successfully creating and reloading a newly authored custom workflow, report the folder containing its generated code as 'Custom workflow created. You can inspect its code at: <workflow-folder-path>'; do this only for newly created custom workflows, never builtin or pre-existing workflows; discover with list/get/inputs/models, list session runs with status (no runId; statusFilter narrows the list), inspect status/stages/stage details, send prompt answers or steering only while the root workflow is nonterminal, pause/resume/interrupt/quit runs, and reload workflow resources. For primitive prompt answers, use booleans or the documented confirm labels, exact case-insensitive select labels or 1-based indexes, and text strings for input/editor; an invalid answer remains pending and returns guidance instead of choosing a default. For large stage handoffs, write context to files/artifacts, pass paths via reads, and prompt downstream agents to 'Read the file at <path>...' instead of injecting large previous text. Wrap critical parts of run inputs and steering messages in <keepContext>...</keepContext> so compaction preserves them verbatim in the stages that inherit them; tag role constraints, prohibitions, must-hold criteria, and identifiers, not background or bulk reference material. For transcripts, prefer status/stages/stage to get sessionFile/transcriptPath, quote the exact path without rewriting separators (Windows backslashes are valid), then search it with rg/grep and read small ranges; transcript is path-only by default when sessionFile/transcriptPath exists, explicit tail/limit returns bounded previews, and missing transcript paths fall back to a small preview. Use action 'models' to inspect models in the configured catalog; the result is a configured-auth snapshot showing what's present in the registry with configured authentication, not proof of credentials, entitlements, OAuth freshness, or live provider access. When authoring a workflow that should dynamically select a model, first call workflow({ action: 'models' }) to inspect the configured catalog, then select from the returned provider/id entries considering the isCurrent marker and available thinking levels.",
680
+ "description": "Run named builtin, project, user, or package workflows; custom definitions may import reusable project/package workflows or builtin definitions from @bastani/workflows/builtin and nest them with ctx.workflow(...), including deeper composition within the configured maxDepth; when workflow execution fits but another shape would better achieve the task, author a custom TypeScript workflow({...}) inline with normal coding tools, reload it, and run it; after successfully creating and reloading a newly authored custom workflow, report the folder containing its generated code as 'Custom workflow created. You can inspect its code at: <workflow-folder-path>'; do this only for newly created custom workflows, never builtin or pre-existing workflows; discover with list/get/inputs/models, list session runs with status (no runId; statusFilter narrows the list), inspect status/stages/stage details, send prompt answers or steering only while the root workflow is nonterminal, pause/resume/interrupt/quit runs, and reload workflow resources. For action 'run' and 'resume', budget accepts per-field duration, token, cost, and warning overrides; fields resolve over the workflow declaration and config, and 0 disables a field. For primitive prompt answers, use booleans or the documented confirm labels, exact case-insensitive select labels or 1-based indexes, and text strings for input/editor; an invalid answer remains pending and returns guidance instead of choosing a default. For large stage handoffs, write context to files/artifacts, pass paths via reads, and prompt downstream agents to 'Read the file at <path>...' instead of injecting large previous text. Wrap critical parts of run inputs and steering messages in <keepContext>...</keepContext> so compaction preserves them verbatim in the stages that inherit them; tag role constraints, prohibitions, must-hold criteria, and identifiers, not background or bulk reference material. For transcripts, prefer status/stages/stage to get sessionFile/transcriptPath, quote the exact path without rewriting separators (Windows backslashes are valid), then search it with rg/grep and read small ranges; transcript is path-only by default when sessionFile/transcriptPath exists, explicit tail/limit returns bounded previews, and missing transcript paths fall back to a small preview. Use action 'models' to inspect models in the configured catalog; the result is a configured-auth snapshot showing what's present in the registry with configured authentication, not proof of credentials, entitlements, OAuth freshness, or live provider access. When authoring a workflow that should dynamically select a model, first call workflow({ action: 'models' }) to inspect the configured catalog, then select from the returned provider/id entries considering the isCurrent marker and available thinking levels.",
681
681
  "parameters": {
682
682
  "workflow": "string (optional) — workflow ID or normalized name",
683
683
  "inputs": "object (optional) — key/value map of workflow inputs",
684
+ "budget": "optional run-only object: maxDurationMs/maxTokens non-negative integers; maxCost/warnAtPercent non-negative numbers; 0 disables a field",
684
685
  "action": "'run' | 'list' | 'get' | 'inputs' | 'models' | 'status' | 'stages' | 'stage' | 'transcript' | 'send' | 'pause' | 'interrupt' | 'quit' | 'resume' | 'reload'",
685
686
  "runId": "optional full 36-character run id; prefixes are rejected; control actions default to the active run where safe; use '--all' or all:true for pause/interrupt/quit all",
686
687
  "stageId": "optional exact stage id or exact stage name for stage-scoped actions; prefixes and partial names are rejected; cannot be combined with all:true",
@@ -772,10 +773,10 @@ The six common patterns ship as full builtins and are discoverable/runnable by n
772
773
  |---|---|---|---|
773
774
  | `classify-and-act` | structured classifier → deterministic action; HIL fallback for low confidence | `prompt`; `categories` (3 defaults), `confidence_threshold=0.75` | result, category/confidence, classification/action paths |
774
775
  | `fan-out-and-synthesize` | partition → bounded artifact fan-out → evidence synthesis barrier | `prompt`; `max_branches=4`, `max_concurrency=4` | result, partitions, branch/synthesis/manifest paths |
775
- | `adversarial-verification` | worker → fresh rubric verifiers → reducer → bounded repair | `task`; `verifier_count=3`, `max_repairs=2` | result, approval, repairs, candidate/review/verifier paths |
776
- | `generate-and-filter` | candidate fan-out → dedupe/filter → optional judge → shortlist | `prompt`; `num_candidates=8`, `shortlist_size=3`, `use_judge=true`, `max_concurrency=4` | result, shortlist and candidate/filter/judge/final/manifest paths |
777
- | `tournament` | independent attempts → order-balanced pairwise judges → bracket reducer | `prompt`; `num_attempts=4`, `max_concurrency=4` | result, winner, attempt/judge/bracket paths |
778
- | `loop-until-done` | durable ledger → iteration/evaluator loop → complete or inspectable exhaustion | `prompt`; `max_iterations=5` | result/status, ledger, iteration/evaluation paths, remaining work |
776
+ | `adversarial-verification` | worker → per-criterion fresh rubric verifiers → deterministic mean+veto reducer → findings consolidation / bounded repair; decompose rubrics into named criteria and aggregate by mean plus explicit veto, never a unanimity AND; `criteria.md` uses `## Criteria` with non-empty `### Name {#id}` bodies and the shared `verification-criteria` doors; consolidator cannot approve | `task`; `criteria` (record or criteria.md markdown, defaults to task_fit/evidence/completeness), `verifier_count=3` (1–5), `max_repairs=2` (0–5), `accept_mean=14`, `reask_limit=1`; invalid reports are re-asked within the bound and never become fail votes; normal calls per round: criteria.length × verifier_count | `approved`, `mean_score`, `score_table_path` (`verification-summary-<round>.json` with scores, mean, invalidCount, decision, and usage), `repairs_completed`, `candidate_path`, `review_report_path`, `remaining_work` |
777
+ | `generate-and-filter` | candidate fan-out → dedupe/filter → optional judge → shortlist; use graded per-criterion scores, Bradley–Terry preference from score gaps, and slot-swap repeats when ranking candidates; custom judges can reuse the shared `verification-criteria` module and `criteria.md` rubric format | `prompt`; `num_candidates=8`, `shortlist_size=3`, `use_judge=true`, `max_concurrency=4` | result, shortlist and candidate/filter/judge/final/manifest paths |
778
+ | `tournament` | independent attempts → seeded ring and pivot-round soft scoring with order-balanced pairwise judges → full ranking/comparisons reducer; derive a Bradley–Terry preference from graded score gaps and repeat with A/B slot swaps; `comparisons.json` preserves per-job score or invalid rows, pair aggregates, ranking, and planned/executed budget records | `prompt`; `num_attempts=4`, `max_concurrency=4`, `n_evaluations=2`, `pivots=1`, `seed=0`, optional `criteria` (markdown rubric, record, string list, or `CriterionInput` list)/`models` | result, winner, `attempt_artifact_paths`, `judge_artifact_paths`, `comparisons_path`, `ranking`, `seed` |
779
+ | `loop-until-done` | durable ledger → iteration/evaluator loop → complete or inspectable exhaustion; each scored iteration records `progress` (`score`, `perRepeat`, `trend`, `window`) and the ledger emits `progress_curve`, `final_trend`, and `progress_disclaimer`; trend is advisory and never a kill switch | `prompt`; `max_iterations=5`, `progress_scoring=true`, `progress_repeats=1` | result/status, ledger, iteration/evaluation paths, remaining work, `progress_curve`, `final_trend`, `progress_disclaimer` |
779
780
 
780
781
  All six are exported from `@bastani/workflows/builtin` as definitions (`classifyAndAct`, `fanOutAndSynthesize`, `adversarialVerification`, `generateAndFilter`, `tournament`, and `loopUntilDone`). Import and nest them through `ctx.workflow(definition, { inputs, stageName })`; nested calls respect `maxDepth`. Prefer composition over copying their prompts or graphs: children contribute their stages, dedicated prompts, gates, artifacts, HIL nodes, and declared outputs to the expanded parent graph.
781
782
 
@@ -1,3 +1,5 @@
1
+ import type { Criterion } from "./verification-criteria.js";
2
+
1
3
  const GROUNDED_REPORTING = "Before reporting progress, audit each claim against a tool result from this session. Report only work you can point to evidence for; say so explicitly when something is unverified.";
2
4
  const READABLE_REPORT = "Lead with the outcome. Keep facts, decisions, caveats, and next steps; drop background and repetition. Use complete, readable sentences rather than compressed fragments.";
3
5
 
@@ -5,14 +7,20 @@ export function renderWorkerPrompt(task: string): string {
5
7
  return `<role>\nYou produce a candidate solution for independent verification.\n</role>\n\n<success_criteria>\nThe task is complete and important claims have observable support. Preserve concrete evidence and state every validation performed.\n</success_criteria>\n\n<stop_rules>\nStop when the candidate is complete and validated where practical, or state the evidence still missing.\n</stop_rules>\n\n<output_format>\nA self-contained candidate with actions taken, evidence, validation, and remaining risks. ${READABLE_REPORT}\n${GROUNDED_REPORTING}\n</output_format>\n\n<objective>\n${task}\n</objective>`;
6
8
  }
7
9
 
8
- export function renderVerifierPrompt(task: string, candidatePath: string, rubricPath: string): string {
9
- return `<artifacts>\nRead the complete candidate at ${candidatePath} and rubric at ${rubricPath}.\n</artifacts>\n\n<role>\nYou are an independent adversarial verifier. Find blockers; do not rewrite the candidate.\n</role>\n\n<success_criteria>\nA pass requires concrete evidence for every rubric item; absence of evidence is not evidence of correctness.\n</success_criteria>\n\n<evidence_rules>\nTest important claims where practical. For each conclusion, report the command run, observed output, and file:line evidence where applicable. Report precise blocking findings. ${GROUNDED_REPORTING}\n</evidence_rules>\n\n<stop_rules>\nStop after every rubric item has pass evidence or a precise blocking finding.\n</stop_rules>\n\n<output_format>\nCall structured_output with verdict (pass or fail), concise evidence, and blocking_findings. ${READABLE_REPORT}\n</output_format>\n\n<objective>\nVerify the candidate against the task and rubric. Task: ${task}\n</objective>`;
10
+ export function renderVerifierPrompt(task: string, candidatePath: string, criteriaPath: string, criterion: Criterion): string {
11
+ return `<artifacts>\nRead the complete candidate at ${candidatePath} and resolved criteria at ${criteriaPath}.\n</artifacts>\n\n<role>\nYou are an independent adversarial verifier. Score exactly one criterion and do not rewrite the candidate.\n</role>\n\n<criterion>\nid: ${criterion.id}\nname: ${criterion.name}\ninstructions: ${criterion.description}\n</criterion>\n\n<scale>\nUse an integer score from 1 to 20: 1 = certainly fails 10 = borderline … 20 = verified correct.\n</scale>\n\n<evidence_rules>\nTest important claims where practical. Evidence must cite observable support; file findings cite file:line where applicable. Report precise findings and use severity veto only for a finding that unconditionally prevents acceptance. ${GROUNDED_REPORTING}\n</evidence_rules>\n\n<output_format>\nCall structured_output with exactly criterion_id (set to ${criterion.id}), score, evidence (string array), and findings (objects with finding and severity veto, blocking, or note). ${READABLE_REPORT}\n</output_format>\n\n<objective>\nVerify criterion ${criterion.id} for the candidate produced for: ${task}\n</objective>`;
10
12
  }
11
13
 
12
- export function renderReducerPrompt(task: string, candidatePath: string, verifierPaths: readonly string[], repairsCompleted: number, maxRepairs: number): string {
13
- return `<artifacts>\nCandidate: ${candidatePath}\nVerifier reports: ${verifierPaths.join(", ")}\n</artifacts>\n\n<role>\nYou reduce independent verification reports into one deterministic next action.\n</role>\n\n<decision_rules>\nAccept only when all material rubric requirements have pass evidence. Request repair when findings are actionable and repair budget remains (${repairsCompleted}/${maxRepairs}). Reject when evidence proves the candidate cannot satisfy the task or the repair budget is exhausted. Preserve unresolved blockers verbatim.\n</decision_rules>\n\n<success_criteria>\nThe decision follows the evidence and repair bound, with every unresolved blocker preserved.\n</success_criteria>\n\n<stop_rules>\nStop after selecting exactly one decision: accept, reject, or repair.\n</stop_rules>\n\n<output_format>\nCall structured_output with decision (accept, reject, or repair), a concise evidence-based rationale, and remaining_work. ${READABLE_REPORT}\n${GROUNDED_REPORTING}\n</output_format>\n\n<objective>\nDecide whether the candidate for ${task} is accepted, rejected, or needs repair.\n</objective>`;
14
+ export function renderConsolidatorPrompt(
15
+ task: string,
16
+ candidatePath: string,
17
+ scorePaths: readonly string[],
18
+ repairsCompleted: number,
19
+ maxRepairs: number,
20
+ ): string {
21
+ return `<artifacts>\nCandidate: ${candidatePath}\nConfirmed criterion score reports: ${scorePaths.join(", ")}\n</artifacts>\n\n<role>\nYou consolidate confirmed verifier findings into actionable repair guidance. You do not decide whether verification is approved; the deterministic verification gate already made that decision.\n</role>\n\n<decision_rules>\nPreserve every confirmed veto or blocking finding in remaining_work verbatim. Explain concrete repairs and validation steps. Repair budget: ${repairsCompleted}/${maxRepairs}.\n</decision_rules>\n\n<output_format>\nCall structured_output with repair_guidance (a concise actionable repair plan) and remaining_work (the unresolved findings as strings). Never emit an approval or rejection decision. ${READABLE_REPORT}\n${GROUNDED_REPORTING}\n</output_format>\n\n<objective>\nConsolidate findings for the candidate produced for: ${task}\n</objective>`;
14
22
  }
15
23
 
16
24
  export function renderRepairPrompt(task: string, candidatePath: string, reviewPath: string): string {
17
- return `<artifacts>\nRead the current candidate at ${candidatePath} and reducer report at ${reviewPath}.\n</artifacts>\n\n<role>\nYou repair a candidate using independent blocking findings.\n</role>\n\n<success_criteria>\nEvery actionable blocker is addressed, relevant validation is rerun, and valid prior work is retained.\n</success_criteria>\n\n<evidence_rules>\nDo not dismiss a finding without contrary evidence. Report commands run, observed output, and file:line evidence where applicable. ${GROUNDED_REPORTING}\n</evidence_rules>\n\n<stop_rules>\nStop when all actionable blockers are repaired and validated, or list any blocker that remains with the missing evidence.\n</stop_rules>\n\n<output_format>\nA complete replacement candidate with repair summary, evidence, validation, and remaining risks. ${READABLE_REPORT}\n</output_format>\n\n<objective>\nRepair the candidate for: ${task}\n</objective>`;
25
+ return `<artifacts>\nRead the current candidate at ${candidatePath} and consolidated findings at ${reviewPath}.\n</artifacts>\n\n<role>\nYou repair a candidate using independent blocking findings.\n</role>\n\n<success_criteria>\nEvery actionable blocker is addressed, relevant validation is rerun, and valid prior work is retained.\n</success_criteria>\n\n<evidence_rules>\nDo not dismiss a finding without contrary evidence. Report commands run, observed output, and file:line evidence where applicable. ${GROUNDED_REPORTING}\n</evidence_rules>\n\n<stop_rules>\nStop when all actionable blockers are repaired and validated, or list any blocker that remains with the missing evidence.\n</stop_rules>\n\n<output_format>\nA complete replacement candidate with repair summary, evidence, validation, and remaining risks. ${READABLE_REPORT}\n</output_format>\n\n<objective>\nRepair the candidate for: ${task}\n</objective>`;
18
26
  }