@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.
- package/CHANGELOG.md +13 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +6 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/schemas.ts +5 -0
- package/dist/builtin/subagents/src/runs/shared/long-running-guard.ts +3 -1
- package/dist/builtin/subagents/src/runs/shared/progress-trend.ts +69 -0
- package/dist/builtin/subagents/src/runs/shared/subagent-control.ts +12 -1
- package/dist/builtin/subagents/src/shared/types-results.ts +2 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +47 -1
- package/dist/builtin/workflows/README.md +6 -5
- package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +13 -5
- package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +376 -89
- package/dist/builtin/workflows/builtin/adversarial-verification.d.ts +30 -6
- package/dist/builtin/workflows/builtin/adversarial-verification.ts +14 -9
- package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +26 -3
- package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +18 -14
- package/dist/builtin/workflows/builtin/goal-artifacts.ts +9 -8
- package/dist/builtin/workflows/builtin/goal-convergence.ts +87 -0
- package/dist/builtin/workflows/builtin/goal-ledger.ts +4 -0
- package/dist/builtin/workflows/builtin/goal-prompts.ts +2 -0
- package/dist/builtin/workflows/builtin/goal-reducer.ts +6 -1
- package/dist/builtin/workflows/builtin/goal-reverify.ts +305 -0
- package/dist/builtin/workflows/builtin/goal-runner.ts +75 -10
- package/dist/builtin/workflows/builtin/goal-schemas.ts +7 -0
- package/dist/builtin/workflows/builtin/goal-types.ts +6 -0
- package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +94 -6
- package/dist/builtin/workflows/builtin/loop-until-done.d.ts +8 -0
- package/dist/builtin/workflows/builtin/loop-until-done.ts +15 -0
- package/dist/builtin/workflows/builtin/progress-scoring.ts +230 -0
- package/dist/builtin/workflows/builtin/ralph-core.ts +11 -0
- package/dist/builtin/workflows/builtin/ralph-review-gate.ts +1 -0
- package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +2 -0
- package/dist/builtin/workflows/builtin/ralph-runner.ts +60 -10
- package/dist/builtin/workflows/builtin/selection-math.ts +156 -0
- package/dist/builtin/workflows/builtin/shared-prompts.ts +5 -0
- package/dist/builtin/workflows/builtin/tournament-prompts.ts +57 -75
- package/dist/builtin/workflows/builtin/tournament-runner.ts +384 -178
- package/dist/builtin/workflows/builtin/tournament.d.ts +46 -17
- package/dist/builtin/workflows/builtin/tournament.ts +66 -32
- package/dist/builtin/workflows/builtin/verification-criteria.ts +330 -0
- package/dist/builtin/workflows/builtin/verification-prompts.ts +206 -0
- package/dist/builtin/workflows/builtin/verification-usage.ts +44 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/create-spec/SKILL.md +90 -30
- package/dist/builtin/workflows/skills/show-me/LICENSE.txt +21 -0
- package/dist/builtin/workflows/skills/show-me/SKILL.md +143 -0
- package/dist/builtin/workflows/src/authoring/workflow.ts +8 -0
- package/dist/builtin/workflows/src/authoring.d.ts +1 -1
- package/dist/builtin/workflows/src/durable/completed-catalog.ts +5 -2
- package/dist/builtin/workflows/src/durable/dbos-envelope.ts +1 -1
- package/dist/builtin/workflows/src/durable/resume-eligibility.ts +5 -3
- package/dist/builtin/workflows/src/durable/run-timing.ts +41 -10
- package/dist/builtin/workflows/src/durable/tool-primitive.ts +24 -2
- package/dist/builtin/workflows/src/engine/options.ts +1 -0
- package/dist/builtin/workflows/src/engine/primitives/workflow.ts +12 -3
- package/dist/builtin/workflows/src/engine/run-budget.ts +308 -0
- package/dist/builtin/workflows/src/engine/run-returned-status.ts +8 -0
- package/dist/builtin/workflows/src/engine/run-tool-node-lifecycle.ts +6 -0
- package/dist/builtin/workflows/src/engine/run.ts +124 -2
- package/dist/builtin/workflows/src/engine/runtime.ts +9 -0
- package/dist/builtin/workflows/src/extension/config-file-loader.ts +6 -0
- package/dist/builtin/workflows/src/extension/config-loader.ts +24 -1
- package/dist/builtin/workflows/src/extension/dispatcher.ts +6 -5
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +2 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +2975 -843
- package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +51 -4
- package/dist/builtin/workflows/src/extension/public-types.ts +3 -1
- package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +7 -1
- package/dist/builtin/workflows/src/extension/runtime.ts +22 -10
- package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +5 -0
- package/dist/builtin/workflows/src/extension/workflow-prompts.ts +1 -0
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +16 -0
- package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +44 -1
- package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +10 -1
- package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +21 -9
- package/dist/builtin/workflows/src/runs/foreground/executor-continuation.ts +14 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-lifecycle.ts +15 -4
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +62 -5
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +4 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +3 -1
- package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +10 -1
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +1 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +1 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +7 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +7 -0
- package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +1 -0
- package/dist/builtin/workflows/src/shared/budget-meter.ts +34 -0
- package/dist/builtin/workflows/src/shared/budget.d.ts +67 -0
- package/dist/builtin/workflows/src/shared/budget.ts +127 -0
- package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +92 -8
- package/dist/builtin/workflows/src/shared/persistence-restore.ts +11 -1
- package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +15 -3
- package/dist/builtin/workflows/src/shared/returned-run-status.ts +35 -2
- package/dist/builtin/workflows/src/shared/store-public-types.ts +4 -1
- package/dist/builtin/workflows/src/shared/store-run-methods.ts +8 -1
- package/dist/builtin/workflows/src/shared/store-stage-methods.ts +1 -0
- package/dist/builtin/workflows/src/shared/store-types.ts +24 -0
- package/dist/builtin/workflows/src/shared/types.ts +3 -0
- package/dist/builtin/workflows/src/shared/workflow-artifacts.ts +1 -0
- package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +3 -0
- package/dist/builtin/workflows/src/shared/workflow-authoring-types.ts +3 -0
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +1 -0
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/extensions/ui-types.d.ts +13 -3
- package/dist/core/extensions/ui-types.d.ts.map +1 -1
- package/dist/core/extensions/ui-types.js +15 -3
- package/dist/core/extensions/ui-types.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +33 -3
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/main-deferred-startup.d.ts.map +1 -1
- package/dist/main-deferred-startup.js +6 -2
- package/dist/main-deferred-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +4 -0
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-tui.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-tui.js +19 -1
- package/dist/modes/interactive/interactive-tui.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.d.ts +7 -0
- package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +94 -37
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +1 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +15 -2
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-input-scheduler.d.ts +3 -2
- package/dist/modes/rpc/rpc-input-scheduler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-input-scheduler.js +5 -2
- package/dist/modes/rpc/rpc-input-scheduler.js.map +1 -1
- package/docs/extensions.md +1 -1
- package/docs/quickstart.md +1 -0
- package/docs/skills.md +4 -0
- package/docs/workflows.md +74 -10
- package/npm-shrinkwrap.json +29 -29
- 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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
}
|