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

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 +47 -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
@@ -1,6 +1,7 @@
1
1
  import { mkdir, writeFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import { Type } from "typebox";
4
+ import { VERIFICATION_SCALE } from "./verification-criteria.js";
4
5
  import type { WorkflowTaskResult } from "../src/shared/types.js";
5
6
  import { createWorkflowArtifactDirectory } from "../src/shared/workflow-artifacts.js";
6
7
  import {
@@ -19,6 +20,8 @@ import {
19
20
  type ParsedReviewDecision,
20
21
  type ReviewConvergenceSummary,
21
22
  } from "./review-convergence.js";
23
+ import type { ConvergenceEntry } from "./goal-convergence.js";
24
+ import type { ReverifyAuditEntry } from "./goal-reverify.js";
22
25
 
23
26
 
24
27
  export const DEFAULT_MAX_LOOPS = 10;
@@ -104,6 +107,12 @@ export const reviewDecisionSchema = Type.Object(
104
107
  ]),
105
108
  overall_explanation: Type.String(),
106
109
  overall_confidence_score: Type.Number({ minimum: 0, maximum: 1 }),
110
+ criterion_scores: Type.Optional(
111
+ Type.Array(Type.Object({
112
+ criterion_id: Type.String(),
113
+ score: VERIFICATION_SCALE.schema,
114
+ }, { additionalProperties: false })),
115
+ ),
107
116
  requirements_traceability: Type.Array(requirementsTraceabilitySchema),
108
117
  stop_review_loop: Type.Boolean(),
109
118
  reviewer_error: Type.Optional(
@@ -294,7 +303,9 @@ type ReviewArtifact = {
294
303
 
295
304
  type ReviewRoundArtifact = {
296
305
  readonly convergence_decision: ReviewConvergenceSummary;
306
+ readonly convergence: readonly ConvergenceEntry[];
297
307
  readonly consolidated_findings?: readonly ConsolidatedFinding<ReviewFinding>[];
308
+ readonly reverification?: readonly ReverifyAuditEntry<ReviewFinding>[];
298
309
  readonly reviews: readonly {
299
310
  readonly reviewer: string;
300
311
  readonly artifact_path: string;
@@ -64,6 +64,7 @@ export type RequirementTraceability = {
64
64
 
65
65
  export type ReviewDecision = {
66
66
  readonly findings: readonly ReviewFinding[];
67
+ readonly criterion_scores?: readonly { readonly criterion_id: string; readonly score: number }[];
67
68
  readonly overall_correctness: "patch is correct" | "patch is incorrect";
68
69
  readonly overall_explanation: string;
69
70
  readonly overall_confidence_score: number;
@@ -7,6 +7,7 @@ import {
7
7
  REVIEW_CODE_DELTA_CONTRACT,
8
8
  REVIEWER_INDEPENDENT_VERIFICATION_CONTRACT,
9
9
  REVIEWER_INTERCOM_COORDINATION_PROTOCOL,
10
+ REVIEWER_CALIBRATION_RULES,
10
11
  REVIEWER_OVERIMPLEMENTATION_GUARD,
11
12
  REVIEWER_SPEC_VS_OBJECTIVE_GUARD,
12
13
  WORKTREE_DISCIPLINE_CONTRACT,
@@ -42,6 +43,7 @@ export function renderRalphReviewerPrompt(args: {
42
43
  ["literal_contract", LITERAL_OBJECTIVE_CONTRACT],
43
44
  ["acceptance_matrix", ACCEPTANCE_MATRIX_CONTRACT],
44
45
  ["independent_verification", REVIEWER_INDEPENDENT_VERIFICATION_CONTRACT],
46
+ ["calibration", REVIEWER_CALIBRATION_RULES],
45
47
  ["code_delta_review", REVIEW_CODE_DELTA_CONTRACT],
46
48
  ["worktree_discipline", WORKTREE_DISCIPLINE_CONTRACT],
47
49
  ["reviewer_coordination", REVIEWER_INTERCOM_COORDINATION_PROTOCOL],
@@ -1,6 +1,12 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { join, resolve } from "node:path";
3
- import type { WorkflowRunContext, WorkflowTaskResult } from "../src/shared/types.js";
3
+ import type { WorkflowRunContext, WorkflowTaskOptions, WorkflowTaskResult } from "../src/shared/types.js";
4
+ import { fold_usage } from "./verification-usage.js";
5
+ import {
6
+ convergence_escalation_evidence,
7
+ record_convergence,
8
+ type ConvergenceEntry,
9
+ } from "./goal-convergence.js";
4
10
  import { createWorkflowArtifactDirectory } from "../src/shared/workflow-artifacts.js";
5
11
  import {
6
12
  ACCEPTANCE_MATRIX_CONTRACT,
@@ -41,6 +47,7 @@ import {
41
47
  type RalphWorkflowResult,
42
48
  } from "./ralph-core.js";
43
49
  import { consolidateFindingsBatch, summarizeReviewConvergence } from "./review-convergence.js";
50
+ import { reverify_consolidated_batch } from "./goal-reverify.js";
44
51
  import {
45
52
  orchestratorModelConfig,
46
53
  promptEngineerModelConfig,
@@ -69,6 +76,7 @@ export async function runRalphWorkflow(
69
76
  let approved = false;
70
77
  let iterationsCompleted = 0;
71
78
  let previousResearchPromptRefinementSessionFile: string | undefined;
79
+ const convergenceEntries: ConvergenceEntry[] = [];
72
80
  let previousResearchSessionFile: string | undefined;
73
81
  let previousOrchestratorSessionFile: string | undefined;
74
82
  for (let iteration = 1; iteration <= maxLoops; iteration += 1) {
@@ -203,6 +211,7 @@ export async function runRalphWorkflow(
203
211
  createPr,
204
212
  });
205
213
  let reviews: WorkflowTaskResult[];
214
+ let reviewerBatchFailed = false;
206
215
  try {
207
216
  reviews = await ctx.parallel(
208
217
  [
@@ -234,6 +243,7 @@ export async function runRalphWorkflow(
234
243
  },
235
244
  );
236
245
  } catch (err) {
246
+ reviewerBatchFailed = true;
237
247
  reviews = [reviewerErrorResult(err)];
238
248
  }
239
249
  const reviewEntries = await Promise.all(reviews.map(async (review) => {
@@ -262,6 +272,7 @@ export async function runRalphWorkflow(
262
272
  convergence_decision: convergenceDecision,
263
273
  };
264
274
  }));
275
+ const roundProducedDecisions = reviewEntries.some((review) => review.convergence_decision.parsed);
265
276
  const approvalCount = reviewEntries.filter((review) =>
266
277
  review.convergence_decision.approved,
267
278
  ).length;
@@ -279,19 +290,54 @@ export async function runRalphWorkflow(
279
290
  finalActionRemaining: approved && createPr,
280
291
  diagnostics: reviewEntries.flatMap((review) => review.convergence_decision.diagnostics),
281
292
  });
293
+ const consolidatedFindings = consolidateFindingsBatch(
294
+ reviewEntries.map((review) => ({
295
+ reviewer: review.reviewer,
296
+ findings: review.decision.findings,
297
+ })),
298
+ );
299
+ const reverifyResults: WorkflowTaskResult[] = [];
300
+ const reverifyContext = {
301
+ task: async (name: string, taskOptions: WorkflowTaskOptions): Promise<WorkflowTaskResult> => {
302
+ const result = await ctx.task(name, taskOptions);
303
+ reverifyResults.push(result);
304
+ return result;
305
+ },
306
+ };
307
+ const reverified = approved
308
+ ? { batch: consolidatedFindings, audits: [] as const }
309
+ : await reverify_consolidated_batch(reverifyContext, {
310
+ batch: consolidatedFindings,
311
+ context: {
312
+ objective: workflowPrompt,
313
+ candidateRefs: [researchPath, implementationNotesPath, orchestratorReportPath],
314
+ },
315
+ });
316
+ const findings = reviewEntries.flatMap((review) => review.decision.findings);
317
+ const traceability = reviewEntries.flatMap((review) => review.decision.requirements_traceability);
318
+ // A thrown reviewer batch or an all-unparsed reviewer batch produced no
319
+ // decisions, so recording a zero-blocker round would fabricate progress
320
+ // and can suppress the escalation evidence on the very escalation it triggers.
321
+ if (!reviewerBatchFailed && roundProducedDecisions) {
322
+ convergenceEntries.push(record_convergence({
323
+ unresolvedBlockingCount: reverified.batch.filter((entry) => entry.blocking).length,
324
+ meanFindingConfidence: findings.length === 0
325
+ ? null
326
+ : findings.reduce((total, finding) => total + finding.confidence_score, 0) / findings.length,
327
+ fractionProven: traceability.length === 0
328
+ ? 0
329
+ : traceability.filter((entry) => entry.status === "proven").length / traceability.length,
330
+ demotions: reverified.audits.filter((audit) => audit.verdict === "demoted").length,
331
+ usage: fold_usage([orchestrator, ...reviews, ...reverifyResults]),
332
+ }));
333
+ }
282
334
  latestReviewReportPath = await writeJsonArtifact(
283
335
  join(artifactDir, "review-round-latest.json"),
284
336
  {
285
337
  convergence_decision: roundConvergenceDecision,
286
- // Deduplicated cross-reviewer findings batch so the next research and
287
- // orchestrator passes repair the round's findings together instead of
288
- // one at a time.
289
- consolidated_findings: consolidateFindingsBatch(
290
- reviewEntries.map((review) => ({
291
- reviewer: review.reviewer,
292
- findings: review.decision.findings,
293
- })),
294
- ),
338
+ convergence: convergenceEntries,
339
+ consolidated_findings: reverified.batch,
340
+ reverification: reverified.audits,
295
341
  reviews: reviewEntries,
296
342
  },
297
343
  );
@@ -303,6 +349,9 @@ export async function runRalphWorkflow(
303
349
  // carrying the unresolved blocking findings. Without create_pr the workflow
304
350
  // never touches a PR, approved or not.
305
351
  const unapprovedHandoff = createPr === true && !approved;
352
+ const escalationEvidence = unapprovedHandoff
353
+ ? convergence_escalation_evidence(convergenceEntries)
354
+ : [];
306
355
  if (createPr === true) {
307
356
  const prResult = await ctx.task("pull-request", {
308
357
  prompt: taggedPrompt([
@@ -319,6 +368,7 @@ export async function runRalphWorkflow(
319
368
  : unapprovedHandoff
320
369
  ? `Final unapproved review-round artifact: ${latestReviewReportPath}`
321
370
  : `Approved review-round artifact: ${latestReviewReportPath}`,
371
+ ...escalationEvidence,
322
372
  ].join("\n"),
323
373
  ],
324
374
  ...(unapprovedHandoff
@@ -0,0 +1,156 @@
1
+ /** Pure, seeded selection math for the probabilistic pivot tournament. */
2
+ import { VERIFICATION_SCALE } from "./verification-criteria.js";
3
+
4
+ export interface DirectedPair {
5
+ a: number;
6
+ b: number;
7
+ }
8
+
9
+ export interface ScoringJob {
10
+ a: number;
11
+ b: number;
12
+ criterionId: string;
13
+ rep: number;
14
+ swapped: boolean;
15
+ }
16
+
17
+ export interface ComparisonPlan {
18
+ ring: DirectedPair[];
19
+ pivotRounds: (pivots: number[]) => DirectedPair[];
20
+ jobs: (pairs: readonly DirectedPair[], criterionIds: readonly string[]) => ScoringJob[];
21
+ }
22
+
23
+ export interface Preference {
24
+ a: number;
25
+ b: number;
26
+ p: number;
27
+ }
28
+
29
+ export interface RankingEntry {
30
+ index: number;
31
+ meanPreference: number;
32
+ }
33
+
34
+ export type Ranking = RankingEntry[];
35
+
36
+ const UINT32_RANGE = 0x1_0000_0000;
37
+ const MULBERRY_INCREMENT = 0x6d2b79f5;
38
+
39
+ /** Return a deterministic Mulberry32-class random-float generator. */
40
+ export function seeded_rng(seed: number): () => number {
41
+ let state = seed >>> 0;
42
+ return () => {
43
+ state = (state + MULBERRY_INCREMENT) >>> 0;
44
+ let t = state;
45
+ t = Math.imul(t ^ (t >>> 15), t | 1);
46
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
47
+ return ((t ^ (t >>> 14)) >>> 0) / UINT32_RANGE;
48
+ };
49
+ }
50
+
51
+ function ring_cycle(n: number, rng: () => number): DirectedPair[] {
52
+ const permutation = Array.from({ length: n }, (_, index) => index);
53
+ for (let index = n - 1; index > 0; index -= 1) {
54
+ const other = Math.floor(rng() * (index + 1));
55
+ [permutation[index], permutation[other]] = [permutation[other]!, permutation[index]!];
56
+ }
57
+ return permutation.map((a, index) => ({ a, b: permutation[(index + 1) % n]! }));
58
+ }
59
+
60
+ function pair_key(a: number, b: number): string {
61
+ return a < b ? `${a}:${b}` : `${b}:${a}`;
62
+ }
63
+
64
+
65
+ /** Build a deterministic ring and expose the later pivot/job phases as pure closures. */
66
+ export function plan_comparisons(input: {
67
+ n: number;
68
+ pivots: number;
69
+ repeats: number;
70
+ seed: number;
71
+ }): ComparisonPlan {
72
+ const { n, pivots: pivotCount, repeats, seed } = input;
73
+ if (n < 2) throw new RangeError("n must be at least 2");
74
+ if (pivotCount < 1) throw new RangeError("pivots must be at least 1");
75
+ if (repeats < 1) throw new RangeError("repeats must be at least 1");
76
+
77
+ const ring = ring_cycle(n, seeded_rng(seed));
78
+ return {
79
+ ring,
80
+ pivotRounds: (pivots) => {
81
+ const scheduled = new Set(ring.map(({ a, b }) => pair_key(a, b)));
82
+ const pivotSet = new Set(pivots);
83
+ const pairs: DirectedPair[] = [];
84
+ const add = (a: number, b: number): void => {
85
+ if (a === b) return;
86
+ const key = pair_key(a, b);
87
+ if (scheduled.has(key)) return;
88
+ scheduled.add(key);
89
+ pairs.push({ a, b });
90
+ };
91
+ for (let candidate = 0; candidate < n; candidate += 1) {
92
+ if (pivotSet.has(candidate)) continue;
93
+ for (const pivot of pivots) add(candidate, pivot);
94
+ }
95
+ for (let first = 0; first < pivots.length; first += 1) {
96
+ for (let second = first + 1; second < pivots.length; second += 1) {
97
+ add(pivots[first]!, pivots[second]!);
98
+ }
99
+ }
100
+ return pairs;
101
+ },
102
+ jobs: (pairs, criterionIds) => {
103
+ const jobs: ScoringJob[] = [];
104
+ for (const pair of pairs) {
105
+ for (const criterionId of criterionIds) {
106
+ for (let rep = 0; rep < repeats; rep += 1) {
107
+ jobs.push({ a: pair.a, b: pair.b, criterionId, rep, swapped: rep % 2 === 1 });
108
+ }
109
+ }
110
+ }
111
+ return jobs;
112
+ },
113
+ };
114
+ }
115
+
116
+ /** Convert a verification score to the reward range used by Bradley-Terry. */
117
+ function normalize_score(score: number): number {
118
+ return (score - VERIFICATION_SCALE.min) / (VERIFICATION_SCALE.max - VERIFICATION_SCALE.min);
119
+ }
120
+
121
+ /** Return the Bradley-Terry soft preference for the first score. */
122
+ export function soft_win(scoreA: number, scoreB: number): number {
123
+ const difference = normalize_score(scoreA) - normalize_score(scoreB);
124
+ return 1 / (1 + Math.exp(-difference));
125
+ }
126
+
127
+ /** Add each preference to both candidates' win mass and comparison count. */
128
+ export function accumulate(prefs: readonly Preference[], w: number[], c: number[]): void {
129
+ for (const { a, b, p } of prefs) {
130
+ w[a]! += p;
131
+ c[a]! += 1;
132
+ w[b]! += 1 - p;
133
+ c[b]! += 1;
134
+ }
135
+ }
136
+
137
+ function mean_preference(w: number[], c: number[], index: number): number {
138
+ return c[index] === 0 ? 0 : w[index]! / c[index]!;
139
+ }
140
+
141
+ function candidate_order(w: number[], c: number[]): number[] {
142
+ return Array.from({ length: w.length }, (_, index) => index).sort((left, right) => {
143
+ const difference = mean_preference(w, c, right) - mean_preference(w, c, left);
144
+ return difference === 0 ? left - right : difference;
145
+ });
146
+ }
147
+
148
+ /** Return the top-k candidate indices, resolving equal means by index. */
149
+ export function select_pivots(w: number[], c: number[], k: number): number[] {
150
+ return candidate_order(w, c).slice(0, k);
151
+ }
152
+
153
+ /** Return every candidate ordered by mean preference, including unscored ones. */
154
+ export function rank_candidates(w: number[], c: number[]): Ranking {
155
+ return candidate_order(w, c).map((index) => ({ index, meanPreference: mean_preference(w, c, index) }));
156
+ }
@@ -49,6 +49,11 @@ export function withSteeringPropagation(prompt: string): string {
49
49
  return `${prompt.slice(0, instructionAt)}\n\n${tagged}${prompt.slice(instructionAt)}`;
50
50
  }
51
51
 
52
+ export const REVIEWER_CALIBRATION_RULES = [
53
+ "Trust observed output over the agent's narration.",
54
+ 'Agent declarations of success ("done", "all tests pass") are ZERO evidence on their own.',
55
+ ].join("\n");
56
+
52
57
  export const WORKER_PREFLIGHT_CONTRACT = [
53
58
  "Before implementation delegation, infer the checkout's language, framework, build system, and setup requirements from repository evidence rather than ecosystem assumptions.",
54
59
  "Inspect source layout, setup docs, manifests, lockfiles, toolchain and codegen files, CI/workflow configuration, scripts, and generated-artifact conventions for missing dependencies, generated files, toolchains, submodules, or other initialization artifacts.",
@@ -3,86 +3,68 @@ type PromptSection = readonly [tag: string, content: string];
3
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.";
4
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.";
5
5
 
6
+ /** The default three-criterion decomposition of the tournament rubric. */
7
+ export const DEFAULT_TOURNAMENT_CRITERIA = [
8
+ { name: "Correctness", description: "Satisfies the task without material errors." },
9
+ { name: "Completeness", description: "Covers required outcomes and important edge cases." },
10
+ {
11
+ name: "Evidence and task fit",
12
+ description: "Supports claims with observable evidence or checks and is directly usable without irrelevant work.",
13
+ },
14
+ ] as const;
15
+
6
16
  function taggedPrompt(sections: readonly PromptSection[]): string {
7
- return sections
8
- .map(([tag, content]) => `<${tag}>\n${content.trim()}\n</${tag}>`)
9
- .join("\n\n");
17
+ return sections
18
+ .map(([tag, content]) => `<${tag}>\n${content.trim()}\n</${tag}>`)
19
+ .join("\n\n");
10
20
  }
11
21
 
12
22
  export function renderTournamentAttemptPrompt(task: string, attempt: number): string {
13
- return taggedPrompt([
14
- ["role", "You are an independent solution author competing on solution quality."],
15
- ["attempt", `Produce attempt ${attempt} without assuming another attempt's approach or conclusions.`],
16
- ["success_criteria", "A judge can evaluate this artifact directly against correctness, completeness, evidence, and task fit."],
17
- ["requirements", [
18
- "Deliver a complete, self-contained solution rather than commentary about how to solve it.",
19
- "Ground important claims in observable evidence or executable checks.",
20
- "State assumptions, limitations, and validation performed.",
21
- "Optimize for correctness and usefulness, not length.",
22
- GROUNDED_REPORTING,
23
- ].join("\n")],
24
- ["stop_rules", "Stop when the solution is complete, supported, validated where practical, and its residual risks are stated."],
25
- ["output_format", `Markdown with Solution, Evidence and validation, Assumptions, and Residual risks. ${READABLE_REPORT}`],
26
- ["objective", task],
27
- ]);
28
- }
29
-
30
- export function renderPairwiseJudgePrompt(options: {
31
- readonly task: string;
32
- readonly firstLabel: string;
33
- readonly secondLabel: string;
34
- readonly firstPath: string;
35
- readonly secondPath: string;
36
- }): string {
37
- return taggedPrompt([
38
- ["candidates", [
39
- `First presentation: ${options.firstLabel} at ${options.firstPath}`,
40
- `Second presentation: ${options.secondLabel} at ${options.secondPath}`,
41
- "Read both files completely before deciding.",
42
- ].join("\n")],
43
- ["role", "You are an impartial pairwise judge. Evaluate only the supplied artifacts."],
44
- ["rubric", [
45
- "1. Correctness: satisfies the task without material errors.",
46
- "2. Completeness: covers required outcomes and important edge cases.",
47
- "3. Evidence: supports claims with observable evidence or checks.",
48
- "4. Task fit: is directly usable and avoids irrelevant work.",
49
- ].join("\n")],
50
- ["decision_rules", [
51
- "Choose exactly one presented candidate; do not merge or rewrite them.",
52
- "Ignore presentation order, writing length, and stylistic polish unless they affect the rubric.",
53
- "Cite observable evidence from both artifacts and give concise criteria-based justification.",
54
- GROUNDED_REPORTING,
55
- ].join("\n")],
56
- ["success_criteria", "The selected winner is traceable to short rubric-grounded evidence from both candidates."],
57
- ["stop_rules", "Stop after selecting one candidate and supporting the choice against every rubric criterion."],
58
- ["output_format", `Return only the required structured decision with winner, rationale, and evidence. ${READABLE_REPORT}`],
59
- ["objective", options.task],
60
- ]);
23
+ return taggedPrompt([
24
+ ["role", "You are an independent solution author competing on solution quality."],
25
+ ["attempt", `Produce attempt ${attempt} without assuming another attempt's approach or conclusions.`],
26
+ ["success_criteria", "A judge can evaluate this artifact directly against correctness, completeness, evidence, and task fit."],
27
+ ["requirements", [
28
+ "Deliver a complete, self-contained solution rather than commentary about how to solve it.",
29
+ "Ground important claims in observable evidence or executable checks.",
30
+ "State assumptions, limitations, and validation performed.",
31
+ "Optimize for correctness and usefulness, not length.",
32
+ GROUNDED_REPORTING,
33
+ ].join("\n")],
34
+ ["stop_rules", "Stop when the solution is complete, supported, validated where practical, and its residual risks are stated."],
35
+ ["output_format", `Markdown with Solution, Evidence and validation, Assumptions, and Residual risks. ${READABLE_REPORT}`],
36
+ ["objective", task],
37
+ ]);
61
38
  }
62
39
 
63
- export function renderBracketReducerPrompt(options: {
64
- readonly task: string;
65
- readonly bracketPath: string;
66
- readonly winnerLabel: string;
67
- readonly winnerPath: string;
40
+ export function renderComparisonsReducerPrompt(options: {
41
+ readonly task: string;
42
+ readonly comparisonsPath: string;
43
+ readonly ranking: readonly { readonly label: string; readonly meanPreference: number }[];
44
+ readonly winnerLabel: string;
45
+ readonly winnerPath: string;
68
46
  }): string {
69
- return taggedPrompt([
70
- ["artifacts", [
71
- `Bracket ledger: ${options.bracketPath}`,
72
- `Winning artifact (${options.winnerLabel}): ${options.winnerPath}`,
73
- "Read both files before reporting.",
74
- ].join("\n")],
75
- ["role", "You are the tournament bracket reducer and final reporter."],
76
- ["requirements", [
77
- "Return the winning solution faithfully; do not silently combine losing material into it.",
78
- "Summarize why it advanced using the recorded pairwise rationale and evidence.",
79
- "Call out bracket byes and any limitations recorded by judges.",
80
- "Cite the bracket ledger and winning artifact paths.",
81
- GROUNDED_REPORTING,
82
- ].join("\n")],
83
- ["success_criteria", "A reader can use the winning solution and audit every comparison that selected it."],
84
- ["stop_rules", "Stop after faithfully presenting the winner and an auditable decision trail, including byes and limitations."],
85
- ["output_format", `Markdown with Winner, Winning solution, Decision trail, Evidence, and Residual risks. ${READABLE_REPORT}`],
86
- ["objective", options.task],
87
- ]);
47
+ const rankingText = options.ranking
48
+ .map((entry, index) => `${index + 1}. ${entry.label} (mean preference ${entry.meanPreference})`)
49
+ .join("\n");
50
+ return taggedPrompt([
51
+ ["artifacts", [
52
+ `Comparisons ledger: ${options.comparisonsPath}`,
53
+ `Winning artifact (${options.winnerLabel}): ${options.winnerPath}`,
54
+ "Read the ledger and winning artifact before reporting.",
55
+ ].join("\n")],
56
+ ["role", "You are the soft-scored tournament reducer and final reporter."],
57
+ ["ranking", rankingText],
58
+ ["requirements", [
59
+ "Return the winning solution faithfully; do not silently combine losing material into it.",
60
+ "Report the full ranking exactly as recorded in the comparisons ledger.",
61
+ "Identify notable score disagreements, invalid reports, or other limitations recorded by judges.",
62
+ "Cite the comparisons ledger and winning artifact paths.",
63
+ GROUNDED_REPORTING,
64
+ ].join("\n")],
65
+ ["success_criteria", "A reader can use the winning solution and recompute the ranking from the durable comparisons ledger."],
66
+ ["stop_rules", "Stop after faithfully presenting the winner, full ranking, decision evidence, and residual risks."],
67
+ ["output_format", `Markdown with Winner, Full ranking, Decision trail, Evidence, Notable disagreements, and Residual risks. ${READABLE_REPORT}`],
68
+ ["objective", options.task],
69
+ ]);
88
70
  }