@bastani/atomic 0.9.14-alpha.3 → 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.
- package/CHANGELOG.md +23 -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/CHANGELOG.md +10 -0
- package/dist/builtin/web-access/README.md +3 -6
- package/dist/builtin/web-access/index.ts +1 -2
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/web-access/web-search-config.ts +2 -8
- package/dist/builtin/web-access/web-search-tool.ts +2 -7
- package/dist/builtin/web-access/web-search-workflow.ts +10 -0
- package/dist/builtin/workflows/CHANGELOG.md +46 -0
- 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 +3033 -872
- 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/builtin/workflows/src/tui/graph-theme.ts +11 -0
- package/dist/builtin/workflows/src/tui/graph-view-render.ts +19 -10
- package/dist/builtin/workflows/src/tui/tool-detail.ts +45 -26
- 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 +75 -11
- package/npm-shrinkwrap.json +29 -29
- package/package.json +2 -2
|
@@ -1,104 +1,391 @@
|
|
|
1
|
-
import { writeFile } from "node:fs/promises";
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { Type
|
|
4
|
-
import type { WorkflowRunContext, WorkflowSerializableValue } from "../src/shared/types.js";
|
|
5
|
-
import {
|
|
3
|
+
import { Type } from "typebox";
|
|
4
|
+
import type { WorkflowRunContext, WorkflowSerializableValue, WorkflowTaskResult } from "../src/shared/types.js";
|
|
5
|
+
import {
|
|
6
|
+
renderConsolidatorPrompt,
|
|
7
|
+
renderRepairPrompt,
|
|
8
|
+
renderWorkerPrompt,
|
|
9
|
+
} from "./adversarial-verification-prompts.js";
|
|
10
|
+
import {
|
|
11
|
+
build_scoring_prompt,
|
|
12
|
+
scoring_prompt_reads,
|
|
13
|
+
warm_first_fan_out,
|
|
14
|
+
type ScoringCandidate,
|
|
15
|
+
type SharedHead,
|
|
16
|
+
} from "./verification-prompts.js";
|
|
6
17
|
import { stableArtifactRoot } from "./pattern-artifact-root.js";
|
|
18
|
+
import { fold_usage } from "./verification-usage.js";
|
|
19
|
+
import {
|
|
20
|
+
decide_verification,
|
|
21
|
+
normalize_criteria,
|
|
22
|
+
parse_rubric,
|
|
23
|
+
VERIFICATION_SCALE,
|
|
24
|
+
type Criterion,
|
|
25
|
+
type Criteria,
|
|
26
|
+
type CriterionScore,
|
|
27
|
+
type VerificationDecision,
|
|
28
|
+
} from "./verification-criteria.js";
|
|
7
29
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
30
|
+
export const DEFAULT_CRITERIA = {
|
|
31
|
+
task_fit: "The candidate satisfies the literal task.",
|
|
32
|
+
evidence: "Important claims cite observable evidence, and file findings cite file:line where applicable.",
|
|
33
|
+
completeness: "Relevant validation is executed and reported with commands run and observed output, and no blocking correctness, safety, or completeness gap remains.",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// V2 deliberately requires every fanned-out cell to return a schema-valid score
|
|
37
|
+
// after its bounded re-ask wave. A missing score is loudly Indeterminate rather
|
|
38
|
+
// than silently narrowing the mean, and quorum is not caller-configurable.
|
|
39
|
+
const QUORUM_FRACTION = 1;
|
|
40
|
+
|
|
41
|
+
const criterionScoreSchema = Type.Object({
|
|
42
|
+
criterion_id: Type.String(),
|
|
43
|
+
score: VERIFICATION_SCALE.schema,
|
|
44
|
+
evidence: Type.Array(Type.String()),
|
|
45
|
+
findings: Type.Array(Type.Object({
|
|
46
|
+
finding: Type.String(),
|
|
47
|
+
severity: Type.Union([Type.Literal("veto"), Type.Literal("blocking"), Type.Literal("note")]),
|
|
48
|
+
}, { additionalProperties: false })),
|
|
12
49
|
}, { additionalProperties: false });
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
remaining_work: Type.Array(Type.String()),
|
|
50
|
+
const consolidatorSchema = Type.Object({
|
|
51
|
+
repair_guidance: Type.String(),
|
|
52
|
+
remaining_work: Type.Array(Type.String()),
|
|
17
53
|
}, { additionalProperties: false });
|
|
18
54
|
|
|
19
|
-
type
|
|
20
|
-
|
|
21
|
-
|
|
55
|
+
type CriterionScoreReport = {
|
|
56
|
+
readonly criterion_id: string;
|
|
57
|
+
readonly score: number;
|
|
58
|
+
readonly evidence: readonly string[];
|
|
59
|
+
readonly findings: readonly {
|
|
60
|
+
readonly finding: string;
|
|
61
|
+
readonly severity: "veto" | "blocking" | "note";
|
|
62
|
+
}[];
|
|
63
|
+
};
|
|
64
|
+
type ConsolidatedReport = {
|
|
65
|
+
readonly repair_guidance: string;
|
|
66
|
+
readonly remaining_work: readonly string[];
|
|
67
|
+
};
|
|
68
|
+
type Inputs = {
|
|
69
|
+
readonly task: string;
|
|
70
|
+
readonly verifier_count: number;
|
|
71
|
+
readonly max_repairs: number;
|
|
72
|
+
readonly criteria?: string | Record<string, string>;
|
|
73
|
+
readonly accept_mean?: number;
|
|
74
|
+
readonly reask_limit?: number;
|
|
75
|
+
};
|
|
22
76
|
export type AdversarialVerificationResult = {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
77
|
+
readonly approved: boolean;
|
|
78
|
+
readonly mean_score: number;
|
|
79
|
+
readonly score_table_path: string;
|
|
80
|
+
readonly repairs_completed: number;
|
|
81
|
+
readonly candidate_path: string;
|
|
82
|
+
readonly review_report_path: string;
|
|
83
|
+
readonly remaining_work: string[];
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
type VerificationCell = {
|
|
87
|
+
readonly criterion: Criterion;
|
|
88
|
+
readonly verifierIndex: number;
|
|
89
|
+
readonly name: string;
|
|
90
|
+
readonly artifactPath: string;
|
|
91
|
+
};
|
|
92
|
+
type ValidResult = {
|
|
93
|
+
readonly cell: VerificationCell;
|
|
94
|
+
readonly report: CriterionScoreReport;
|
|
95
|
+
readonly artifactPath: string;
|
|
96
|
+
};
|
|
97
|
+
type InvalidArtifact = {
|
|
98
|
+
readonly invalid: true;
|
|
99
|
+
readonly stage: string;
|
|
31
100
|
};
|
|
32
101
|
|
|
33
|
-
function structured<T extends WorkflowSerializableValue>(value: WorkflowSerializableValue | undefined, guard: (candidate: WorkflowSerializableValue) => candidate is T): T | undefined {
|
|
34
|
-
return value !== undefined && guard(value) ? value : undefined;
|
|
35
|
-
}
|
|
36
102
|
function isRecord(value: WorkflowSerializableValue): value is Record<string, WorkflowSerializableValue> {
|
|
37
|
-
|
|
103
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
38
104
|
}
|
|
39
|
-
|
|
40
|
-
|
|
105
|
+
|
|
106
|
+
function hasOnlyKeys(value: Record<string, WorkflowSerializableValue>, keys: readonly string[]): boolean {
|
|
107
|
+
const actual = Object.keys(value);
|
|
108
|
+
return actual.length === keys.length && keys.every((key) => key in value);
|
|
41
109
|
}
|
|
42
|
-
|
|
43
|
-
|
|
110
|
+
|
|
111
|
+
function isStringArray(value: WorkflowSerializableValue | undefined): value is string[] {
|
|
112
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
44
113
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
114
|
+
|
|
115
|
+
function isStringRecord(value: WorkflowSerializableValue): value is Record<string, string> {
|
|
116
|
+
return isRecord(value) && Object.values(value).every((entry) => typeof entry === "string");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function isCriterionFinding(value: WorkflowSerializableValue): boolean {
|
|
120
|
+
return isRecord(value)
|
|
121
|
+
&& hasOnlyKeys(value, ["finding", "severity"])
|
|
122
|
+
&& typeof value.finding === "string"
|
|
123
|
+
&& (value.severity === "veto" || value.severity === "blocking" || value.severity === "note");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function isCriterionScore(value: WorkflowSerializableValue | undefined, criterionId: string): value is CriterionScoreReport {
|
|
127
|
+
return value !== undefined
|
|
128
|
+
&& isRecord(value)
|
|
129
|
+
&& hasOnlyKeys(value, ["criterion_id", "score", "evidence", "findings"])
|
|
130
|
+
&& value.criterion_id === criterionId
|
|
131
|
+
&& typeof value.score === "number"
|
|
132
|
+
&& Number.isInteger(value.score)
|
|
133
|
+
&& value.score >= VERIFICATION_SCALE.min
|
|
134
|
+
&& value.score <= VERIFICATION_SCALE.max
|
|
135
|
+
&& isStringArray(value.evidence)
|
|
136
|
+
&& Array.isArray(value.findings)
|
|
137
|
+
&& value.findings.every(isCriterionFinding);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function isConsolidatedReport(value: WorkflowSerializableValue | undefined): value is ConsolidatedReport {
|
|
141
|
+
return value !== undefined
|
|
142
|
+
&& isRecord(value)
|
|
143
|
+
&& hasOnlyKeys(value, ["repair_guidance", "remaining_work"])
|
|
144
|
+
&& typeof value.repair_guidance === "string"
|
|
145
|
+
&& isStringArray(value.remaining_work);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function structured<T extends WorkflowSerializableValue>(
|
|
149
|
+
value: WorkflowSerializableValue | undefined,
|
|
150
|
+
guard: (candidate: WorkflowSerializableValue | undefined) => candidate is T,
|
|
151
|
+
): T | undefined {
|
|
152
|
+
return guard(value) ? value : undefined;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function resolveCriteria(value: WorkflowSerializableValue | undefined): Criteria {
|
|
156
|
+
if (value === undefined) {
|
|
157
|
+
return { groundTruthNote: "", criteria: normalize_criteria(DEFAULT_CRITERIA) };
|
|
158
|
+
}
|
|
159
|
+
if (typeof value === "string") return parse_rubric(value);
|
|
160
|
+
if (isStringRecord(value)) return { groundTruthNote: "", criteria: normalize_criteria(value) };
|
|
161
|
+
throw new TypeError("criteria must be a criteria.md string or a record of criterion descriptions");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function renderCriteriaMarkdown(criteria: Criteria): string {
|
|
165
|
+
const lines = ["# Verification criteria"];
|
|
166
|
+
if (criteria.groundTruthNote.length > 0) lines.push("## Ground Truth Note", criteria.groundTruthNote);
|
|
167
|
+
lines.push("## Criteria");
|
|
168
|
+
for (const item of criteria.criteria) {
|
|
169
|
+
lines.push(`### ${item.name} {#${item.id}}`, item.description);
|
|
170
|
+
}
|
|
171
|
+
return `${lines.join("\n\n")}\n`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function meanScore(scores: readonly CriterionScoreReport[]): number {
|
|
175
|
+
if (scores.length === 0) return 0;
|
|
176
|
+
return scores.reduce((total, score) => total + score.score, 0) / scores.length;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function toCriterionScore(report: CriterionScoreReport): CriterionScore {
|
|
180
|
+
return {
|
|
181
|
+
criterionId: report.criterion_id,
|
|
182
|
+
score: report.score,
|
|
183
|
+
evidence: report.evidence,
|
|
184
|
+
findings: report.findings,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function findingText(decision: Extract<VerificationDecision, { kind: "repair" }>): string[] {
|
|
189
|
+
return decision.findings.map((finding) => finding.finding);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function quorumEvidence(missing: number, invalidCount: number, expectedCount: number, reaskLimit: number): string {
|
|
193
|
+
return `Quorum failure: ${missing} of ${expectedCount} criterion scores remain missing after ${reaskLimit} re-ask wave(s); ${invalidCount} report attempts were invalid or missing.`;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function stageName(cell: VerificationCell, reaskWave: number): string {
|
|
197
|
+
return reaskWave === 0 ? cell.name : `${cell.name}-reask-${reaskWave}`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function verifierOutputFormat(criterionId: string): string {
|
|
201
|
+
return `Call structured_output with criterion_id (set to ${criterionId}), score (1–20), evidence (string array), and findings containing finding and severity (veto, blocking, or note).`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function scoreStep(
|
|
205
|
+
cell: VerificationCell,
|
|
206
|
+
reaskWave: number,
|
|
207
|
+
head: SharedHead,
|
|
208
|
+
criteriaPath: string,
|
|
209
|
+
) {
|
|
210
|
+
const promptHead: SharedHead = {
|
|
211
|
+
...head,
|
|
212
|
+
outputFormat: verifierOutputFormat(cell.criterion.id),
|
|
213
|
+
};
|
|
214
|
+
return {
|
|
215
|
+
name: stageName(cell, reaskWave),
|
|
216
|
+
prompt: build_scoring_prompt(promptHead, cell.criterion),
|
|
217
|
+
context: "fresh" as const,
|
|
218
|
+
reads: [criteriaPath, ...scoring_prompt_reads(promptHead)],
|
|
219
|
+
schema: criterionScoreSchema,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function writeInvalid(path: string, stage: string): Promise<void> {
|
|
224
|
+
const marker: InvalidArtifact = { invalid: true, stage };
|
|
225
|
+
await writeFile(path, `${JSON.stringify(marker, null, 2)}\n`);
|
|
226
|
+
}
|
|
227
|
+
|
|
50
228
|
export async function runAdversarialVerification(ctx: WorkflowRunContext<Inputs>): Promise<AdversarialVerificationResult> {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
229
|
+
const root = await stableArtifactRoot(ctx, "adversarial-verification");
|
|
230
|
+
const criteriaPath = join(root, "criteria.md");
|
|
231
|
+
const candidatePath = join(root, "candidate.md");
|
|
232
|
+
const resolvedCriteria = resolveCriteria(ctx.inputs.criteria);
|
|
233
|
+
await writeFile(criteriaPath, renderCriteriaMarkdown(resolvedCriteria));
|
|
234
|
+
await ctx.task("worker", { prompt: renderWorkerPrompt(ctx.inputs.task), context: "fresh", output: candidatePath, outputMode: "file-only" });
|
|
235
|
+
|
|
236
|
+
const verifierCount = ctx.inputs.verifier_count ?? 3;
|
|
237
|
+
const maxRepairs = ctx.inputs.max_repairs ?? 2;
|
|
238
|
+
const acceptMean = ctx.inputs.accept_mean ?? 14;
|
|
239
|
+
const reaskLimit = Math.max(0, Math.floor(ctx.inputs.reask_limit ?? 1));
|
|
240
|
+
const expectedCount = resolvedCriteria.criteria.length * verifierCount;
|
|
241
|
+
let repairsCompleted = 0;
|
|
242
|
+
let consecutiveIndeterminate = 0;
|
|
243
|
+
let finalDecision: VerificationDecision;
|
|
244
|
+
let finalMean = 0;
|
|
245
|
+
let scoreTablePath: string;
|
|
246
|
+
let reviewReportPath: string;
|
|
247
|
+
let remainingWork: string[] = [];
|
|
248
|
+
|
|
249
|
+
for (let round = 0; ; round += 1) {
|
|
250
|
+
const candidateBody = await readFile(candidatePath, "utf8");
|
|
251
|
+
const scoringHead: SharedHead = {
|
|
252
|
+
task: ctx.inputs.task,
|
|
253
|
+
groundTruthNote: resolvedCriteria.groundTruthNote,
|
|
254
|
+
candidates: [{ path: candidatePath, body: candidateBody } satisfies ScoringCandidate],
|
|
255
|
+
scaleAnchors: VERIFICATION_SCALE.anchors,
|
|
256
|
+
};
|
|
257
|
+
const cells: VerificationCell[] = [];
|
|
258
|
+
for (const criterion of resolvedCriteria.criteria) {
|
|
259
|
+
for (let index = 0; index < verifierCount; index += 1) {
|
|
260
|
+
const name = `verifier-${round}-${criterion.id}-${index + 1}`;
|
|
261
|
+
cells.push({
|
|
262
|
+
criterion,
|
|
263
|
+
verifierIndex: index + 1,
|
|
264
|
+
name,
|
|
265
|
+
artifactPath: join(root, `verification-${round}-${criterion.id}-${index + 1}.json`),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const runWave = async (
|
|
271
|
+
pending: readonly VerificationCell[],
|
|
272
|
+
reaskWave: number,
|
|
273
|
+
): Promise<{ readonly valid: readonly ValidResult[]; readonly invalid: readonly VerificationCell[]; readonly results: readonly WorkflowTaskResult[] }> => {
|
|
274
|
+
if (pending.length === 0) return { valid: [], invalid: [], results: [] };
|
|
275
|
+
const reports = await warm_first_fan_out(
|
|
276
|
+
ctx,
|
|
277
|
+
pending.map((cell) => scoreStep(cell, reaskWave, scoringHead, criteriaPath)),
|
|
278
|
+
() => scoringHead,
|
|
279
|
+
{ concurrency: Math.min(pending.length, 4), failFast: false },
|
|
280
|
+
);
|
|
281
|
+
const byName = new Map<string, (typeof reports)[number]>();
|
|
282
|
+
for (const report of reports) {
|
|
283
|
+
const name = report.name ?? report.stageName;
|
|
284
|
+
if (name !== undefined) byName.set(name, report);
|
|
285
|
+
}
|
|
286
|
+
const valid: ValidResult[] = [];
|
|
287
|
+
const invalid: VerificationCell[] = [];
|
|
288
|
+
for (const cell of pending) {
|
|
289
|
+
const report = byName.get(stageName(cell, reaskWave));
|
|
290
|
+
const artifactPath = reaskWave === 0
|
|
291
|
+
? cell.artifactPath
|
|
292
|
+
: cell.artifactPath.replace(/\.json$/, `-reask-${reaskWave}.json`);
|
|
293
|
+
if (isCriterionScore(report?.structured, cell.criterion.id)) {
|
|
294
|
+
await writeFile(artifactPath, `${JSON.stringify(report.structured, null, 2)}\n`);
|
|
295
|
+
valid.push({ cell, report: report.structured, artifactPath });
|
|
296
|
+
} else {
|
|
297
|
+
await writeInvalid(artifactPath, stageName(cell, reaskWave));
|
|
298
|
+
invalid.push(cell);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return { valid, invalid, results: reports };
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const roundResults: WorkflowTaskResult[] = [];
|
|
305
|
+
let pending = [...cells];
|
|
306
|
+
const validResults: ValidResult[] = [];
|
|
307
|
+
let invalidCount = 0;
|
|
308
|
+
for (let reaskWave = 0; reaskWave <= reaskLimit; reaskWave += 1) {
|
|
309
|
+
const wave = await runWave(pending, reaskWave);
|
|
310
|
+
roundResults.push(...wave.results);
|
|
311
|
+
validResults.push(...wave.valid);
|
|
312
|
+
invalidCount += wave.invalid.length;
|
|
313
|
+
pending = [...wave.invalid];
|
|
314
|
+
if (pending.length === 0) break;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const validReportsByCell = new Map(validResults.map((item) => [item.cell.name, item.report]));
|
|
318
|
+
const scoreReports = cells.flatMap((cell) => {
|
|
319
|
+
const report = validReportsByCell.get(cell.name);
|
|
320
|
+
return report === undefined ? [] : [report];
|
|
321
|
+
});
|
|
322
|
+
const scores = scoreReports.map(toCriterionScore);
|
|
323
|
+
const roundResult = { scores, invalidCount, expectedCount };
|
|
324
|
+
const decision = decide_verification(roundResult, { acceptMean, quorumFraction: QUORUM_FRACTION });
|
|
325
|
+
finalDecision = decision;
|
|
326
|
+
finalMean = meanScore(scoreReports);
|
|
327
|
+
scoreTablePath = join(root, `verification-summary-${round}.json`);
|
|
328
|
+
reviewReportPath = join(root, `review-${round}.json`);
|
|
329
|
+
const summary = {
|
|
330
|
+
scores: scoreReports,
|
|
331
|
+
mean: finalMean,
|
|
332
|
+
invalidCount,
|
|
333
|
+
decision,
|
|
334
|
+
usage: fold_usage(roundResults),
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
if (decision.kind === "accept") {
|
|
338
|
+
remainingWork = [];
|
|
339
|
+
await writeFile(scoreTablePath, `${JSON.stringify(summary, null, 2)}\n`);
|
|
340
|
+
await writeFile(reviewReportPath, `${JSON.stringify({ decision, remaining_work: [] }, null, 2)}\n`);
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (decision.kind === "indeterminate") {
|
|
345
|
+
consecutiveIndeterminate += 1;
|
|
346
|
+
remainingWork = [quorumEvidence(decision.missing, invalidCount, expectedCount, reaskLimit)];
|
|
347
|
+
await writeFile(scoreTablePath, `${JSON.stringify(summary, null, 2)}\n`);
|
|
348
|
+
await writeFile(reviewReportPath, `${JSON.stringify({ decision, evidence: remainingWork, remaining_work: remainingWork }, null, 2)}\n`);
|
|
349
|
+
if (consecutiveIndeterminate >= 2) break;
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
consecutiveIndeterminate = 0;
|
|
354
|
+
const confirmedFindings = findingText(decision);
|
|
355
|
+
const scorePaths = validResults.map((item) => item.artifactPath);
|
|
356
|
+
const consolidated = await ctx.task(`consolidate-findings-${round}`, {
|
|
357
|
+
prompt: renderConsolidatorPrompt(ctx.inputs.task, candidatePath, scorePaths, repairsCompleted, maxRepairs),
|
|
358
|
+
context: "fresh",
|
|
359
|
+
reads: [candidatePath, criteriaPath, ...scorePaths],
|
|
360
|
+
schema: consolidatorSchema,
|
|
361
|
+
});
|
|
362
|
+
const fallbackRemaining = confirmedFindings.length > 0
|
|
363
|
+
? confirmedFindings
|
|
364
|
+
: [`Mean score ${decision.mean} is below the acceptance threshold ${acceptMean}.`];
|
|
365
|
+
const consolidatedReport: ConsolidatedReport = structured(consolidated.structured, isConsolidatedReport)
|
|
366
|
+
?? { repair_guidance: "Confirmed verifier findings require repair.", remaining_work: fallbackRemaining };
|
|
367
|
+
remainingWork = fallbackRemaining;
|
|
368
|
+
await writeFile(scoreTablePath, `${JSON.stringify(summary, null, 2)}\n`);
|
|
369
|
+
await writeFile(reviewReportPath, `${JSON.stringify({ ...consolidatedReport, remaining_work: remainingWork }, null, 2)}\n`);
|
|
370
|
+
|
|
371
|
+
if (repairsCompleted >= maxRepairs) break;
|
|
372
|
+
repairsCompleted += 1;
|
|
373
|
+
await ctx.task(`repair-${repairsCompleted}`, {
|
|
374
|
+
prompt: renderRepairPrompt(ctx.inputs.task, candidatePath, reviewReportPath),
|
|
375
|
+
context: "fresh",
|
|
376
|
+
reads: [candidatePath, reviewReportPath],
|
|
377
|
+
output: candidatePath,
|
|
378
|
+
outputMode: "file-only",
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
approved: finalDecision.kind === "accept",
|
|
384
|
+
mean_score: finalMean,
|
|
385
|
+
score_table_path: scoreTablePath,
|
|
386
|
+
repairs_completed: repairsCompleted,
|
|
387
|
+
candidate_path: candidatePath,
|
|
388
|
+
review_report_path: reviewReportPath,
|
|
389
|
+
remaining_work: finalDecision.kind === "accept" ? [] : remainingWork,
|
|
390
|
+
};
|
|
104
391
|
}
|
|
@@ -1,11 +1,35 @@
|
|
|
1
1
|
import type { WorkflowDefinition, WorkflowInputValues, WorkflowOutputValues } from "../src/authoring.js";
|
|
2
|
-
|
|
3
|
-
export type
|
|
2
|
+
|
|
3
|
+
export type AdversarialVerificationCriteria = string | Record<string, string>;
|
|
4
|
+
export type AdversarialVerificationInputs = WorkflowInputValues & {
|
|
5
|
+
readonly task: string;
|
|
6
|
+
readonly verifier_count: number;
|
|
7
|
+
readonly max_repairs: number;
|
|
8
|
+
readonly criteria: AdversarialVerificationCriteria;
|
|
9
|
+
readonly accept_mean: number;
|
|
10
|
+
readonly reask_limit: number;
|
|
11
|
+
};
|
|
12
|
+
export type AdversarialVerificationRunInputs = WorkflowInputValues & {
|
|
13
|
+
readonly task: string;
|
|
14
|
+
readonly verifier_count?: number;
|
|
15
|
+
readonly max_repairs?: number;
|
|
16
|
+
readonly criteria?: AdversarialVerificationCriteria;
|
|
17
|
+
readonly accept_mean?: number;
|
|
18
|
+
readonly reask_limit?: number;
|
|
19
|
+
};
|
|
4
20
|
export type AdversarialVerificationOutputs = WorkflowOutputValues & {
|
|
5
|
-
readonly
|
|
6
|
-
readonly
|
|
7
|
-
readonly
|
|
21
|
+
readonly approved: boolean;
|
|
22
|
+
readonly mean_score: number;
|
|
23
|
+
readonly score_table_path: string;
|
|
24
|
+
readonly repairs_completed: number;
|
|
25
|
+
readonly candidate_path: string;
|
|
26
|
+
readonly review_report_path: string;
|
|
27
|
+
readonly remaining_work: string[];
|
|
8
28
|
};
|
|
9
|
-
export type AdversarialVerificationDefinition = WorkflowDefinition<
|
|
29
|
+
export type AdversarialVerificationDefinition = WorkflowDefinition<
|
|
30
|
+
AdversarialVerificationInputs,
|
|
31
|
+
AdversarialVerificationOutputs,
|
|
32
|
+
AdversarialVerificationRunInputs
|
|
33
|
+
>;
|
|
10
34
|
declare const workflow: AdversarialVerificationDefinition;
|
|
11
35
|
export default workflow;
|
|
@@ -1,29 +1,34 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { workflow } from "../src/authoring/workflow.js";
|
|
3
3
|
import { withSteeringPropagationContext } from "./steering-context.js";
|
|
4
|
-
import { runAdversarialVerification } from "./adversarial-verification-runner.js";
|
|
4
|
+
import { DEFAULT_CRITERIA, runAdversarialVerification } from "./adversarial-verification-runner.js";
|
|
5
5
|
|
|
6
6
|
export default workflow({
|
|
7
7
|
name: "adversarial-verification",
|
|
8
|
-
description: "Produce a candidate,
|
|
8
|
+
description: "Produce a candidate, score independent per-criterion verifier reports, and apply a deterministic mean-and-veto gate with bounded repairs.",
|
|
9
9
|
// The 15-minute default, stated rather than inherited: this is a per-workflow
|
|
10
10
|
// product decision, so a future change to the global default must not silently
|
|
11
11
|
// re-cadence a long autonomous run.
|
|
12
12
|
heartbeatIntervalMinutes: 15,
|
|
13
13
|
inputs: {
|
|
14
14
|
task: Type.String({ description: "Task whose candidate result must be independently verified." }),
|
|
15
|
-
verifier_count: Type.Integer({ minimum: 1, maximum: 5, default: 3, description: "Number of independent verifiers per
|
|
15
|
+
verifier_count: Type.Integer({ minimum: 1, maximum: 5, default: 3, description: "Number of independent verifiers for each criterion per round." }),
|
|
16
16
|
max_repairs: Type.Integer({ minimum: 0, maximum: 5, default: 2, description: "Maximum candidate repair rounds before rejection." }),
|
|
17
|
+
criteria: Type.Union([
|
|
18
|
+
Type.String(),
|
|
19
|
+
Type.Record(Type.String(), Type.String()),
|
|
20
|
+
], { default: DEFAULT_CRITERIA, description: "Criteria record of name-to-description entries, or criteria.md markdown." }),
|
|
21
|
+
accept_mean: Type.Number({ default: 14, description: "Mean score required for acceptance on the 1–20 verification scale." }),
|
|
22
|
+
reask_limit: Type.Integer({ minimum: 0, default: 1, description: "Maximum bounded re-ask waves for invalid criterion reports." }),
|
|
17
23
|
},
|
|
18
24
|
outputs: {
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
approved: Type.Boolean({ description: "Whether the deterministic mean-and-veto gate accepted the candidate." }),
|
|
26
|
+
mean_score: Type.Number({ description: "Mean score of the final round's schema-valid criterion reports." }),
|
|
27
|
+
score_table_path: Type.String({ description: "Path to the final round per-criterion score summary." }),
|
|
21
28
|
repairs_completed: Type.Integer({ description: "Number of repair rounds performed." }),
|
|
22
29
|
candidate_path: Type.String({ description: "Path to the final candidate artifact." }),
|
|
23
|
-
review_report_path: Type.String({ description: "Path to the final
|
|
24
|
-
|
|
25
|
-
artifact_dir: Type.String({ description: "Directory containing run artifacts." }),
|
|
26
|
-
remaining_work: Type.Array(Type.String(), { description: "Unresolved blocking findings when not approved." }),
|
|
30
|
+
review_report_path: Type.String({ description: "Path to the final consolidated findings or quorum report." }),
|
|
31
|
+
remaining_work: Type.Array(Type.String(), { description: "Unresolved findings or quorum evidence when not approved." }),
|
|
27
32
|
},
|
|
28
33
|
run: async (ctx) => await runAdversarialVerification(withSteeringPropagationContext(ctx)),
|
|
29
34
|
});
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
import type { ScoringCandidate, SharedHead } from "./verification-prompts.js";
|
|
2
|
+
import { build_scoring_prompt, scoring_prompt_reads } from "./verification-prompts.js";
|
|
3
|
+
|
|
1
4
|
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
5
|
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
6
|
|
|
7
|
+
export interface RenderedJudgePrompt {
|
|
8
|
+
readonly prompt: string;
|
|
9
|
+
readonly reads: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
export function renderGeneratorPrompt(task: string, ordinal: number): string {
|
|
5
13
|
return `<role>\nYou independently generate candidate ${ordinal}; do not imitate or assume other candidates.\n</role>\n\n<success_criteria>\nOne distinct, concrete candidate states its value, constraints, risks, and how it can be evaluated.\n</success_criteria>\n\n<stop_rules>\nStop after one self-contained, evaluable candidate; do not add alternatives.\n</stop_rules>\n\n<output_format>\nA candidate artifact with title, proposal, criteria-based rationale, risks, and evaluation evidence. ${READABLE_REPORT}\n${GROUNDED_REPORTING}\n</output_format>\n\n<objective>\n${task}\n</objective>`;
|
|
6
14
|
}
|
|
@@ -9,10 +17,25 @@ export function renderFilterPrompt(task: string, candidatePaths: readonly string
|
|
|
9
17
|
return `<artifacts>\nRead every candidate: ${candidatePaths.join(", ")}\n</artifacts>\n\n<role>\nYou deduplicate and filter independently generated candidates.\n</role>\n\n<rubric>\nFirst collapse substantively equivalent candidates. Then score fit to the task, feasibility, evidence, distinctiveness, and risk. Near-duplicates must not gain weight by repetition. Record every discarded candidate and a concrete reason.\n</rubric>\n\n<success_criteria>\nAt most ${shortlistSize} strongest distinct candidates remain, ranked by the rubric, and every discarded candidate has a concrete reason.\n</success_criteria>\n\n<stop_rules>\nStop after every candidate is shortlisted once or recorded as discarded.\n</stop_rules>\n\n<output_format>\nCall structured_output with shortlist (candidate artifact paths in ranked order) and discarded entries containing path and a concise, criteria-based reason.\n</output_format>\n\n<objective>\nSelect at most ${shortlistSize} strongest candidates for: ${task}\n</objective>`;
|
|
10
18
|
}
|
|
11
19
|
|
|
12
|
-
export function renderJudgePrompt(
|
|
13
|
-
|
|
20
|
+
export function renderJudgePrompt(
|
|
21
|
+
task: string,
|
|
22
|
+
filterPath: string,
|
|
23
|
+
shortlistSize: number,
|
|
24
|
+
candidates: readonly ScoringCandidate[] = [],
|
|
25
|
+
): RenderedJudgePrompt {
|
|
26
|
+
const head: SharedHead = {
|
|
27
|
+
task: `Rank at most ${shortlistSize} distinct candidate paths for: ${task}.`,
|
|
28
|
+
groundTruthNote: `Read the filter report at ${filterPath} and every candidate path it references before judging.`,
|
|
29
|
+
candidates,
|
|
30
|
+
outputFormat: "Call structured_output with shortlist (candidate paths in ranked order) and rationale (a concise, criteria-based explanation).",
|
|
31
|
+
};
|
|
32
|
+
const prompt = build_scoring_prompt(head, {
|
|
33
|
+
id: "filtered_shortlist",
|
|
34
|
+
name: "Filtered shortlist",
|
|
35
|
+
description: "Check task fit, feasibility, evidence, distinctiveness, and material risk; do not restore a duplicate merely because it is phrased differently.",
|
|
36
|
+
});
|
|
37
|
+
return { prompt, reads: [filterPath, ...scoring_prompt_reads(head)] };
|
|
14
38
|
}
|
|
15
|
-
|
|
16
39
|
export function renderFinalShortlistPrompt(task: string, decisionPath: string): string {
|
|
17
40
|
return `<artifact>\nRead the authoritative selection at ${decisionPath}; follow its order and do not add candidates.\n</artifact>\n\n<role>\nYou present a concise, actionable final shortlist so the reader can choose the next evaluation without reading the selection session.\n</role>\n\n<success_criteria>\nEvery selected candidate appears once in authoritative order with its differentiator, evidence, tradeoffs, and recommended next evaluation.\n</success_criteria>\n\n<stop_rules>\nStop after presenting every selected candidate once; do not add or reorder candidates.\n</stop_rules>\n\n<output_format>\nRanked markdown shortlist with candidate path, differentiator, evidence, tradeoffs, and recommended next evaluation. ${READABLE_REPORT}\n${GROUNDED_REPORTING}\n</output_format>\n\n<objective>\nSummarize the selected candidates for: ${task}\n</objective>`;
|
|
18
41
|
}
|