@peterxiaoyang/superspec 0.1.4 → 0.1.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/adapters/codex/agents/architect.toml +4 -148
- package/adapters/codex/agents/code-reviewer.toml +4 -166
- package/adapters/codex/agents/critic.toml +5 -106
- package/adapters/codex/agents/executor.toml +13 -0
- package/adapters/codex/agents/test-engineer.toml +4 -154
- package/adapters/codex/agents/test-runner.toml +13 -0
- package/adapters/codex/agents/verifier.toml +4 -110
- package/adapters/codex/install-map.json +20 -0
- package/dist/src/apply_worker_chain.d.ts +57 -0
- package/dist/src/apply_worker_chain.js +1188 -0
- package/dist/src/cli.js +13 -0
- package/dist/src/cli_args.d.ts +13 -1
- package/dist/src/cli_args.js +237 -12
- package/dist/src/core.d.ts +1 -0
- package/dist/src/core.js +1 -0
- package/dist/src/evidence.js +152 -0
- package/dist/src/gates.d.ts +2 -1
- package/dist/src/gates.js +275 -21
- package/dist/src/i18n.js +4 -3
- package/dist/src/install_engine.d.ts +17 -0
- package/dist/src/install_engine.js +125 -2
- package/dist/src/packet_measure.d.ts +43 -0
- package/dist/src/packet_measure.js +417 -0
- package/dist/src/packet_render.d.ts +4 -0
- package/dist/src/packet_render.js +1623 -0
- package/dist/src/packet_schema.d.ts +56 -0
- package/dist/src/packet_schema.js +1 -0
- package/dist/src/project_init.js +7 -49
- package/dist/src/tasks.d.ts +10 -0
- package/dist/src/tasks.js +86 -0
- package/dist/src/util.d.ts +11 -3
- package/dist/src/util.js +27 -6
- package/package.json +2 -2
- package/schemas/install-manifest.schema.json +17 -0
- package/templates/workflow/prompts/architect.md +16 -109
- package/templates/workflow/prompts/code-reviewer.md +20 -134
- package/templates/workflow/prompts/critic.md +18 -75
- package/templates/workflow/prompts/executor.md +32 -0
- package/templates/workflow/prompts/test-engineer.md +16 -126
- package/templates/workflow/prompts/test-runner.md +33 -0
- package/templates/workflow/prompts/verifier.md +20 -77
- package/templates/workflow/skills/superspec-apply/SKILL.md +102 -78
- package/templates/workflow/skills/superspec-archive/SKILL.md +41 -37
- package/templates/workflow/skills/superspec-explore/SKILL.md +63 -77
- package/templates/workflow/skills/superspec-propose/SKILL.md +64 -85
- package/templates/workflow/skills/superspec-review/SKILL.md +76 -233
|
@@ -0,0 +1,1623 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { load_config, sidecar_business_invariants_path, sidecar_discovery_path, sidecar_test_contract_path } from "./paths.js";
|
|
4
|
+
import {} from "./cli_args.js";
|
|
5
|
+
import { CLAIM_ADJUDICATION_DECISIONS, FINDING_ADJUDICATION_DECISIONS, fingerprint_obj, GuardError, MAIN_ADJUDICATION_REQUIRED_FIELDS, MAIN_ADJUDICATION_DECISIONS, REQUEST_CHANGES_ROUTES, REVIEW_EVIDENCE_REQUIRED_FIELDS, ROLE_EVIDENCE_FIELDS, SOURCE_GUIDANCE_REQUIRED_FIELDS, VERIFY_EVIDENCE_REQUIRED_FIELDS, allow, block, deepEqual, isObject, reason, renderList, runtime, safe_within, sha256_text, } from "./util.js";
|
|
6
|
+
import { artifact_status_map, gate_route_phase, get_change_root, get_repo_root, normalize_gate, openspec_status, openspec_status_shape_reasons, } from "./openspec.js";
|
|
7
|
+
import { build_finding_ledger, enumerate_review_targets, render_finding_ledger, REVIEW_TARGETS_BY_GATE, review_round_number, } from "./disclosure.js";
|
|
8
|
+
import { evidence_schema_guard, check_apply_ready, check_archive_ready, check_review_complete, check_superspec_gate, check_task_complete, check_task_edit, check_task_reopen, apply_worker_chain_lifecycle_reasons, } from "./gates.js";
|
|
9
|
+
import { final_verification_evidences, index_evidence, live_pass, live_user_confirmations } from "./evidence.js";
|
|
10
|
+
import { file_blob_sha, dirty_worktree_paths } from "./git.js";
|
|
11
|
+
import { preset_upgrade_reasons, preset_upgrade_required_from_context } from "./archive.js";
|
|
12
|
+
import { state_corrupt_reasons, state_stale_reasons } from "./state.js";
|
|
13
|
+
import { parse_tasks, resolve_test_contract_command, splitList, test_contract_invariant_refs_by_test } from "./tasks.js";
|
|
14
|
+
import { APPLY_CODE_REVIEW_REPORT_REQUIRED_FIELDS, APPLY_EXECUTOR_REPORT_REQUIRED_FIELDS, APPLY_TEST_RUNNER_REPORT_REQUIRED_FIELDS, APPLY_VERIFIER_REPORT_REQUIRED_FIELDS, apply_worker_implementation_fingerprint, apply_worker_executor_input_ref_digest, apply_worker_protected_path_refs, compute_apply_worker_freshness, fingerprint_digest, fingerprint_matches, pinned_artifact_ref_reasons as shared_pinned_artifact_ref_reasons, pre_edit_evidence_ref_reasons, read_pinned_artifact_json, worker_input_ref_digest, worker_test_run_reasons, } from "./apply_worker_chain.js";
|
|
15
|
+
function load_packet_context(change) {
|
|
16
|
+
if (typeof runtime.load_context === "function") {
|
|
17
|
+
const [status, repoRoot, changeRoot, evidences] = runtime.load_context(change);
|
|
18
|
+
return { change, status, repoRoot, changeRoot, evidences };
|
|
19
|
+
}
|
|
20
|
+
const status = openspec_status(change);
|
|
21
|
+
return {
|
|
22
|
+
change,
|
|
23
|
+
repoRoot: get_repo_root(status),
|
|
24
|
+
changeRoot: get_change_root(status),
|
|
25
|
+
evidences: index_evidence(get_change_root(status)),
|
|
26
|
+
status,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function change_pinned_ref(changeRoot, relPath) {
|
|
30
|
+
const absPath = join(changeRoot, relPath);
|
|
31
|
+
if (!existsSync(absPath) || !statSync(absPath).isFile())
|
|
32
|
+
return null;
|
|
33
|
+
return { root: "change", path: relPath, blob_sha: file_blob_sha(absPath) };
|
|
34
|
+
}
|
|
35
|
+
function repo_pinned_ref(repoRoot, relPath) {
|
|
36
|
+
const absPath = join(repoRoot, relPath);
|
|
37
|
+
if (!existsSync(absPath) || !statSync(absPath).isFile())
|
|
38
|
+
return null;
|
|
39
|
+
return { root: "repo", path: relPath, blob_sha: file_blob_sha(absPath) };
|
|
40
|
+
}
|
|
41
|
+
function collect_change_refs(changeRoot, relPaths) {
|
|
42
|
+
const refs = [];
|
|
43
|
+
for (const relPath of relPaths) {
|
|
44
|
+
const ref = change_pinned_ref(changeRoot, relPath);
|
|
45
|
+
if (ref)
|
|
46
|
+
refs.push(ref);
|
|
47
|
+
}
|
|
48
|
+
return refs;
|
|
49
|
+
}
|
|
50
|
+
function collect_repo_refs(repoRoot, relPaths) {
|
|
51
|
+
const refs = [];
|
|
52
|
+
for (const relPath of relPaths) {
|
|
53
|
+
const ref = repo_pinned_ref(repoRoot, relPath);
|
|
54
|
+
if (ref)
|
|
55
|
+
refs.push(ref);
|
|
56
|
+
}
|
|
57
|
+
return refs;
|
|
58
|
+
}
|
|
59
|
+
function unique_pinned_refs(refs) {
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
const out = [];
|
|
62
|
+
for (const ref of refs) {
|
|
63
|
+
const key = `${ref.root}\u0000${ref.path}\u0000${ref.blob_sha}`;
|
|
64
|
+
if (seen.has(key))
|
|
65
|
+
continue;
|
|
66
|
+
seen.add(key);
|
|
67
|
+
out.push(ref);
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
function unique_strings(items) {
|
|
72
|
+
return [...new Set(items.filter(Boolean))];
|
|
73
|
+
}
|
|
74
|
+
function evidence_pinned_ref(root, item) {
|
|
75
|
+
if (!isObject(item))
|
|
76
|
+
return null;
|
|
77
|
+
if (typeof item.path !== "string" || !item.path)
|
|
78
|
+
return null;
|
|
79
|
+
if (typeof item.blob_sha !== "string" || !item.blob_sha)
|
|
80
|
+
return null;
|
|
81
|
+
return { root, path: item.path, blob_sha: item.blob_sha };
|
|
82
|
+
}
|
|
83
|
+
function dirty_repo_refs(ctx, reason) {
|
|
84
|
+
try {
|
|
85
|
+
const dirtyPaths = typeof runtime.dirty_worktree_paths === "function"
|
|
86
|
+
? runtime.dirty_worktree_paths(ctx.repoRoot)
|
|
87
|
+
: dirty_worktree_paths(ctx.repoRoot);
|
|
88
|
+
return collect_repo_refs(ctx.repoRoot, dirtyPaths);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
throw new GuardError(`${reason}: ${err.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function packet_data_problems(ctx) {
|
|
95
|
+
const { change, status, repoRoot, changeRoot, evidences } = ctx;
|
|
96
|
+
const [config, configProblems] = load_config(repoRoot, changeRoot);
|
|
97
|
+
const shapeProblems = openspec_status_shape_reasons(status);
|
|
98
|
+
const evidenceProblems = evidence_schema_guard(change, changeRoot, repoRoot, evidences);
|
|
99
|
+
const changedPaths = String(config.preset ?? "full") !== "full"
|
|
100
|
+
? (typeof runtime.dirty_worktree_paths === "function" ? runtime.dirty_worktree_paths(repoRoot) : dirty_worktree_paths(repoRoot))
|
|
101
|
+
: [];
|
|
102
|
+
const presetRequired = preset_upgrade_required_from_context(config, changedPaths);
|
|
103
|
+
const presetHumanConfirmed = live_user_confirmations(evidences, "preset_upgrade").length > 0;
|
|
104
|
+
const presetProblems = preset_upgrade_reasons(config, changedPaths, presetHumanConfirmed);
|
|
105
|
+
return [
|
|
106
|
+
...shapeProblems,
|
|
107
|
+
...configProblems,
|
|
108
|
+
...evidenceProblems,
|
|
109
|
+
...state_corrupt_reasons(changeRoot),
|
|
110
|
+
...state_stale_reasons(changeRoot, status),
|
|
111
|
+
...(presetRequired ? presetProblems : presetProblems),
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
function packet_contract_problems(ctx) {
|
|
115
|
+
const { change, status, repoRoot, changeRoot, evidences } = ctx;
|
|
116
|
+
const [, configProblems] = load_config(repoRoot, changeRoot);
|
|
117
|
+
const shapeProblems = openspec_status_shape_reasons(status);
|
|
118
|
+
const evidenceProblems = evidence_schema_guard(change, changeRoot, repoRoot, evidences);
|
|
119
|
+
return [...shapeProblems, ...configProblems, ...evidenceProblems];
|
|
120
|
+
}
|
|
121
|
+
function assert_packet_context_clean(ctx) {
|
|
122
|
+
const problems = packet_contract_problems(ctx);
|
|
123
|
+
if (problems.length === 0)
|
|
124
|
+
return;
|
|
125
|
+
throw new GuardError(problems[0].message);
|
|
126
|
+
}
|
|
127
|
+
function apply_data_problems(change, decision, dataProblems) {
|
|
128
|
+
if (dataProblems.length === 0)
|
|
129
|
+
return decision;
|
|
130
|
+
if (decision.allowed) {
|
|
131
|
+
return block(change, String(decision.gate ?? "guard_error"), dataProblems, {
|
|
132
|
+
task_id: decision.task_id,
|
|
133
|
+
openspec_summary: decision.openspec_status_summary,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
...decision,
|
|
138
|
+
block_reasons: [...(Array.isArray(decision.block_reasons) ? decision.block_reasons : []), ...dataProblems],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function evaluate_workflow_decision(ctx, gateRaw, taskId) {
|
|
142
|
+
const gate = normalize_gate(gateRaw);
|
|
143
|
+
let decision;
|
|
144
|
+
if (gate === "apply_ready") {
|
|
145
|
+
decision = check_apply_ready(ctx.change, ctx.status, ctx.changeRoot, ctx.evidences);
|
|
146
|
+
}
|
|
147
|
+
else if (gate === "task_edit") {
|
|
148
|
+
decision = check_task_edit(ctx.change, ctx.status, ctx.changeRoot, ctx.evidences, taskId ?? "");
|
|
149
|
+
}
|
|
150
|
+
else if (gate === "task_complete") {
|
|
151
|
+
decision = check_task_complete(ctx.change, ctx.status, ctx.changeRoot, ctx.evidences, taskId ?? "");
|
|
152
|
+
}
|
|
153
|
+
else if (gate === "task_reopen") {
|
|
154
|
+
decision = check_task_reopen(ctx.change, ctx.status, ctx.changeRoot, ctx.evidences, taskId ?? "");
|
|
155
|
+
}
|
|
156
|
+
else if (gate === "review_complete") {
|
|
157
|
+
decision = check_review_complete(ctx.change, ctx.status, ctx.changeRoot, ctx.evidences);
|
|
158
|
+
}
|
|
159
|
+
else if (gate === "archive_ready") {
|
|
160
|
+
decision = check_archive_ready(ctx.change, ctx.status, ctx.changeRoot, ctx.evidences);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
decision = check_superspec_gate(ctx.change, ctx.status, ctx.changeRoot, ctx.evidences, gate);
|
|
164
|
+
}
|
|
165
|
+
return apply_data_problems(ctx.change, decision, packet_data_problems(ctx));
|
|
166
|
+
}
|
|
167
|
+
function gate_recheck_command(change, gate, taskId, diagnostic = false) {
|
|
168
|
+
const format = diagnostic ? "json" : "agent";
|
|
169
|
+
if (gate === "apply_ready")
|
|
170
|
+
return `superspec guard check-apply-ready --change "${change}" --format ${format}`;
|
|
171
|
+
if (gate === "review_complete")
|
|
172
|
+
return `superspec guard check-review-complete --change "${change}" --format ${format}`;
|
|
173
|
+
if (gate === "archive_ready")
|
|
174
|
+
return `superspec guard check-archive-ready --change "${change}" --format ${format}`;
|
|
175
|
+
if (gate === "task_edit")
|
|
176
|
+
return `superspec guard check-task-edit --change "${change}" --task-id "${taskId ?? ""}" --format ${format}`;
|
|
177
|
+
if (gate === "task_complete")
|
|
178
|
+
return `superspec guard check-task-complete --change "${change}" --task-id "${taskId ?? ""}" --format ${format}`;
|
|
179
|
+
if (gate === "task_reopen")
|
|
180
|
+
return `superspec guard check-task-reopen --change "${change}" --task-id "${taskId ?? ""}" --format ${format}`;
|
|
181
|
+
return `superspec guard check-enter --change "${change}" --gate "${gate}" --format ${format}`;
|
|
182
|
+
}
|
|
183
|
+
function default_allowed_next_action(gate) {
|
|
184
|
+
if (gate === "review_complete")
|
|
185
|
+
return "current review gate already passes; continue with archive-ready work.";
|
|
186
|
+
if (gate === "archive_ready")
|
|
187
|
+
return "current archive gate already passes; continue with archive handoff.";
|
|
188
|
+
if (gate === "apply_ready")
|
|
189
|
+
return "current apply gate already passes; continue with OpenSpec apply instructions and task execution.";
|
|
190
|
+
if (gate === "task_edit")
|
|
191
|
+
return "current task is clear to enter implementation edits.";
|
|
192
|
+
if (gate === "task_complete")
|
|
193
|
+
return "current task has enough completion proof to be checked off.";
|
|
194
|
+
if (gate === "task_reopen")
|
|
195
|
+
return "current task is authorized for a guarded reopen.";
|
|
196
|
+
return "current gate already passes; continue with the next workflow step.";
|
|
197
|
+
}
|
|
198
|
+
function openspec_cli_surfaces_for_gate(change, gate) {
|
|
199
|
+
if (gate === "explore_complete") {
|
|
200
|
+
return [
|
|
201
|
+
"openspec list --json",
|
|
202
|
+
`openspec status --change "${change}" --json`,
|
|
203
|
+
];
|
|
204
|
+
}
|
|
205
|
+
if (gate === "apply_ready" || gate === "task_edit" || gate === "task_complete" || gate === "task_reopen") {
|
|
206
|
+
return [`openspec instructions apply --change "${change}" --json`];
|
|
207
|
+
}
|
|
208
|
+
if (gate === "proposal_reviewed" || gate === "design_complete" || gate === "invariants_reviewed" || gate === "test_contract_drafted" || gate === "tasks_complete") {
|
|
209
|
+
return [
|
|
210
|
+
`openspec status --change "${change}" --json`,
|
|
211
|
+
`openspec instructions <artifact-id> --change "${change}" --json`,
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
if (gate === "review_complete")
|
|
215
|
+
return [`openspec validate "${change}"`];
|
|
216
|
+
if (gate === "archive_ready") {
|
|
217
|
+
return [
|
|
218
|
+
`openspec validate "${change}"`,
|
|
219
|
+
`openspec archive -y "${change}"`,
|
|
220
|
+
];
|
|
221
|
+
}
|
|
222
|
+
return [];
|
|
223
|
+
}
|
|
224
|
+
function workflow_gate_refs(ctx, gate) {
|
|
225
|
+
const reviewTargets = enumerate_review_targets(gate, ctx.changeRoot);
|
|
226
|
+
if (reviewTargets) {
|
|
227
|
+
return unique_pinned_refs([...reviewTargets.keys()].map((path) => change_pinned_ref(ctx.changeRoot, path)).filter(Boolean));
|
|
228
|
+
}
|
|
229
|
+
if (gate === "apply_ready" || gate === "task_edit" || gate === "task_complete" || gate === "task_reopen") {
|
|
230
|
+
return unique_pinned_refs(collect_change_refs(ctx.changeRoot, [
|
|
231
|
+
"tasks.md",
|
|
232
|
+
"design.md",
|
|
233
|
+
".superspec/artifacts/business-invariants.md",
|
|
234
|
+
".superspec/artifacts/test-contract.md",
|
|
235
|
+
]));
|
|
236
|
+
}
|
|
237
|
+
if (gate === "review_complete" || gate === "archive_ready") {
|
|
238
|
+
const changeRefs = collect_change_refs(ctx.changeRoot, [
|
|
239
|
+
"tasks.md",
|
|
240
|
+
"design.md",
|
|
241
|
+
".superspec/artifacts/business-invariants.md",
|
|
242
|
+
".superspec/artifacts/test-contract.md",
|
|
243
|
+
]);
|
|
244
|
+
const repoRefs = dirty_repo_refs(ctx, `${gate}: failed to inspect dirty worktree`);
|
|
245
|
+
return unique_pinned_refs([...changeRefs, ...repoRefs]);
|
|
246
|
+
}
|
|
247
|
+
if (gate === "explore_complete")
|
|
248
|
+
return collect_change_refs(ctx.changeRoot, [".superspec/artifacts/discovery.md"]);
|
|
249
|
+
return [];
|
|
250
|
+
}
|
|
251
|
+
function evidence_ref_selector(changeRoot, ev) {
|
|
252
|
+
if (typeof ev._path !== "string" || !ev._path)
|
|
253
|
+
return null;
|
|
254
|
+
return change_pinned_ref(changeRoot, ev._path);
|
|
255
|
+
}
|
|
256
|
+
function decision_selectors_for_gate(ctx, gate) {
|
|
257
|
+
const selectors = [];
|
|
258
|
+
for (const ev of ctx.evidences) {
|
|
259
|
+
if (!isObject(ev) || ev._invalid)
|
|
260
|
+
continue;
|
|
261
|
+
if (normalize_gate(String(ev.gate ?? "")) !== gate)
|
|
262
|
+
continue;
|
|
263
|
+
const evidenceRef = evidence_ref_selector(ctx.changeRoot, ev);
|
|
264
|
+
if (!evidenceRef)
|
|
265
|
+
continue;
|
|
266
|
+
if (typeof ev.decision_scope_key === "string" && ev.decision_scope_key) {
|
|
267
|
+
selectors.push({
|
|
268
|
+
evidence_id: String(ev.evidence_id ?? ""),
|
|
269
|
+
decision_scope_key: ev.decision_scope_key,
|
|
270
|
+
evidence_ref: evidenceRef,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
for (const item of Array.isArray(ev.finding_dispositions) ? ev.finding_dispositions : []) {
|
|
274
|
+
if (!isObject(item))
|
|
275
|
+
continue;
|
|
276
|
+
if (typeof item.decision_scope_key !== "string" || !item.decision_scope_key)
|
|
277
|
+
continue;
|
|
278
|
+
selectors.push({
|
|
279
|
+
evidence_id: String(ev.evidence_id ?? ""),
|
|
280
|
+
decision_scope_key: item.decision_scope_key,
|
|
281
|
+
evidence_ref: evidenceRef,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return selectors;
|
|
286
|
+
}
|
|
287
|
+
function finding_selectors_for_gate(ctx, gate, round) {
|
|
288
|
+
const selectors = [];
|
|
289
|
+
for (const ev of ctx.evidences) {
|
|
290
|
+
if (!isObject(ev) || ev._invalid)
|
|
291
|
+
continue;
|
|
292
|
+
if (normalize_gate(String(ev.gate ?? "")) !== gate)
|
|
293
|
+
continue;
|
|
294
|
+
if (!Array.isArray(ev.findings))
|
|
295
|
+
continue;
|
|
296
|
+
const evidenceRef = evidence_ref_selector(ctx.changeRoot, ev);
|
|
297
|
+
if (!evidenceRef)
|
|
298
|
+
continue;
|
|
299
|
+
if (round !== undefined) {
|
|
300
|
+
const roundNumber = review_round_number(gate, String(ev.review_round_id ?? ""));
|
|
301
|
+
if (roundNumber !== round)
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
for (const item of ev.findings) {
|
|
305
|
+
if (!isObject(item) || typeof item.finding_uid !== "string" || !item.finding_uid)
|
|
306
|
+
continue;
|
|
307
|
+
selectors.push({
|
|
308
|
+
evidence_id: String(ev.evidence_id ?? ""),
|
|
309
|
+
finding_uid: item.finding_uid,
|
|
310
|
+
evidence_ref: evidenceRef,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return selectors;
|
|
315
|
+
}
|
|
316
|
+
function review_complete_finding_selectors(ctx) {
|
|
317
|
+
const selectors = [];
|
|
318
|
+
for (const ev of live_pass(ctx.evidences, { gate: "review_complete", kind: "source_guidance" })) {
|
|
319
|
+
const evidenceRef = evidence_ref_selector(ctx.changeRoot, ev);
|
|
320
|
+
if (!evidenceRef)
|
|
321
|
+
continue;
|
|
322
|
+
for (const item of Array.isArray(ev.blocking_findings) ? ev.blocking_findings : []) {
|
|
323
|
+
if (!isObject(item))
|
|
324
|
+
continue;
|
|
325
|
+
const findingUid = typeof item.finding_uid === "string" && item.finding_uid
|
|
326
|
+
? item.finding_uid
|
|
327
|
+
: (typeof item.finding_id === "string" && item.finding_id
|
|
328
|
+
? `review_complete:${String(ev.evidence_id ?? "")}:${item.finding_id}`
|
|
329
|
+
: "");
|
|
330
|
+
if (!findingUid)
|
|
331
|
+
continue;
|
|
332
|
+
selectors.push({
|
|
333
|
+
evidence_id: String(ev.evidence_id ?? ""),
|
|
334
|
+
finding_uid: findingUid,
|
|
335
|
+
evidence_ref: evidenceRef,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return selectors;
|
|
340
|
+
}
|
|
341
|
+
const REVIEW_PACKET_ROLES_BY_GATE = {
|
|
342
|
+
explore_complete: ["critic"],
|
|
343
|
+
proposal_reviewed: ["critic"],
|
|
344
|
+
design_complete: ["architect", "critic", "test-engineer"],
|
|
345
|
+
invariants_reviewed: ["critic", "test-engineer"],
|
|
346
|
+
test_contract_drafted: ["critic", "test-engineer"],
|
|
347
|
+
tasks_complete: ["critic"],
|
|
348
|
+
review_complete: ["code-reviewer", "architect", "critic", "verifier"],
|
|
349
|
+
};
|
|
350
|
+
function validate_review_packet_gate_and_role(gate, role) {
|
|
351
|
+
if (gate !== "review_complete" && !(gate in REVIEW_TARGETS_BY_GATE)) {
|
|
352
|
+
throw new GuardError(`review-packet only supports disclosure gates and review_complete, got ${gate}`);
|
|
353
|
+
}
|
|
354
|
+
if (role === "main-thread")
|
|
355
|
+
return;
|
|
356
|
+
const allowedRoles = REVIEW_PACKET_ROLES_BY_GATE[gate] ?? [];
|
|
357
|
+
if (!allowedRoles.includes(role)) {
|
|
358
|
+
throw new GuardError(`review-packet role ${role} is not supported for gate ${gate}; expected one of ${renderList([...allowedRoles, "main-thread"])}`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function workflow_packet(ctx, gateRaw, taskId) {
|
|
362
|
+
const gate = normalize_gate(gateRaw);
|
|
363
|
+
const decision = evaluate_workflow_decision(ctx, gate, taskId);
|
|
364
|
+
const reasonCodes = unique_strings((Array.isArray(decision.block_reasons) ? decision.block_reasons : []).map((item) => item.code));
|
|
365
|
+
const topBlockers = reasonCodes.slice(0, 5);
|
|
366
|
+
const nextActions = Array.isArray(decision.next_allowed_actions) ? decision.next_allowed_actions.filter((item) => typeof item === "string" && item.length > 0) : [];
|
|
367
|
+
const packet = {
|
|
368
|
+
stage: gate_route_phase(gate),
|
|
369
|
+
current_gate: gate,
|
|
370
|
+
status: decision.allowed ? "allowed" : "blocked",
|
|
371
|
+
next_action: nextActions[0] ?? default_allowed_next_action(gate),
|
|
372
|
+
next_command: gate_recheck_command(ctx.change, gate, taskId, false),
|
|
373
|
+
diagnostic_command: gate_recheck_command(ctx.change, gate, taskId, true),
|
|
374
|
+
must_read_refs: workflow_gate_refs(ctx, gate),
|
|
375
|
+
};
|
|
376
|
+
const cliSurfaces = openspec_cli_surfaces_for_gate(ctx.change, gate);
|
|
377
|
+
if (cliSurfaces.length > 0)
|
|
378
|
+
packet.openspec_cli_surfaces = cliSurfaces;
|
|
379
|
+
if (taskId)
|
|
380
|
+
packet.task_id = taskId;
|
|
381
|
+
if (!decision.allowed) {
|
|
382
|
+
packet.top_blockers = topBlockers;
|
|
383
|
+
packet.blocker_count = reasonCodes.length;
|
|
384
|
+
packet.has_more_blockers = reasonCodes.length > topBlockers.length;
|
|
385
|
+
}
|
|
386
|
+
const findings = finding_selectors_for_gate(ctx, gate);
|
|
387
|
+
if (findings.length > 0)
|
|
388
|
+
packet.must_read_verbatim_findings = findings;
|
|
389
|
+
const decisions = decision_selectors_for_gate(ctx, gate);
|
|
390
|
+
if (decisions.length > 0)
|
|
391
|
+
packet.must_read_verbatim_decisions = decisions;
|
|
392
|
+
return packet;
|
|
393
|
+
}
|
|
394
|
+
function live_output_refs(changeRoot, evidences) {
|
|
395
|
+
const refs = [];
|
|
396
|
+
for (const ev of evidences) {
|
|
397
|
+
if (!isObject(ev))
|
|
398
|
+
continue;
|
|
399
|
+
if (typeof ev.output_ref !== "string" || !ev.output_ref)
|
|
400
|
+
continue;
|
|
401
|
+
const ref = change_pinned_ref(changeRoot, ev.output_ref);
|
|
402
|
+
if (ref)
|
|
403
|
+
refs.push(ref);
|
|
404
|
+
}
|
|
405
|
+
return unique_pinned_refs(refs);
|
|
406
|
+
}
|
|
407
|
+
function disclosure_round_reviews(ctx, gate, round) {
|
|
408
|
+
return ctx.evidences.filter((ev) => isObject(ev)
|
|
409
|
+
&& !ev._invalid
|
|
410
|
+
&& normalize_gate(String(ev.gate ?? "")) === gate
|
|
411
|
+
&& Boolean(ev.agent_role)
|
|
412
|
+
&& review_round_number(gate, String(ev.review_round_id ?? "")) === round);
|
|
413
|
+
}
|
|
414
|
+
function review_complete_role_target_refs(ctx) {
|
|
415
|
+
const repoRefs = dirty_repo_refs(ctx, "review_complete: failed to inspect dirty worktree");
|
|
416
|
+
return unique_pinned_refs([
|
|
417
|
+
...repoRefs,
|
|
418
|
+
...collect_change_refs(ctx.changeRoot, [
|
|
419
|
+
"tasks.md",
|
|
420
|
+
"design.md",
|
|
421
|
+
".superspec/artifacts/business-invariants.md",
|
|
422
|
+
".superspec/artifacts/test-contract.md",
|
|
423
|
+
]),
|
|
424
|
+
]);
|
|
425
|
+
}
|
|
426
|
+
function verification_output_fields() {
|
|
427
|
+
return [...ROLE_EVIDENCE_FIELDS, ...VERIFY_EVIDENCE_REQUIRED_FIELDS, "scope_drift"];
|
|
428
|
+
}
|
|
429
|
+
function review_output_fields() {
|
|
430
|
+
return [...ROLE_EVIDENCE_FIELDS, "review_round_id", "findings", "acknowledged_accepted_deviation_uids"];
|
|
431
|
+
}
|
|
432
|
+
function source_guidance_output_fields() {
|
|
433
|
+
return [
|
|
434
|
+
...ROLE_EVIDENCE_FIELDS,
|
|
435
|
+
...SOURCE_GUIDANCE_REQUIRED_FIELDS,
|
|
436
|
+
...REVIEW_EVIDENCE_REQUIRED_FIELDS,
|
|
437
|
+
"blocking_findings",
|
|
438
|
+
"non_blocking_findings",
|
|
439
|
+
"finding_dispositions",
|
|
440
|
+
"rollback_targets",
|
|
441
|
+
];
|
|
442
|
+
}
|
|
443
|
+
function main_review_digest_fields() {
|
|
444
|
+
return [
|
|
445
|
+
"review_round_id",
|
|
446
|
+
"target_refs",
|
|
447
|
+
"source_review_evidence_refs",
|
|
448
|
+
"previous_digest_refs",
|
|
449
|
+
"finding_dispositions",
|
|
450
|
+
];
|
|
451
|
+
}
|
|
452
|
+
function main_adjudication_fields() {
|
|
453
|
+
return [
|
|
454
|
+
...MAIN_ADJUDICATION_REQUIRED_FIELDS,
|
|
455
|
+
"review_decision",
|
|
456
|
+
"request_changes_route",
|
|
457
|
+
"blocking_source_evidence_refs",
|
|
458
|
+
"reopen_task_ids",
|
|
459
|
+
`review_decision values: ${renderList([...MAIN_ADJUDICATION_DECISIONS])}`,
|
|
460
|
+
`request_changes_route values: ${renderList([...REQUEST_CHANGES_ROUTES])}`,
|
|
461
|
+
`claim_adjudications decision values: ${renderList([...CLAIM_ADJUDICATION_DECISIONS])}`,
|
|
462
|
+
`finding_adjudications decision values: ${renderList([...FINDING_ADJUDICATION_DECISIONS])}`,
|
|
463
|
+
];
|
|
464
|
+
}
|
|
465
|
+
function review_packet(ctx, gateRaw, role, round, requestedKind) {
|
|
466
|
+
const gate = normalize_gate(gateRaw);
|
|
467
|
+
const consumer = role === "main-thread" ? "main-thread" : "role";
|
|
468
|
+
validate_review_packet_gate_and_role(gate, role);
|
|
469
|
+
let targetRefs = [];
|
|
470
|
+
let sourceRefs = [];
|
|
471
|
+
let requiredLoadRefs = [];
|
|
472
|
+
let requiredClaimIds = [];
|
|
473
|
+
let requiredOutputKind = "review";
|
|
474
|
+
let outputContractFields = review_output_fields();
|
|
475
|
+
let stopConditions = [];
|
|
476
|
+
if (gate === "review_complete") {
|
|
477
|
+
targetRefs = review_complete_role_target_refs(ctx);
|
|
478
|
+
if (consumer === "main-thread") {
|
|
479
|
+
if (requestedKind !== undefined)
|
|
480
|
+
throw new GuardError("review-packet --kind is only supported for review_complete role lanes");
|
|
481
|
+
const sourceGuidance = live_pass(ctx.evidences, { gate: "review_complete", kind: "source_guidance" });
|
|
482
|
+
const verification = final_verification_evidences(ctx.evidences);
|
|
483
|
+
sourceRefs = unique_pinned_refs([
|
|
484
|
+
...live_output_refs(ctx.changeRoot, sourceGuidance),
|
|
485
|
+
...live_output_refs(ctx.changeRoot, verification),
|
|
486
|
+
]);
|
|
487
|
+
requiredLoadRefs = unique_pinned_refs(sourceGuidance.flatMap((ev) => Array.isArray(ev.required_load_refs)
|
|
488
|
+
? ev.required_load_refs.map((item) => evidence_pinned_ref("repo", item)).filter(Boolean)
|
|
489
|
+
: []));
|
|
490
|
+
requiredClaimIds = unique_strings(sourceGuidance.flatMap((ev) => Array.isArray(ev.required_claim_ids) ? ev.required_claim_ids.map(String) : []));
|
|
491
|
+
requiredOutputKind = "main_adjudication";
|
|
492
|
+
outputContractFields = main_adjudication_fields();
|
|
493
|
+
stopConditions = [
|
|
494
|
+
"Stop if any required_load_refs item has not been actually read by the main thread.",
|
|
495
|
+
"Stop if any required_claim_ids entry is left unadjudicated.",
|
|
496
|
+
"Stop if any blocking finding remains needs_fix.",
|
|
497
|
+
"Stop if request_changes_route='change_update'; hand off back to propose instead of forcing review_complete.",
|
|
498
|
+
];
|
|
499
|
+
}
|
|
500
|
+
else if (role === "verifier" || (role === "critic" && requestedKind === "verification_review")) {
|
|
501
|
+
if (role === "verifier" && requestedKind !== undefined && requestedKind !== "verification_review") {
|
|
502
|
+
throw new GuardError("review-packet role verifier only supports --kind verification_review");
|
|
503
|
+
}
|
|
504
|
+
sourceRefs = targetRefs;
|
|
505
|
+
requiredOutputKind = "verification_review";
|
|
506
|
+
outputContractFields = verification_output_fields();
|
|
507
|
+
stopConditions = [
|
|
508
|
+
"Stop after writing verification_review only; do not write main_adjudication.",
|
|
509
|
+
"Stop if validation or evidence gaps remain unresolved.",
|
|
510
|
+
];
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
if (requestedKind !== undefined && requestedKind !== "source_guidance") {
|
|
514
|
+
throw new GuardError(`review-packet role ${role} cannot write ${requestedKind} for gate ${gate}`);
|
|
515
|
+
}
|
|
516
|
+
sourceRefs = targetRefs;
|
|
517
|
+
requiredOutputKind = "source_guidance";
|
|
518
|
+
outputContractFields = source_guidance_output_fields();
|
|
519
|
+
stopConditions = [
|
|
520
|
+
"Stop after writing source_guidance only; do not write main_adjudication.",
|
|
521
|
+
"Stop if reviewed_files does not cover the implementation diff.",
|
|
522
|
+
"Stop if rollback_targets are missing.",
|
|
523
|
+
];
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
else if (consumer === "main-thread") {
|
|
527
|
+
if (requestedKind !== undefined)
|
|
528
|
+
throw new GuardError("review-packet --kind is only supported for review_complete role lanes");
|
|
529
|
+
const roundReviews = disclosure_round_reviews(ctx, gate, round);
|
|
530
|
+
const targetSet = enumerate_review_targets(gate, ctx.changeRoot);
|
|
531
|
+
targetRefs = targetSet ? unique_pinned_refs([...targetSet.keys()].map((path) => change_pinned_ref(ctx.changeRoot, path)).filter(Boolean)) : [];
|
|
532
|
+
sourceRefs = live_output_refs(ctx.changeRoot, roundReviews);
|
|
533
|
+
requiredLoadRefs = sourceRefs;
|
|
534
|
+
requiredOutputKind = "main_review_digest";
|
|
535
|
+
outputContractFields = main_review_digest_fields();
|
|
536
|
+
stopConditions = [
|
|
537
|
+
"Stop if any finding from the round lacks a disposition in the digest.",
|
|
538
|
+
"Stop if any material finding lacks a user decision, standing authorization, or baseline decision anchor.",
|
|
539
|
+
"Stop if a round>1 digest is missing the deterministic ledger block.",
|
|
540
|
+
];
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
if (requestedKind !== undefined)
|
|
544
|
+
throw new GuardError("review-packet --kind is only supported for review_complete role lanes");
|
|
545
|
+
const targetSet = enumerate_review_targets(gate, ctx.changeRoot);
|
|
546
|
+
targetRefs = targetSet ? unique_pinned_refs([...targetSet.keys()].map((path) => change_pinned_ref(ctx.changeRoot, path)).filter(Boolean)) : [];
|
|
547
|
+
sourceRefs = targetRefs;
|
|
548
|
+
requiredOutputKind = "review";
|
|
549
|
+
outputContractFields = review_output_fields();
|
|
550
|
+
stopConditions = [
|
|
551
|
+
"Stop after writing role review evidence only; do not write main_review_digest or main_adjudication.",
|
|
552
|
+
"Stop if target_refs do not pin the current gate target set.",
|
|
553
|
+
];
|
|
554
|
+
}
|
|
555
|
+
const packet = {
|
|
556
|
+
consumer,
|
|
557
|
+
gate,
|
|
558
|
+
role,
|
|
559
|
+
round,
|
|
560
|
+
target_refs: targetRefs,
|
|
561
|
+
source_refs: sourceRefs,
|
|
562
|
+
required_output_kind: requiredOutputKind,
|
|
563
|
+
output_contract_fields: outputContractFields,
|
|
564
|
+
required_review_scope: targetRefs.map((item) => item.path),
|
|
565
|
+
stop_conditions: stopConditions,
|
|
566
|
+
};
|
|
567
|
+
if (requiredLoadRefs.length > 0)
|
|
568
|
+
packet.required_load_refs = requiredLoadRefs;
|
|
569
|
+
if (requiredClaimIds.length > 0)
|
|
570
|
+
packet.required_claim_ids = requiredClaimIds;
|
|
571
|
+
const findingSelectors = gate === "review_complete"
|
|
572
|
+
? (consumer === "main-thread" ? review_complete_finding_selectors(ctx) : [])
|
|
573
|
+
: finding_selectors_for_gate(ctx, gate, round);
|
|
574
|
+
if (findingSelectors.length > 0)
|
|
575
|
+
packet.must_read_verbatim_findings = findingSelectors;
|
|
576
|
+
const decisionSelectors = decision_selectors_for_gate(ctx, gate);
|
|
577
|
+
if (decisionSelectors.length > 0)
|
|
578
|
+
packet.must_read_verbatim_decisions = decisionSelectors;
|
|
579
|
+
return packet;
|
|
580
|
+
}
|
|
581
|
+
function decision_status(decision) {
|
|
582
|
+
return decision.allowed ? "allowed" : "blocked";
|
|
583
|
+
}
|
|
584
|
+
function safe_decision_status(ctx, gate, taskId) {
|
|
585
|
+
try {
|
|
586
|
+
return decision_status(evaluate_workflow_decision(ctx, gate, taskId));
|
|
587
|
+
}
|
|
588
|
+
catch {
|
|
589
|
+
return "blocked";
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function decision_reasons(decision) {
|
|
593
|
+
return Array.isArray(decision.block_reasons) ? decision.block_reasons : [];
|
|
594
|
+
}
|
|
595
|
+
function apply_worker_report_policy() {
|
|
596
|
+
return {
|
|
597
|
+
max_inline_report_chars: 12000,
|
|
598
|
+
artifact_ref_policy: "raw transcripts, diffs, logs, and long outputs must be returned as refs",
|
|
599
|
+
truncation_policy: "fail_closed",
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function without_audit_metadata(value) {
|
|
603
|
+
if (Array.isArray(value))
|
|
604
|
+
return value.map(without_audit_metadata);
|
|
605
|
+
if (!isObject(value))
|
|
606
|
+
return value;
|
|
607
|
+
const out = {};
|
|
608
|
+
for (const [key, child] of Object.entries(value)) {
|
|
609
|
+
if (key === "computed_at")
|
|
610
|
+
continue;
|
|
611
|
+
out[key] = without_audit_metadata(child);
|
|
612
|
+
}
|
|
613
|
+
return out;
|
|
614
|
+
}
|
|
615
|
+
function apply_worker_stop_conditions(kind) {
|
|
616
|
+
if (kind === "apply_test") {
|
|
617
|
+
return [
|
|
618
|
+
"Stop after reporting the bounded test command result only.",
|
|
619
|
+
"Do not change implementation files.",
|
|
620
|
+
"Return raw logs as pinned artifact refs when output is long.",
|
|
621
|
+
];
|
|
622
|
+
}
|
|
623
|
+
if (kind === "apply_executor") {
|
|
624
|
+
return [
|
|
625
|
+
"Stay inside declared_task_write_scope.",
|
|
626
|
+
"Stop after implementation report; do not mark tasks complete.",
|
|
627
|
+
"Do not write OpenSpec artifacts or .superspec evidence directly.",
|
|
628
|
+
];
|
|
629
|
+
}
|
|
630
|
+
if (kind === "apply_code_review") {
|
|
631
|
+
return [
|
|
632
|
+
"Review only the task-local executor output and current diff.",
|
|
633
|
+
"Stop after implementation code-review report.",
|
|
634
|
+
"Do not write GREEN evidence or task completion state.",
|
|
635
|
+
];
|
|
636
|
+
}
|
|
637
|
+
return [
|
|
638
|
+
"Verify the accepted worker chain and GREEN evidence only.",
|
|
639
|
+
"Stop after verifier report.",
|
|
640
|
+
"Do not mark tasks complete or enter change-level review.",
|
|
641
|
+
];
|
|
642
|
+
}
|
|
643
|
+
function apply_worker_packet_fingerprint(packet) {
|
|
644
|
+
const { generated_at, guard_fingerprint, block_reasons, apply_worker_chain_refs, apply_worker_chain_active_refs, ...stable } = packet;
|
|
645
|
+
const blockerCodes = Array.isArray(block_reasons) ? block_reasons.map((item) => item.code).sort() : [];
|
|
646
|
+
return fingerprint_obj(without_audit_metadata({ ...stable, blocker_codes: blockerCodes }));
|
|
647
|
+
}
|
|
648
|
+
function finalize_apply_worker_packet(packet) {
|
|
649
|
+
packet.guard_fingerprint = apply_worker_packet_fingerprint(packet);
|
|
650
|
+
return packet;
|
|
651
|
+
}
|
|
652
|
+
function apply_packet_common(ctx, packetKind, taskId, workerChainContext, blockers) {
|
|
653
|
+
const applyReady = evaluate_workflow_decision(ctx, "apply_ready");
|
|
654
|
+
const packet = {
|
|
655
|
+
packet_kind: packetKind,
|
|
656
|
+
change: ctx.change,
|
|
657
|
+
task_id: taskId,
|
|
658
|
+
generated_at: new Date().toISOString(),
|
|
659
|
+
worker_chain_context: workerChainContext,
|
|
660
|
+
worker_state: blockers.length === 0 ? "ready" : "blocked",
|
|
661
|
+
apply_ready: decision_status(applyReady),
|
|
662
|
+
openspec_context_file_refs: collect_change_refs(ctx.changeRoot, [
|
|
663
|
+
"proposal.md",
|
|
664
|
+
"design.md",
|
|
665
|
+
"tasks.md",
|
|
666
|
+
".superspec/artifacts/business-invariants.md",
|
|
667
|
+
".superspec/artifacts/test-contract.md",
|
|
668
|
+
]),
|
|
669
|
+
openspec_dynamic_instructions: openspec_cli_surfaces_for_gate(ctx.change, "task_edit"),
|
|
670
|
+
task_refs: collect_change_refs(ctx.changeRoot, ["tasks.md"]),
|
|
671
|
+
test_contract_refs: collect_change_refs(ctx.changeRoot, [".superspec/artifacts/test-contract.md"]),
|
|
672
|
+
common_worker_report_policy: apply_worker_report_policy(),
|
|
673
|
+
stop_conditions: apply_worker_stop_conditions(packetKind),
|
|
674
|
+
};
|
|
675
|
+
if (blockers.length > 0) {
|
|
676
|
+
packet.blockers = unique_strings(blockers.map((item) => item.code));
|
|
677
|
+
packet.block_reasons = blockers;
|
|
678
|
+
}
|
|
679
|
+
return packet;
|
|
680
|
+
}
|
|
681
|
+
function task_lookup_blockers(ctx, taskId) {
|
|
682
|
+
const task = parse_tasks(ctx.changeRoot)[taskId];
|
|
683
|
+
if (!task)
|
|
684
|
+
return { task: null, blockers: [reason("unknown_task", `task ${taskId} not found`)] };
|
|
685
|
+
return { task, blockers: [] };
|
|
686
|
+
}
|
|
687
|
+
function active_apply_worker_chain_id(ctx, taskId) {
|
|
688
|
+
const active = active_apply_worker_chain(ctx, taskId).active;
|
|
689
|
+
return active ? String(active.apply_worker_chain_id) : null;
|
|
690
|
+
}
|
|
691
|
+
function terminal_apply_worker_chain_valid(ctx, ev, taskId, chainId) {
|
|
692
|
+
const state = String(ev.chain_state ?? "");
|
|
693
|
+
if (state === "closed") {
|
|
694
|
+
return shared_pinned_artifact_ref_reasons(ctx.changeRoot, ev.executor_report_ref, "executor_report_ref", { kind: "worker_report", role: "executor", taskId, chainId }).length === 0
|
|
695
|
+
&& shared_pinned_artifact_ref_reasons(ctx.changeRoot, ev.task_code_review_report_ref, "task_code_review_report_ref", { kind: "worker_report", role: "code-reviewer", taskId, chainId }).length === 0
|
|
696
|
+
&& shared_pinned_artifact_ref_reasons(ctx.changeRoot, ev.verifier_report_ref, "verifier_report_ref", { kind: "worker_report", role: "verifier", taskId, chainId }).length === 0;
|
|
697
|
+
}
|
|
698
|
+
if (state === "abandoned") {
|
|
699
|
+
if ("restored_implementation_fingerprint" in ev && fingerprint_digest(ev.restored_implementation_fingerprint))
|
|
700
|
+
return true;
|
|
701
|
+
if ("serial_takeover_baseline_ref" in ev) {
|
|
702
|
+
return shared_pinned_artifact_ref_reasons(ctx.changeRoot, ev.serial_takeover_baseline_ref, "serial_takeover_baseline_ref", { kind: "status_report", role: "verifier", taskId, chainId }).length === 0
|
|
703
|
+
&& Array.isArray(ev.successor_green_evidence_refs)
|
|
704
|
+
&& ev.successor_green_evidence_refs.length > 0
|
|
705
|
+
&& ev.successor_green_evidence_refs.every((item) => typeof item === "string" && item.length > 0);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
function apply_worker_chain_packet_state(ctx, taskId) {
|
|
711
|
+
const chains = ctx.evidences
|
|
712
|
+
.filter((ev) => isObject(ev) && !ev._invalid && ev.status === "pass" && ev.gate === "task_complete" && ev.kind === "apply_worker_chain" && ev.task_id === taskId)
|
|
713
|
+
.filter((ev) => typeof ev.apply_worker_chain_id === "string" && ev.apply_worker_chain_id);
|
|
714
|
+
const blockers = [];
|
|
715
|
+
const activeByChain = new Map();
|
|
716
|
+
const terminalsByChain = new Map();
|
|
717
|
+
for (const ev of chains) {
|
|
718
|
+
const chainId = String(ev.apply_worker_chain_id ?? "");
|
|
719
|
+
if (String(ev.chain_state ?? "") === "active") {
|
|
720
|
+
const bucket = activeByChain.get(chainId) ?? [];
|
|
721
|
+
bucket.push(ev);
|
|
722
|
+
activeByChain.set(chainId, bucket);
|
|
723
|
+
}
|
|
724
|
+
if (String(ev.chain_state ?? "") === "closed" || String(ev.chain_state ?? "") === "abandoned") {
|
|
725
|
+
const bucket = terminalsByChain.get(chainId) ?? [];
|
|
726
|
+
bucket.push(ev);
|
|
727
|
+
terminalsByChain.set(chainId, bucket);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
const validTerminalChainIds = new Set(chains
|
|
731
|
+
.filter((ev) => String(ev.chain_state ?? "") === "closed" || String(ev.chain_state ?? "") === "abandoned")
|
|
732
|
+
.filter((ev) => terminal_apply_worker_chain_valid(ctx, ev, taskId, String(ev.apply_worker_chain_id ?? "")))
|
|
733
|
+
.map((ev) => String(ev.apply_worker_chain_id ?? ""))
|
|
734
|
+
.filter(Boolean));
|
|
735
|
+
const duplicateActives = [...activeByChain.entries()]
|
|
736
|
+
.filter(([chainId]) => !validTerminalChainIds.has(chainId))
|
|
737
|
+
.map(([, items]) => items)
|
|
738
|
+
.filter((items) => items.length > 1)
|
|
739
|
+
.flatMap((items) => items.map((ev) => String(ev.evidence_id ?? ev.apply_worker_chain_id ?? "")))
|
|
740
|
+
.filter(Boolean)
|
|
741
|
+
.sort();
|
|
742
|
+
const activeCandidates = [...activeByChain.entries()]
|
|
743
|
+
.filter(([chainId]) => !validTerminalChainIds.has(chainId))
|
|
744
|
+
.map(([, items]) => items[0])
|
|
745
|
+
.filter(Boolean);
|
|
746
|
+
blockers.push(...apply_worker_chain_lifecycle_reasons(ctx.repoRoot, ctx.changeRoot, ctx.evidences, taskId).filter((item) => (item.code === "apply_worker_chain_active_conflict"
|
|
747
|
+
|| item.code === "apply_worker_chain_terminal_conflict"
|
|
748
|
+
|| item.code === "apply_worker_chain_terminal_invalid"
|
|
749
|
+
|| item.code === "apply_worker_chain_missing_active")));
|
|
750
|
+
const parallelActives = activeCandidates.length > 1
|
|
751
|
+
? activeCandidates.map((ev) => String(ev.evidence_id ?? ev.apply_worker_chain_id ?? "")).filter(Boolean).sort()
|
|
752
|
+
: [];
|
|
753
|
+
const activeConflicts = [...new Set([...duplicateActives, ...parallelActives])].sort();
|
|
754
|
+
if (activeConflicts.length > 0) {
|
|
755
|
+
blockers.push(reason("apply_worker_chain_active_conflict", `task ${taskId} has conflicting active apply worker chain markers: ${renderList(activeConflicts)}`, activeConflicts));
|
|
756
|
+
}
|
|
757
|
+
const duplicateTerminals = [...terminalsByChain.values()]
|
|
758
|
+
.filter((items) => items.length > 1)
|
|
759
|
+
.flatMap((items) => items.map((ev) => String(ev.evidence_id ?? ev.apply_worker_chain_id ?? "")))
|
|
760
|
+
.filter(Boolean)
|
|
761
|
+
.sort();
|
|
762
|
+
if (duplicateTerminals.length > 0) {
|
|
763
|
+
blockers.push(reason("apply_worker_chain_terminal_conflict", `task ${taskId} has duplicate apply worker chain terminal markers: ${renderList(duplicateTerminals)}`, duplicateTerminals));
|
|
764
|
+
}
|
|
765
|
+
const invalidTerminals = chains
|
|
766
|
+
.filter((ev) => String(ev.chain_state ?? "") === "closed" || String(ev.chain_state ?? "") === "abandoned")
|
|
767
|
+
.filter((ev) => !terminal_apply_worker_chain_valid(ctx, ev, taskId, String(ev.apply_worker_chain_id ?? "")))
|
|
768
|
+
.map((ev) => String(ev.evidence_id ?? ev.apply_worker_chain_id ?? ""))
|
|
769
|
+
.filter(Boolean)
|
|
770
|
+
.sort();
|
|
771
|
+
if (invalidTerminals.length > 0) {
|
|
772
|
+
blockers.push(reason("apply_worker_chain_terminal_invalid", `task ${taskId} has invalid apply worker chain terminal markers: ${renderList(invalidTerminals)}`, invalidTerminals));
|
|
773
|
+
}
|
|
774
|
+
const active = activeCandidates[0] ?? null;
|
|
775
|
+
return { active: blockers.length === 0 ? active : null, blockers };
|
|
776
|
+
}
|
|
777
|
+
function active_apply_worker_chain(ctx, taskId) {
|
|
778
|
+
return apply_worker_chain_packet_state(ctx, taskId);
|
|
779
|
+
}
|
|
780
|
+
function require_active_apply_worker_chain(ctx, taskId, blockers) {
|
|
781
|
+
const state = active_apply_worker_chain(ctx, taskId);
|
|
782
|
+
blockers.push(...state.blockers);
|
|
783
|
+
const active = state.active;
|
|
784
|
+
const chainId = active ? String(active.apply_worker_chain_id) : null;
|
|
785
|
+
if (!chainId) {
|
|
786
|
+
blockers.push(reason("missing_apply_worker_chain_active", `task ${taskId} requires an active apply_worker_chain marker before downstream worker packet generation`));
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
blockers.push(...pre_edit_evidence_ref_reasons(ctx.evidences, active, taskId, "apply_worker_chain_active_invalid"));
|
|
790
|
+
}
|
|
791
|
+
return chainId;
|
|
792
|
+
}
|
|
793
|
+
function generated_apply_worker_chain_id(ctx, taskId, writeScope = []) {
|
|
794
|
+
const digest = sha256_text(`${ctx.change}\n${taskId}\n${writeScope.join("\n")}`).slice("sha256:".length, "sha256:".length + 16);
|
|
795
|
+
const base = `CHAIN-${taskId}-${digest}`;
|
|
796
|
+
const used = new Set(ctx.evidences
|
|
797
|
+
.filter((ev) => isObject(ev) && ev.kind === "apply_worker_chain" && ev.task_id === taskId && typeof ev.apply_worker_chain_id === "string")
|
|
798
|
+
.map((ev) => String(ev.apply_worker_chain_id)));
|
|
799
|
+
if (!used.has(base))
|
|
800
|
+
return base;
|
|
801
|
+
for (let idx = 2; idx < 1000; idx += 1) {
|
|
802
|
+
const candidate = `${base}-R${idx}`;
|
|
803
|
+
if (!used.has(candidate))
|
|
804
|
+
return candidate;
|
|
805
|
+
}
|
|
806
|
+
return `${base}-R${sha256_text([...used].sort().join("\n")).slice("sha256:".length, "sha256:".length + 8)}`;
|
|
807
|
+
}
|
|
808
|
+
function implementation_fingerprint(ctx, declaredTaskWriteScope = []) {
|
|
809
|
+
return apply_worker_implementation_fingerprint(ctx.repoRoot, ctx.changeRoot, [], { declaredTaskWriteScope });
|
|
810
|
+
}
|
|
811
|
+
function read_json_ref(ctx, ref) {
|
|
812
|
+
const target = safe_within(ctx.changeRoot, ref);
|
|
813
|
+
if (target === null)
|
|
814
|
+
return { value: null, blockers: [reason("ref_path_unsafe", `ref escapes change root: ${ref}`, [ref])] };
|
|
815
|
+
if (!existsSync(target) || !statSync(target).isFile())
|
|
816
|
+
return { value: null, blockers: [reason("ref_not_readable", `ref is not readable: ${ref}`, [ref])] };
|
|
817
|
+
if (statSync(target).size <= 0)
|
|
818
|
+
return { value: null, blockers: [reason("ref_empty", `ref is empty: ${ref}`, [ref])] };
|
|
819
|
+
try {
|
|
820
|
+
const value = JSON.parse(readFileSync(target, "utf8"));
|
|
821
|
+
return isObject(value) ? { value, blockers: [] } : { value: null, blockers: [reason("ref_type_mismatch", `ref must contain a JSON object: ${ref}`, [ref])] };
|
|
822
|
+
}
|
|
823
|
+
catch {
|
|
824
|
+
return { value: null, blockers: [reason("ref_type_mismatch", `ref must contain valid JSON: ${ref}`, [ref])] };
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
function validate_pinned_artifact_ref_object(ctx, item, label, expected, code = "pinned_artifact_ref_invalid") {
|
|
828
|
+
return shared_pinned_artifact_ref_reasons(ctx.changeRoot, item, label, {
|
|
829
|
+
kind: expected.kind ?? "worker_report",
|
|
830
|
+
role: expected.role,
|
|
831
|
+
taskId: expected.taskId,
|
|
832
|
+
chainId: expected.chainId,
|
|
833
|
+
}, code);
|
|
834
|
+
}
|
|
835
|
+
function validate_pinned_artifact_ref(ctx, ref, expected) {
|
|
836
|
+
const loaded = read_json_ref(ctx, ref);
|
|
837
|
+
if (loaded.blockers.length > 0 || !loaded.value)
|
|
838
|
+
return { ref: null, blockers: loaded.blockers };
|
|
839
|
+
const item = loaded.value;
|
|
840
|
+
const blockers = validate_pinned_artifact_ref_object(ctx, item, ref, expected);
|
|
841
|
+
return { ref: blockers.length === 0 ? item : null, blockers };
|
|
842
|
+
}
|
|
843
|
+
function validate_worker_report_refs(ctx, refs, expected) {
|
|
844
|
+
if (refs.length === 0)
|
|
845
|
+
return { refs: [], blockers: [reason(expected.missingCode, expected.missingMessage)] };
|
|
846
|
+
const resolved = [];
|
|
847
|
+
const blockers = [];
|
|
848
|
+
for (const ref of refs) {
|
|
849
|
+
const result = validate_pinned_artifact_ref(ctx, ref, { role: expected.role, taskId: expected.taskId, chainId: expected.chainId });
|
|
850
|
+
blockers.push(...result.blockers);
|
|
851
|
+
if (result.ref)
|
|
852
|
+
resolved.push(result.ref);
|
|
853
|
+
}
|
|
854
|
+
return { refs: resolved, blockers };
|
|
855
|
+
}
|
|
856
|
+
function worker_report_origin_blockers(refItem, expectedOrigin, label) {
|
|
857
|
+
if (!isObject(refItem) || typeof expectedOrigin !== "string" || !expectedOrigin.startsWith("sha256:"))
|
|
858
|
+
return [];
|
|
859
|
+
return refItem.origin_packet_fingerprint === expectedOrigin
|
|
860
|
+
? []
|
|
861
|
+
: [reason("worker_report_origin_mismatch", `${label} origin_packet_fingerprint must match active apply_worker_chain executor_packet_fingerprint`, [])];
|
|
862
|
+
}
|
|
863
|
+
function worker_report_input_blockers(refItem, refs, label) {
|
|
864
|
+
if (!isObject(refItem))
|
|
865
|
+
return [];
|
|
866
|
+
const expected = worker_input_ref_digest(refs);
|
|
867
|
+
return refItem.input_ref_digest === expected
|
|
868
|
+
? []
|
|
869
|
+
: [reason("worker_report_input_ref_mismatch", `${label} input_ref_digest must match upstream refs`, [])];
|
|
870
|
+
}
|
|
871
|
+
function worker_report_input_digest_blockers(refItem, expectedDigest, label) {
|
|
872
|
+
if (!isObject(refItem))
|
|
873
|
+
return [];
|
|
874
|
+
return refItem.input_ref_digest === expectedDigest
|
|
875
|
+
? []
|
|
876
|
+
: [reason("worker_report_input_ref_mismatch", `${label} input_ref_digest must match active executor packet inputs`, [])];
|
|
877
|
+
}
|
|
878
|
+
function code_review_executor_binding_blockers(ctx, codeReviewRef, activeChain, taskId, chainId) {
|
|
879
|
+
const blockers = [];
|
|
880
|
+
const report = read_pinned_artifact_json(ctx.changeRoot, codeReviewRef);
|
|
881
|
+
if (!report)
|
|
882
|
+
return [reason("worker_report_content_invalid", "task_code_review_report_ref content must be readable before spawning GREEN test-runner", [])];
|
|
883
|
+
const executorReportRef = report.executor_report_ref;
|
|
884
|
+
blockers.push(...validate_pinned_artifact_ref_object(ctx, executorReportRef, "task_code_review_report_ref.executor_report_ref", {
|
|
885
|
+
role: "executor",
|
|
886
|
+
taskId,
|
|
887
|
+
chainId,
|
|
888
|
+
}));
|
|
889
|
+
if (isObject(executorReportRef)) {
|
|
890
|
+
blockers.push(...worker_report_origin_blockers(executorReportRef, activeChain?.executor_packet_fingerprint, "task_code_review_report_ref.executor_report_ref"));
|
|
891
|
+
if (activeChain) {
|
|
892
|
+
blockers.push(...worker_report_input_digest_blockers(executorReportRef, apply_worker_executor_input_ref_digest(ctx.evidences, activeChain), "task_code_review_report_ref.executor_report_ref"));
|
|
893
|
+
}
|
|
894
|
+
blockers.push(...worker_report_input_blockers(codeReviewRef, [executorReportRef], "task_code_review_report_ref"));
|
|
895
|
+
}
|
|
896
|
+
return blockers;
|
|
897
|
+
}
|
|
898
|
+
function validate_executor_worker_test_run_refs(ctx, ev, ref, taskId, chainId) {
|
|
899
|
+
return worker_test_run_reasons(ctx.changeRoot, ev, taskId, chainId, "pinned_evidence_ref_invalid")
|
|
900
|
+
.map((item) => ({ ...item, refs: [ref] }));
|
|
901
|
+
}
|
|
902
|
+
function validate_test_run_evidence_refs(ctx, refs, expected) {
|
|
903
|
+
if (refs.length === 0)
|
|
904
|
+
return { refs: [], blockers: [reason(expected.missingCode, expected.missingMessage)] };
|
|
905
|
+
const resolved = [];
|
|
906
|
+
const blockers = [];
|
|
907
|
+
for (const ref of refs) {
|
|
908
|
+
let evidenceId = ref;
|
|
909
|
+
const maybePath = safe_within(ctx.changeRoot, ref);
|
|
910
|
+
if (maybePath !== null && existsSync(maybePath) && statSync(maybePath).isFile()) {
|
|
911
|
+
const loaded = read_json_ref(ctx, ref);
|
|
912
|
+
blockers.push(...loaded.blockers);
|
|
913
|
+
if (loaded.value) {
|
|
914
|
+
if (loaded.value.kind !== "test_run")
|
|
915
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: kind must be test_run`, [ref]));
|
|
916
|
+
if (normalize_gate(String(loaded.value.gate ?? "")) !== expected.gate)
|
|
917
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: gate must be ${expected.gate}`, [ref]));
|
|
918
|
+
if (loaded.value.task_id !== expected.taskId)
|
|
919
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: task_id must be ${expected.taskId}`, [ref]));
|
|
920
|
+
if (loaded.value.phase !== undefined && loaded.value.phase !== expected.phase)
|
|
921
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: phase must be ${expected.phase}`, [ref]));
|
|
922
|
+
if (loaded.value.semantic_status !== expected.semanticStatus)
|
|
923
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: semantic_status must be ${expected.semanticStatus}`, [ref]));
|
|
924
|
+
if (expected.chainId && loaded.value.apply_worker_chain_id !== expected.chainId)
|
|
925
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: apply_worker_chain_id mismatch`, [ref]));
|
|
926
|
+
if (typeof loaded.value.evidence_id === "string" && loaded.value.evidence_id)
|
|
927
|
+
evidenceId = loaded.value.evidence_id;
|
|
928
|
+
if (expected.chainId)
|
|
929
|
+
blockers.push(...validate_executor_worker_test_run_refs(ctx, loaded.value, ref, expected.taskId, expected.chainId));
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
const ev = live_pass(ctx.evidences, { kind: "test_run", task_id: expected.taskId })
|
|
933
|
+
.find((item) => String(item.evidence_id ?? "") === evidenceId);
|
|
934
|
+
if (!ev) {
|
|
935
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `test_run evidence ref is not live/pass for ${expected.taskId}: ${ref}`, [ref]));
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
if (normalize_gate(String(ev.gate ?? "")) !== expected.gate)
|
|
939
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: gate must be ${expected.gate}`, [ref]));
|
|
940
|
+
if (ev.semantic_status !== expected.semanticStatus)
|
|
941
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: semantic_status must be ${expected.semanticStatus}`, [ref]));
|
|
942
|
+
if (ev.phase !== undefined && ev.phase !== expected.phase)
|
|
943
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: phase must be ${expected.phase}`, [ref]));
|
|
944
|
+
if (expected.chainId) {
|
|
945
|
+
if (ev.apply_execution_chain !== "executor_worker")
|
|
946
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: apply_execution_chain must be executor_worker`, [ref]));
|
|
947
|
+
if (ev.apply_worker_chain_id !== expected.chainId)
|
|
948
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: apply_worker_chain_id mismatch`, [ref]));
|
|
949
|
+
blockers.push(...validate_executor_worker_test_run_refs(ctx, ev, ref, expected.taskId, expected.chainId));
|
|
950
|
+
}
|
|
951
|
+
resolved.push(ev);
|
|
952
|
+
}
|
|
953
|
+
return { refs: resolved, blockers };
|
|
954
|
+
}
|
|
955
|
+
function validate_active_apply_worker_chain_refs(ctx, refs, taskId, expectedChainId, expectedPacketFingerprint, expectedSourceImplementationFingerprint, expectedDeclaredTaskWriteScope, expectedPreEditEvidenceRefs) {
|
|
956
|
+
if (refs.length === 0) {
|
|
957
|
+
return {
|
|
958
|
+
refs: [],
|
|
959
|
+
blockers: [reason("missing_apply_worker_chain_ref", "apply-executor-packet --format prompt requires --apply-worker-chain-ref pointing at the recorded active apply_worker_chain evidence")],
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
const resolved = [];
|
|
963
|
+
const blockers = [];
|
|
964
|
+
const hasTerminal = ctx.evidences.some((ev) => isObject(ev)
|
|
965
|
+
&& !ev._invalid
|
|
966
|
+
&& ev.status === "pass"
|
|
967
|
+
&& ev.gate === "task_complete"
|
|
968
|
+
&& ev.kind === "apply_worker_chain"
|
|
969
|
+
&& ev.task_id === taskId
|
|
970
|
+
&& ev.apply_worker_chain_id === expectedChainId
|
|
971
|
+
&& (ev.chain_state === "closed" || ev.chain_state === "abandoned"));
|
|
972
|
+
if (hasTerminal) {
|
|
973
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `apply_worker_chain ${expectedChainId} already has a terminal marker and cannot authorize executor spawn`, [expectedChainId]));
|
|
974
|
+
}
|
|
975
|
+
const stringList = (value) => Array.isArray(value)
|
|
976
|
+
? value.map(String).filter(Boolean).sort()
|
|
977
|
+
: [];
|
|
978
|
+
const activeListBindingBlockers = (value, ref) => {
|
|
979
|
+
const fieldBlockers = [];
|
|
980
|
+
if (!deepEqual(stringList(value.declared_task_write_scope), stringList(expectedDeclaredTaskWriteScope))) {
|
|
981
|
+
fieldBlockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: declared_task_write_scope mismatch`, [ref]));
|
|
982
|
+
}
|
|
983
|
+
if (!deepEqual(stringList(value.pre_edit_evidence_refs), stringList(expectedPreEditEvidenceRefs))) {
|
|
984
|
+
fieldBlockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: pre_edit_evidence_refs mismatch`, [ref]));
|
|
985
|
+
}
|
|
986
|
+
return fieldBlockers;
|
|
987
|
+
};
|
|
988
|
+
for (const ref of refs) {
|
|
989
|
+
let evidenceId = ref;
|
|
990
|
+
const maybePath = safe_within(ctx.changeRoot, ref);
|
|
991
|
+
if (maybePath !== null && existsSync(maybePath) && statSync(maybePath).isFile()) {
|
|
992
|
+
const loaded = read_json_ref(ctx, ref);
|
|
993
|
+
blockers.push(...loaded.blockers);
|
|
994
|
+
if (loaded.value) {
|
|
995
|
+
if (loaded.value.kind !== "apply_worker_chain")
|
|
996
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: kind must be apply_worker_chain`, [ref]));
|
|
997
|
+
if (normalize_gate(String(loaded.value.gate ?? "")) !== "task_complete")
|
|
998
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: gate must be task_complete`, [ref]));
|
|
999
|
+
if (loaded.value.task_id !== taskId)
|
|
1000
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: task_id must be ${taskId}`, [ref]));
|
|
1001
|
+
if (loaded.value.chain_state !== "active")
|
|
1002
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: chain_state must be active`, [ref]));
|
|
1003
|
+
if (loaded.value.apply_worker_chain_id !== expectedChainId)
|
|
1004
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: apply_worker_chain_id mismatch`, [ref]));
|
|
1005
|
+
if (loaded.value.executor_packet_fingerprint !== expectedPacketFingerprint)
|
|
1006
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: executor_packet_fingerprint mismatch`, [ref]));
|
|
1007
|
+
if (!fingerprint_matches(loaded.value.source_implementation_fingerprint, expectedSourceImplementationFingerprint)) {
|
|
1008
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: source_implementation_fingerprint mismatch`, [ref]));
|
|
1009
|
+
}
|
|
1010
|
+
blockers.push(...activeListBindingBlockers(loaded.value, ref));
|
|
1011
|
+
if (typeof loaded.value.evidence_id === "string" && loaded.value.evidence_id)
|
|
1012
|
+
evidenceId = loaded.value.evidence_id;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
const ev = live_pass(ctx.evidences, { gate: "task_complete", kind: "apply_worker_chain", task_id: taskId })
|
|
1016
|
+
.find((item) => String(item.evidence_id ?? "") === evidenceId);
|
|
1017
|
+
if (!ev) {
|
|
1018
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `apply_worker_chain ref is not live/pass for ${taskId}: ${ref}`, [ref]));
|
|
1019
|
+
continue;
|
|
1020
|
+
}
|
|
1021
|
+
if (ev.chain_state !== "active")
|
|
1022
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: chain_state must be active`, [ref]));
|
|
1023
|
+
if (ev.apply_worker_chain_id !== expectedChainId)
|
|
1024
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: apply_worker_chain_id mismatch`, [ref]));
|
|
1025
|
+
if (ev.executor_packet_fingerprint !== expectedPacketFingerprint)
|
|
1026
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: executor_packet_fingerprint mismatch`, [ref]));
|
|
1027
|
+
if (!fingerprint_matches(ev.source_implementation_fingerprint, expectedSourceImplementationFingerprint)) {
|
|
1028
|
+
blockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: source_implementation_fingerprint mismatch`, [ref]));
|
|
1029
|
+
}
|
|
1030
|
+
blockers.push(...activeListBindingBlockers(ev, ref));
|
|
1031
|
+
resolved.push(ev);
|
|
1032
|
+
}
|
|
1033
|
+
return { refs: resolved, blockers };
|
|
1034
|
+
}
|
|
1035
|
+
function pre_edit_refs_bound_to_active_chain(ctx, active, refs, label, taskId) {
|
|
1036
|
+
const allowed = new Set(Array.isArray(active?.pre_edit_evidence_refs) ? active.pre_edit_evidence_refs.map(String) : []);
|
|
1037
|
+
if (allowed.size === 0)
|
|
1038
|
+
return [reason("pinned_evidence_ref_invalid", `task ${taskId} active apply_worker_chain has no pre_edit_evidence_refs`)];
|
|
1039
|
+
const blockers = [];
|
|
1040
|
+
for (const ev of refs) {
|
|
1041
|
+
const evidenceId = String(ev.evidence_id ?? "");
|
|
1042
|
+
if (!allowed.has(evidenceId))
|
|
1043
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${label} evidence ref is not part of active apply_worker_chain pre_edit_evidence_refs: ${evidenceId}`, [evidenceId]));
|
|
1044
|
+
}
|
|
1045
|
+
blockers.push(...pre_edit_evidence_ref_reasons(ctx.evidences, active, taskId, "pinned_evidence_ref_invalid"));
|
|
1046
|
+
return blockers;
|
|
1047
|
+
}
|
|
1048
|
+
function write_scope_blockers(taskId, writeScope) {
|
|
1049
|
+
const blockers = [];
|
|
1050
|
+
if (writeScope.length === 0) {
|
|
1051
|
+
blockers.push(reason("missing_write_scope", `task ${taskId} requires declared write_scope before executor worker handoff`));
|
|
1052
|
+
return blockers;
|
|
1053
|
+
}
|
|
1054
|
+
const forbidden = writeScope.filter((item) => item === "."
|
|
1055
|
+
|| item === ""
|
|
1056
|
+
|| item.startsWith("/")
|
|
1057
|
+
|| item.includes("..")
|
|
1058
|
+
|| item === "proposal.md"
|
|
1059
|
+
|| item === "design.md"
|
|
1060
|
+
|| item === "tasks.md"
|
|
1061
|
+
|| item.startsWith("specs/")
|
|
1062
|
+
|| item.startsWith(".superspec/"));
|
|
1063
|
+
if (forbidden.length > 0) {
|
|
1064
|
+
blockers.push(reason("unsafe_write_scope", `task ${taskId} has unsafe executor write_scope entries: ${renderList(forbidden)}`, forbidden));
|
|
1065
|
+
}
|
|
1066
|
+
return blockers;
|
|
1067
|
+
}
|
|
1068
|
+
function pre_edit_evidence_refs(ctx, taskId) {
|
|
1069
|
+
return live_pass(ctx.evidences, { gate: "task_edit", kind: "test_run", task_id: taskId })
|
|
1070
|
+
.filter((ev) => ev.semantic_status === "expected_failure" || ev.semantic_status === "expected_success")
|
|
1071
|
+
.map((ev) => String(ev.evidence_id ?? ""))
|
|
1072
|
+
.filter(Boolean)
|
|
1073
|
+
.sort();
|
|
1074
|
+
}
|
|
1075
|
+
function apply_task_context_fields(ctx, taskId, task, writeScope, expectedGuardRefs = []) {
|
|
1076
|
+
return {
|
|
1077
|
+
task_content_ref: change_pinned_ref(ctx.changeRoot, "tasks.md"),
|
|
1078
|
+
task_acceptance_refs: task ? splitList(task.attrs.requirement_refs ?? "") : [],
|
|
1079
|
+
task_invariant_refs: task ? splitList(task.attrs.invariant_refs ?? "") : [],
|
|
1080
|
+
task_test_refs: task ? splitList(task.attrs.test_refs ?? "") : [],
|
|
1081
|
+
business_invariant_refs: collect_change_refs(ctx.changeRoot, [".superspec/artifacts/business-invariants.md"]),
|
|
1082
|
+
test_contract_refs: collect_change_refs(ctx.changeRoot, [".superspec/artifacts/test-contract.md"]),
|
|
1083
|
+
pre_edit_evidence_refs: pre_edit_evidence_refs(ctx, taskId),
|
|
1084
|
+
current_worktree_refs: dirty_repo_refs(ctx, `apply worker current worktree refs for ${taskId}`),
|
|
1085
|
+
protected_path_refs: apply_worker_protected_path_refs(ctx.repoRoot, ctx.changeRoot, expectedGuardRefs),
|
|
1086
|
+
scope_diff_review_policy: {
|
|
1087
|
+
declared_task_write_scope: writeScope,
|
|
1088
|
+
forbidden_paths: ["proposal.md", "design.md", "tasks.md", "specs/", ".superspec/"],
|
|
1089
|
+
require_scope_verdict: true,
|
|
1090
|
+
mismatch_behavior: "fail_closed",
|
|
1091
|
+
},
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
function apply_test_packet(ctx, args) {
|
|
1095
|
+
const taskId = args.task_id ?? "";
|
|
1096
|
+
const testId = args.test_id ?? "";
|
|
1097
|
+
const phase = args.phase ?? "red";
|
|
1098
|
+
const blockers = [];
|
|
1099
|
+
const applyReady = evaluate_workflow_decision(ctx, "apply_ready");
|
|
1100
|
+
if (!applyReady.allowed)
|
|
1101
|
+
blockers.push(...decision_reasons(applyReady));
|
|
1102
|
+
const { task, blockers: taskBlockers } = task_lookup_blockers(ctx, taskId);
|
|
1103
|
+
blockers.push(...taskBlockers);
|
|
1104
|
+
const declaredTests = new Set(task ? splitList(task.attrs.test_refs ?? "") : []);
|
|
1105
|
+
if (task && !declaredTests.has(testId)) {
|
|
1106
|
+
blockers.push(reason("test_not_declared_for_task", `test ${testId} is not declared in task ${taskId} test_refs`, [testId]));
|
|
1107
|
+
}
|
|
1108
|
+
const [config, configProblems] = load_config(ctx.repoRoot, ctx.changeRoot);
|
|
1109
|
+
blockers.push(...configProblems);
|
|
1110
|
+
const command = resolve_test_contract_command(ctx.changeRoot, testId, config);
|
|
1111
|
+
blockers.push(...command.blockers);
|
|
1112
|
+
const invariantRefs = [...(test_contract_invariant_refs_by_test(ctx.changeRoot).get(testId) ?? new Set())].sort();
|
|
1113
|
+
if (invariantRefs.length === 0)
|
|
1114
|
+
blockers.push(reason("missing_invariant_refs", `test ${testId} requires invariant refs in test contract`, [testId]));
|
|
1115
|
+
if (phase === "green") {
|
|
1116
|
+
const taskEdit = evaluate_workflow_decision(ctx, "task_edit", taskId);
|
|
1117
|
+
if (!taskEdit.allowed)
|
|
1118
|
+
blockers.push(...decision_reasons(taskEdit));
|
|
1119
|
+
if ((args.task_code_review_report_refs ?? []).length === 0) {
|
|
1120
|
+
blockers.push(reason("missing_task_code_review_report_ref", "apply-test-packet --phase green requires --task-code-review-report-ref before spawning test-runner"));
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
else if ((args.task_code_review_report_refs ?? []).length > 0) {
|
|
1124
|
+
blockers.push(reason("unexpected_task_code_review_report_ref", "apply-test-packet --phase red/characterization must not consume --task-code-review-report-ref"));
|
|
1125
|
+
}
|
|
1126
|
+
const workerChainContext = phase === "green" ? "executor_worker" : "none";
|
|
1127
|
+
const packet = apply_packet_common(ctx, "apply_test", taskId, workerChainContext, blockers);
|
|
1128
|
+
packet.test_id = testId;
|
|
1129
|
+
packet.phase = phase;
|
|
1130
|
+
packet.expected_semantic_status = phase === "red" ? "expected_failure" : "expected_success";
|
|
1131
|
+
packet.test_runner_report_required_fields = [...APPLY_TEST_RUNNER_REPORT_REQUIRED_FIELDS];
|
|
1132
|
+
packet.required_invariant_refs = invariantRefs;
|
|
1133
|
+
packet.allowed_test_command = command.command;
|
|
1134
|
+
packet.test_command_source = command.source;
|
|
1135
|
+
packet.expected_worktree_side_effects = [];
|
|
1136
|
+
if (command.command_ref)
|
|
1137
|
+
packet.test_command_ref = command.command_ref;
|
|
1138
|
+
if (command.expected_failure_signature)
|
|
1139
|
+
packet.expected_failure_signature = command.expected_failure_signature;
|
|
1140
|
+
if (command.expected_failure_classifier)
|
|
1141
|
+
packet.expected_failure_classifier = command.expected_failure_classifier;
|
|
1142
|
+
if (workerChainContext === "executor_worker") {
|
|
1143
|
+
const chainId = require_active_apply_worker_chain(ctx, taskId, blockers);
|
|
1144
|
+
if (chainId) {
|
|
1145
|
+
const reviewRefs = validate_worker_report_refs(ctx, args.task_code_review_report_refs ?? [], {
|
|
1146
|
+
role: "code-reviewer",
|
|
1147
|
+
taskId,
|
|
1148
|
+
chainId,
|
|
1149
|
+
missingCode: "missing_task_code_review_report_ref",
|
|
1150
|
+
missingMessage: "apply-test-packet --phase green requires --task-code-review-report-ref before spawning test-runner",
|
|
1151
|
+
});
|
|
1152
|
+
blockers.push(...reviewRefs.blockers);
|
|
1153
|
+
packet.apply_worker_chain_id = chainId;
|
|
1154
|
+
packet.task_code_review_report_pinned_refs = reviewRefs.refs;
|
|
1155
|
+
packet.task_code_review_report_refs = args.task_code_review_report_refs ?? [];
|
|
1156
|
+
const activeChain = active_apply_worker_chain(ctx, taskId).active;
|
|
1157
|
+
if (reviewRefs.refs[0]) {
|
|
1158
|
+
const reviewReport = read_pinned_artifact_json(ctx.changeRoot, reviewRefs.refs[0]);
|
|
1159
|
+
if (isObject(reviewReport?.executor_report_ref))
|
|
1160
|
+
packet.executor_report_pinned_refs = [reviewReport.executor_report_ref];
|
|
1161
|
+
blockers.push(...code_review_executor_binding_blockers(ctx, reviewRefs.refs[0], activeChain, taskId, chainId));
|
|
1162
|
+
}
|
|
1163
|
+
const activeScope = Array.isArray(activeChain?.declared_task_write_scope) ? activeChain.declared_task_write_scope.map(String).filter(Boolean) : [];
|
|
1164
|
+
const currentImplementation = apply_worker_implementation_fingerprint(ctx.repoRoot, ctx.changeRoot, reviewRefs.refs, { declaredTaskWriteScope: activeScope });
|
|
1165
|
+
packet.post_code_review_worktree_fingerprint = currentImplementation;
|
|
1166
|
+
const observedDigest = fingerprint_digest(reviewRefs.refs[0]?.observed_implementation_fingerprint);
|
|
1167
|
+
if (!observedDigest) {
|
|
1168
|
+
blockers.push(reason("post_code_review_worktree_fingerprint_missing", "apply-test-packet --phase green requires code-review report observed_implementation_fingerprint"));
|
|
1169
|
+
}
|
|
1170
|
+
else if (observedDigest !== currentImplementation.fingerprint_digest) {
|
|
1171
|
+
blockers.push(reason("post_code_review_worktree_fingerprint_mismatch", "apply-test-packet --phase green requires code-review report observed implementation fingerprint to match current worktree"));
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
else if ((args.task_code_review_report_refs ?? []).length === 0) {
|
|
1175
|
+
blockers.push(reason("missing_task_code_review_report_ref", "apply-test-packet --phase green requires --task-code-review-report-ref before spawning test-runner"));
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
packet.worker_state = blockers.length === 0 ? "ready" : "blocked";
|
|
1179
|
+
if (blockers.length > 0) {
|
|
1180
|
+
packet.blockers = unique_strings(blockers.map((item) => item.code));
|
|
1181
|
+
packet.block_reasons = blockers;
|
|
1182
|
+
}
|
|
1183
|
+
return finalize_apply_worker_packet(packet);
|
|
1184
|
+
}
|
|
1185
|
+
function apply_executor_packet(ctx, args) {
|
|
1186
|
+
const taskId = args.task_id ?? "";
|
|
1187
|
+
const blockers = [];
|
|
1188
|
+
const applyReady = evaluate_workflow_decision(ctx, "apply_ready");
|
|
1189
|
+
if (!applyReady.allowed)
|
|
1190
|
+
blockers.push(...decision_reasons(applyReady));
|
|
1191
|
+
const taskEdit = evaluate_workflow_decision(ctx, "task_edit", taskId);
|
|
1192
|
+
if (!taskEdit.allowed)
|
|
1193
|
+
blockers.push(...decision_reasons(taskEdit));
|
|
1194
|
+
const { task, blockers: taskBlockers } = task_lookup_blockers(ctx, taskId);
|
|
1195
|
+
blockers.push(...taskBlockers);
|
|
1196
|
+
const writeScope = task ? splitList(task.attrs.write_scope ?? "") : [];
|
|
1197
|
+
blockers.push(...write_scope_blockers(taskId, writeScope));
|
|
1198
|
+
if (task && (task.attrs.tdd_required ?? "true").toLowerCase() === "false") {
|
|
1199
|
+
blockers.push(reason("unsupported_executor_tdd_mode", `task ${taskId} has tdd_required:false and must stay on main-thread apply path`));
|
|
1200
|
+
}
|
|
1201
|
+
blockers.push(...apply_worker_chain_packet_state(ctx, taskId).blockers);
|
|
1202
|
+
const packet = apply_packet_common(ctx, "apply_executor", taskId, "executor_worker", blockers);
|
|
1203
|
+
const chainId = active_apply_worker_chain_id(ctx, taskId) ?? generated_apply_worker_chain_id(ctx, taskId, writeScope);
|
|
1204
|
+
packet.worker_chain_context = "executor_worker";
|
|
1205
|
+
packet.apply_worker_chain_id = chainId;
|
|
1206
|
+
packet.declared_task_write_scope = writeScope;
|
|
1207
|
+
packet.task_edit = decision_status(taskEdit);
|
|
1208
|
+
packet.task_complete = safe_decision_status(ctx, "task_complete", taskId);
|
|
1209
|
+
Object.assign(packet, apply_task_context_fields(ctx, taskId, task, writeScope));
|
|
1210
|
+
packet.required_test_refs = task ? splitList(task.attrs.test_refs ?? "") : [];
|
|
1211
|
+
packet.executor_report_required_fields = [...APPLY_EXECUTOR_REPORT_REQUIRED_FIELDS];
|
|
1212
|
+
packet.executor_runtime_policy = {
|
|
1213
|
+
initial_wait_seconds: 60,
|
|
1214
|
+
max_running_seconds: 7200,
|
|
1215
|
+
status_request_after_seconds: 300,
|
|
1216
|
+
max_silent_seconds: 900,
|
|
1217
|
+
stale_after_seconds: 1800,
|
|
1218
|
+
};
|
|
1219
|
+
packet.chain_activation_template = {
|
|
1220
|
+
kind: "apply_worker_chain",
|
|
1221
|
+
gate: "task_complete",
|
|
1222
|
+
status: "pass",
|
|
1223
|
+
task_id: taskId,
|
|
1224
|
+
apply_worker_chain_id: chainId,
|
|
1225
|
+
chain_state: "active",
|
|
1226
|
+
executor_packet_fingerprint: "",
|
|
1227
|
+
source_implementation_fingerprint: implementation_fingerprint(ctx, writeScope),
|
|
1228
|
+
declared_task_write_scope: writeScope,
|
|
1229
|
+
pre_edit_evidence_refs: pre_edit_evidence_refs(ctx, taskId),
|
|
1230
|
+
};
|
|
1231
|
+
if (args.packet_format === "prompt") {
|
|
1232
|
+
const expectedPacketFingerprint = apply_worker_packet_fingerprint(packet);
|
|
1233
|
+
const activeRefs = validate_active_apply_worker_chain_refs(ctx, args.apply_worker_chain_refs ?? [], taskId, chainId, expectedPacketFingerprint, packet.chain_activation_template.source_implementation_fingerprint, packet.chain_activation_template.declared_task_write_scope, packet.chain_activation_template.pre_edit_evidence_refs);
|
|
1234
|
+
blockers.push(...activeRefs.blockers);
|
|
1235
|
+
packet.apply_worker_chain_refs = args.apply_worker_chain_refs ?? [];
|
|
1236
|
+
packet.apply_worker_chain_active_refs = activeRefs.refs;
|
|
1237
|
+
}
|
|
1238
|
+
packet.worker_state = blockers.length === 0 ? "ready" : "blocked";
|
|
1239
|
+
if (blockers.length > 0) {
|
|
1240
|
+
packet.blockers = unique_strings(blockers.map((item) => item.code));
|
|
1241
|
+
packet.block_reasons = blockers;
|
|
1242
|
+
}
|
|
1243
|
+
finalize_apply_worker_packet(packet);
|
|
1244
|
+
packet.chain_activation_template.executor_packet_fingerprint = packet.guard_fingerprint;
|
|
1245
|
+
return packet;
|
|
1246
|
+
}
|
|
1247
|
+
function apply_code_review_packet(ctx, args) {
|
|
1248
|
+
const taskId = args.task_id ?? "";
|
|
1249
|
+
const blockers = [];
|
|
1250
|
+
const applyReady = evaluate_workflow_decision(ctx, "apply_ready");
|
|
1251
|
+
if (!applyReady.allowed)
|
|
1252
|
+
blockers.push(...decision_reasons(applyReady));
|
|
1253
|
+
const taskEdit = evaluate_workflow_decision(ctx, "task_edit", taskId);
|
|
1254
|
+
if (!taskEdit.allowed)
|
|
1255
|
+
blockers.push(...decision_reasons(taskEdit));
|
|
1256
|
+
const { task, blockers: taskBlockers } = task_lookup_blockers(ctx, taskId);
|
|
1257
|
+
blockers.push(...taskBlockers);
|
|
1258
|
+
const writeScope = task ? splitList(task.attrs.write_scope ?? "") : [];
|
|
1259
|
+
blockers.push(...write_scope_blockers(taskId, writeScope));
|
|
1260
|
+
const chainId = require_active_apply_worker_chain(ctx, taskId, blockers);
|
|
1261
|
+
const activeChain = active_apply_worker_chain(ctx, taskId).active;
|
|
1262
|
+
if (chainId) {
|
|
1263
|
+
const executorRefs = validate_worker_report_refs(ctx, args.executor_report_refs ?? [], {
|
|
1264
|
+
role: "executor",
|
|
1265
|
+
taskId,
|
|
1266
|
+
chainId,
|
|
1267
|
+
missingCode: "missing_executor_report_ref",
|
|
1268
|
+
missingMessage: "apply-code-review-packet requires --executor-report-ref",
|
|
1269
|
+
});
|
|
1270
|
+
blockers.push(...executorRefs.blockers);
|
|
1271
|
+
if (executorRefs.refs[0]) {
|
|
1272
|
+
blockers.push(...worker_report_origin_blockers(executorRefs.refs[0], activeChain?.executor_packet_fingerprint, "executor_report_ref"));
|
|
1273
|
+
if (activeChain) {
|
|
1274
|
+
blockers.push(...worker_report_input_digest_blockers(executorRefs.refs[0], apply_worker_executor_input_ref_digest(ctx.evidences, activeChain), "executor_report_ref"));
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
const packet = apply_packet_common(ctx, "apply_code_review", taskId, "executor_worker", blockers);
|
|
1278
|
+
packet.apply_worker_chain_id = chainId;
|
|
1279
|
+
packet.declared_task_write_scope = writeScope;
|
|
1280
|
+
packet.executor_report_pinned_refs = executorRefs.refs;
|
|
1281
|
+
packet.executor_report_refs = args.executor_report_refs ?? [];
|
|
1282
|
+
packet.expected_executor_origin_packet_fingerprint = activeChain?.executor_packet_fingerprint;
|
|
1283
|
+
if (activeChain)
|
|
1284
|
+
packet.expected_executor_input_ref_digest = apply_worker_executor_input_ref_digest(ctx.evidences, activeChain);
|
|
1285
|
+
packet.executor_report_required_fields = [...APPLY_EXECUTOR_REPORT_REQUIRED_FIELDS];
|
|
1286
|
+
packet.code_review_report_required_fields = [...APPLY_CODE_REVIEW_REPORT_REQUIRED_FIELDS];
|
|
1287
|
+
packet.task_edit = decision_status(taskEdit);
|
|
1288
|
+
Object.assign(packet, apply_task_context_fields(ctx, taskId, task, writeScope, executorRefs.refs));
|
|
1289
|
+
packet.code_review_checks = [
|
|
1290
|
+
"executor_report_matches_current_diff",
|
|
1291
|
+
"changed_files_within_declared_task_write_scope",
|
|
1292
|
+
"protected_paths_unchanged",
|
|
1293
|
+
"test_and_invariant_mapping_supported",
|
|
1294
|
+
"suggest_green_test_ids",
|
|
1295
|
+
];
|
|
1296
|
+
packet.worker_state = blockers.length === 0 ? "ready" : "blocked";
|
|
1297
|
+
if (blockers.length > 0) {
|
|
1298
|
+
packet.blockers = unique_strings(blockers.map((item) => item.code));
|
|
1299
|
+
packet.block_reasons = blockers;
|
|
1300
|
+
}
|
|
1301
|
+
return finalize_apply_worker_packet(packet);
|
|
1302
|
+
}
|
|
1303
|
+
const packet = apply_packet_common(ctx, "apply_code_review", taskId, "executor_worker", blockers);
|
|
1304
|
+
packet.declared_task_write_scope = writeScope;
|
|
1305
|
+
packet.executor_report_refs = args.executor_report_refs ?? [];
|
|
1306
|
+
packet.executor_report_required_fields = [...APPLY_EXECUTOR_REPORT_REQUIRED_FIELDS];
|
|
1307
|
+
packet.code_review_report_required_fields = [...APPLY_CODE_REVIEW_REPORT_REQUIRED_FIELDS];
|
|
1308
|
+
packet.task_edit = decision_status(taskEdit);
|
|
1309
|
+
Object.assign(packet, apply_task_context_fields(ctx, taskId, task, writeScope));
|
|
1310
|
+
return finalize_apply_worker_packet(packet);
|
|
1311
|
+
}
|
|
1312
|
+
function apply_verify_packet(ctx, args) {
|
|
1313
|
+
const taskId = args.task_id ?? "";
|
|
1314
|
+
const blockers = [];
|
|
1315
|
+
const applyReady = evaluate_workflow_decision(ctx, "apply_ready");
|
|
1316
|
+
if (!applyReady.allowed)
|
|
1317
|
+
blockers.push(...decision_reasons(applyReady));
|
|
1318
|
+
const taskEdit = evaluate_workflow_decision(ctx, "task_edit", taskId);
|
|
1319
|
+
if (!taskEdit.allowed)
|
|
1320
|
+
blockers.push(...decision_reasons(taskEdit));
|
|
1321
|
+
const { task, blockers: taskBlockers } = task_lookup_blockers(ctx, taskId);
|
|
1322
|
+
blockers.push(...taskBlockers);
|
|
1323
|
+
const writeScope = task ? splitList(task.attrs.write_scope ?? "") : [];
|
|
1324
|
+
blockers.push(...write_scope_blockers(taskId, writeScope));
|
|
1325
|
+
const chainId = require_active_apply_worker_chain(ctx, taskId, blockers);
|
|
1326
|
+
const activeChain = active_apply_worker_chain(ctx, taskId).active;
|
|
1327
|
+
const packet = apply_packet_common(ctx, "apply_verify", taskId, "executor_worker", blockers);
|
|
1328
|
+
if (chainId) {
|
|
1329
|
+
const executorRefs = validate_worker_report_refs(ctx, args.executor_report_refs ?? [], {
|
|
1330
|
+
role: "executor",
|
|
1331
|
+
taskId,
|
|
1332
|
+
chainId,
|
|
1333
|
+
missingCode: "missing_executor_report_ref",
|
|
1334
|
+
missingMessage: "apply-verify-packet requires --executor-report-ref",
|
|
1335
|
+
});
|
|
1336
|
+
const codeReviewRefs = validate_worker_report_refs(ctx, args.task_code_review_report_refs ?? [], {
|
|
1337
|
+
role: "code-reviewer",
|
|
1338
|
+
taskId,
|
|
1339
|
+
chainId,
|
|
1340
|
+
missingCode: "missing_task_code_review_report_ref",
|
|
1341
|
+
missingMessage: "apply-verify-packet requires --task-code-review-report-ref",
|
|
1342
|
+
});
|
|
1343
|
+
const greenRefs = validate_test_run_evidence_refs(ctx, args.green_test_run_evidence_refs ?? [], {
|
|
1344
|
+
taskId,
|
|
1345
|
+
gate: "task_complete",
|
|
1346
|
+
phase: "green",
|
|
1347
|
+
semanticStatus: "expected_success",
|
|
1348
|
+
chainId,
|
|
1349
|
+
missingCode: "missing_green_test_run_evidence_ref",
|
|
1350
|
+
missingMessage: "apply-verify-packet requires --green-test-run-evidence-ref",
|
|
1351
|
+
});
|
|
1352
|
+
const redRefs = validate_test_run_evidence_refs(ctx, args.red_test_run_evidence_refs ?? [], {
|
|
1353
|
+
taskId,
|
|
1354
|
+
gate: "task_edit",
|
|
1355
|
+
phase: "red",
|
|
1356
|
+
semanticStatus: "expected_failure",
|
|
1357
|
+
chainId: null,
|
|
1358
|
+
missingCode: "missing_red_test_run_evidence_ref",
|
|
1359
|
+
missingMessage: "apply-verify-packet requires --red-test-run-evidence-ref",
|
|
1360
|
+
});
|
|
1361
|
+
const characterizationRefs = validate_test_run_evidence_refs(ctx, args.characterization_test_run_evidence_refs ?? [], {
|
|
1362
|
+
taskId,
|
|
1363
|
+
gate: "task_edit",
|
|
1364
|
+
phase: "characterization",
|
|
1365
|
+
semanticStatus: "expected_success",
|
|
1366
|
+
chainId: null,
|
|
1367
|
+
missingCode: "missing_characterization_test_run_evidence_ref",
|
|
1368
|
+
missingMessage: "apply-verify-packet requires --characterization-test-run-evidence-ref",
|
|
1369
|
+
});
|
|
1370
|
+
blockers.push(...executorRefs.blockers, ...codeReviewRefs.blockers, ...greenRefs.blockers);
|
|
1371
|
+
if (executorRefs.refs[0]) {
|
|
1372
|
+
blockers.push(...worker_report_origin_blockers(executorRefs.refs[0], activeChain?.executor_packet_fingerprint, "executor_report_ref"));
|
|
1373
|
+
if (activeChain) {
|
|
1374
|
+
blockers.push(...worker_report_input_digest_blockers(executorRefs.refs[0], apply_worker_executor_input_ref_digest(ctx.evidences, activeChain), "executor_report_ref"));
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
if (executorRefs.refs[0] && codeReviewRefs.refs[0]) {
|
|
1378
|
+
blockers.push(...worker_report_input_blockers(codeReviewRefs.refs[0], [executorRefs.refs[0]], "task_code_review_report_ref"));
|
|
1379
|
+
}
|
|
1380
|
+
if ((args.red_test_run_evidence_refs ?? []).length === 0 && (args.characterization_test_run_evidence_refs ?? []).length === 0) {
|
|
1381
|
+
blockers.push(reason("missing_pre_edit_test_run_evidence_ref", "apply-verify-packet requires --red-test-run-evidence-ref or --characterization-test-run-evidence-ref"));
|
|
1382
|
+
}
|
|
1383
|
+
else {
|
|
1384
|
+
if ((args.red_test_run_evidence_refs ?? []).length > 0)
|
|
1385
|
+
blockers.push(...redRefs.blockers, ...pre_edit_refs_bound_to_active_chain(ctx, activeChain, redRefs.refs, "red", taskId));
|
|
1386
|
+
if ((args.characterization_test_run_evidence_refs ?? []).length > 0)
|
|
1387
|
+
blockers.push(...characterizationRefs.blockers, ...pre_edit_refs_bound_to_active_chain(ctx, activeChain, characterizationRefs.refs, "characterization", taskId));
|
|
1388
|
+
}
|
|
1389
|
+
packet.apply_worker_chain_id = chainId;
|
|
1390
|
+
packet.executor_report_pinned_refs = executorRefs.refs;
|
|
1391
|
+
packet.task_code_review_report_pinned_refs = codeReviewRefs.refs;
|
|
1392
|
+
packet.green_test_run_evidence_pinned_refs = greenRefs.refs;
|
|
1393
|
+
packet.red_test_run_evidence_pinned_refs = redRefs.refs;
|
|
1394
|
+
packet.characterization_test_run_evidence_pinned_refs = characterizationRefs.refs;
|
|
1395
|
+
if (executorRefs.refs[0])
|
|
1396
|
+
packet.expected_executor_origin_packet_fingerprint = activeChain?.executor_packet_fingerprint;
|
|
1397
|
+
if (executorRefs.refs[0] && activeChain)
|
|
1398
|
+
packet.expected_executor_input_ref_digest = apply_worker_executor_input_ref_digest(ctx.evidences, activeChain);
|
|
1399
|
+
if (executorRefs.refs[0] && codeReviewRefs.refs[0]) {
|
|
1400
|
+
packet.expected_code_review_input_ref_digest = worker_input_ref_digest([executorRefs.refs[0]]);
|
|
1401
|
+
}
|
|
1402
|
+
if (executorRefs.refs[0] && codeReviewRefs.refs[0] && greenRefs.refs[0]) {
|
|
1403
|
+
const canonicalGreenRefs = [...greenRefs.refs].sort((a, b) => String(a.evidence_id ?? "").localeCompare(String(b.evidence_id ?? "")));
|
|
1404
|
+
const expectedFreshness = compute_apply_worker_freshness(ctx.repoRoot, ctx.changeRoot, ctx.evidences, taskId, chainId, {
|
|
1405
|
+
executor_report_ref: executorRefs.refs[0],
|
|
1406
|
+
task_code_review_report_ref: codeReviewRefs.refs[0],
|
|
1407
|
+
green_test_run_evidence_ref: String(canonicalGreenRefs[0].evidence_id ?? ""),
|
|
1408
|
+
green_test_run_evidence_refs: canonicalGreenRefs.map((ev) => String(ev.evidence_id ?? "")).filter(Boolean),
|
|
1409
|
+
});
|
|
1410
|
+
packet.expected_freshness_fingerprint = expectedFreshness;
|
|
1411
|
+
const preEditRefs = [
|
|
1412
|
+
...(Array.isArray(packet.red_test_run_evidence_pinned_refs) ? packet.red_test_run_evidence_pinned_refs : []),
|
|
1413
|
+
...(Array.isArray(packet.characterization_test_run_evidence_pinned_refs) ? packet.characterization_test_run_evidence_pinned_refs : []),
|
|
1414
|
+
];
|
|
1415
|
+
packet.expected_verifier_input_ref_digest = worker_input_ref_digest([
|
|
1416
|
+
executorRefs.refs[0],
|
|
1417
|
+
codeReviewRefs.refs[0],
|
|
1418
|
+
...canonicalGreenRefs.map((ev) => ({ kind: "test_run", evidence_id: String(ev.evidence_id ?? ""), phase: ev.phase, semantic_status: ev.semantic_status })),
|
|
1419
|
+
...preEditRefs.map((ev) => ({ kind: "test_run", evidence_id: String(ev.evidence_id ?? ""), phase: ev.phase, semantic_status: ev.semantic_status })),
|
|
1420
|
+
]);
|
|
1421
|
+
const missingGreenFingerprint = greenRefs.refs.filter((ev) => !fingerprint_digest(ev.implementation_fingerprint));
|
|
1422
|
+
if (missingGreenFingerprint.length > 0) {
|
|
1423
|
+
blockers.push(reason("green_implementation_fingerprint_missing", "apply-verify-packet requires GREEN evidence to record implementation_fingerprint"));
|
|
1424
|
+
}
|
|
1425
|
+
else if (!greenRefs.refs.every((ev) => fingerprint_matches(ev.implementation_fingerprint, expectedFreshness.implementation_fingerprint))) {
|
|
1426
|
+
blockers.push(reason("green_implementation_fingerprint_mismatch", "apply-verify-packet requires current implementation fingerprint to match GREEN evidence implementation_fingerprint"));
|
|
1427
|
+
}
|
|
1428
|
+
if (!fingerprint_matches(codeReviewRefs.refs[0].observed_implementation_fingerprint, expectedFreshness.implementation_fingerprint)) {
|
|
1429
|
+
blockers.push(reason("task_code_review_implementation_fingerprint_mismatch", "apply-verify-packet requires code-reviewer observed_implementation_fingerprint to match current implementation fingerprint"));
|
|
1430
|
+
}
|
|
1431
|
+
if (Array.isArray(expectedFreshness.protected_dirty_paths) && expectedFreshness.protected_dirty_paths.length > 0) {
|
|
1432
|
+
blockers.push(reason("protected_path_dirty", `apply-verify-packet requires protected change artifacts to stay unchanged during executor-worker verification: ${renderList(expectedFreshness.protected_dirty_paths.map(String))}`, expectedFreshness.protected_dirty_paths.map(String)));
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
packet.declared_task_write_scope = writeScope;
|
|
1437
|
+
packet.task_edit = decision_status(taskEdit);
|
|
1438
|
+
packet.task_complete = safe_decision_status(ctx, "task_complete", taskId);
|
|
1439
|
+
Object.assign(packet, apply_task_context_fields(ctx, taskId, task, writeScope, [
|
|
1440
|
+
...(Array.isArray(packet.executor_report_pinned_refs) ? packet.executor_report_pinned_refs : []),
|
|
1441
|
+
...(Array.isArray(packet.task_code_review_report_pinned_refs) ? packet.task_code_review_report_pinned_refs : []),
|
|
1442
|
+
]));
|
|
1443
|
+
packet.current_worktree_refs = dirty_repo_refs(ctx, "apply_verify current worktree refs");
|
|
1444
|
+
packet.protected_path_refs = Array.isArray(packet.expected_freshness_fingerprint?.protected_path_refs)
|
|
1445
|
+
? packet.expected_freshness_fingerprint.protected_path_refs
|
|
1446
|
+
: apply_worker_protected_path_refs(ctx.repoRoot, ctx.changeRoot);
|
|
1447
|
+
packet.scope_diff_review_policy = {
|
|
1448
|
+
declared_task_write_scope: writeScope,
|
|
1449
|
+
forbidden_paths: ["proposal.md", "design.md", "tasks.md", "specs/", ".superspec/"],
|
|
1450
|
+
require_scope_verdict: true,
|
|
1451
|
+
};
|
|
1452
|
+
packet.verification_checks = [
|
|
1453
|
+
"red_or_characterization_pre_edit_evidence_bound_to_active_chain",
|
|
1454
|
+
"executor_report_same_chain_and_fresh",
|
|
1455
|
+
"task_code_review_report_same_chain_and_fresh",
|
|
1456
|
+
"green_test_run_same_chain_and_pinned_transcripts",
|
|
1457
|
+
"current_freshness_matches_expected_freshness_fingerprint",
|
|
1458
|
+
"protected_change_artifacts_clean",
|
|
1459
|
+
];
|
|
1460
|
+
packet.executor_report_required_fields = [...APPLY_EXECUTOR_REPORT_REQUIRED_FIELDS];
|
|
1461
|
+
packet.code_review_report_required_fields = [...APPLY_CODE_REVIEW_REPORT_REQUIRED_FIELDS];
|
|
1462
|
+
packet.verifier_report_required_fields = [...APPLY_VERIFIER_REPORT_REQUIRED_FIELDS];
|
|
1463
|
+
packet.test_evidence_required_fields = ["command", "cwd", "exit_code", "repo_head", "implementation_fingerprint", "guard_artifact_manifest_fingerprint", "raw_log_pinned_refs"];
|
|
1464
|
+
packet.executor_report_refs = args.executor_report_refs ?? [];
|
|
1465
|
+
packet.task_code_review_report_refs = args.task_code_review_report_refs ?? [];
|
|
1466
|
+
packet.green_test_run_evidence_refs = args.green_test_run_evidence_refs ?? [];
|
|
1467
|
+
packet.red_test_run_evidence_refs = args.red_test_run_evidence_refs ?? [];
|
|
1468
|
+
packet.characterization_test_run_evidence_refs = args.characterization_test_run_evidence_refs ?? [];
|
|
1469
|
+
packet.worker_state = blockers.length === 0 ? "ready" : "blocked";
|
|
1470
|
+
if (blockers.length > 0) {
|
|
1471
|
+
packet.blockers = unique_strings(blockers.map((item) => item.code));
|
|
1472
|
+
packet.block_reasons = blockers;
|
|
1473
|
+
}
|
|
1474
|
+
return finalize_apply_worker_packet(packet);
|
|
1475
|
+
}
|
|
1476
|
+
function render_apply_worker_prompt(packet) {
|
|
1477
|
+
const blockedHeaders = {
|
|
1478
|
+
apply_test: "DO NOT SPAWN TEST-RUNNER",
|
|
1479
|
+
apply_executor: "DO NOT SPAWN IMPLEMENTATION EXECUTOR",
|
|
1480
|
+
apply_code_review: "DO NOT SPAWN IMPLEMENTATION CODE-REVIEWER",
|
|
1481
|
+
apply_verify: "DO NOT SPAWN IMPLEMENTATION VERIFIER",
|
|
1482
|
+
};
|
|
1483
|
+
const lines = [];
|
|
1484
|
+
if (packet.worker_state === "blocked") {
|
|
1485
|
+
lines.push(blockedHeaders[String(packet.packet_kind)] ?? "DO NOT SPAWN WORKER", "");
|
|
1486
|
+
}
|
|
1487
|
+
else {
|
|
1488
|
+
lines.push("# SuperSpec Apply Worker Packet", "");
|
|
1489
|
+
}
|
|
1490
|
+
lines.push(`packet_kind: ${String(packet.packet_kind)}`, `change: ${String(packet.change)}`, `task_id: ${String(packet.task_id)}`, `worker_state: ${String(packet.worker_state)}`, `worker_chain_context: ${String(packet.worker_chain_context)}`);
|
|
1491
|
+
if (typeof packet.test_id === "string")
|
|
1492
|
+
lines.push(`test_id: ${packet.test_id}`);
|
|
1493
|
+
if (typeof packet.phase === "string")
|
|
1494
|
+
lines.push(`phase: ${packet.phase}`);
|
|
1495
|
+
if (typeof packet.allowed_test_command === "string")
|
|
1496
|
+
lines.push(`allowed_test_command: ${packet.allowed_test_command}`);
|
|
1497
|
+
if (typeof packet.apply_worker_chain_id === "string")
|
|
1498
|
+
lines.push(`apply_worker_chain_id: ${packet.apply_worker_chain_id}`);
|
|
1499
|
+
if (packet.packet_kind === "apply_executor" && packet.worker_state === "ready") {
|
|
1500
|
+
lines.push("", "CHAIN ACTIVATION VERIFIED FOR IMPLEMENTATION EXECUTOR", "The active apply_worker_chain evidence ref has been validated for this prompt.", "", "chain_activation_template:", JSON.stringify(packet.chain_activation_template ?? {}, null, 2));
|
|
1501
|
+
}
|
|
1502
|
+
if (packet.worker_state === "ready") {
|
|
1503
|
+
lines.push("", "Packet JSON:", JSON.stringify(packet, null, 2));
|
|
1504
|
+
}
|
|
1505
|
+
if (Array.isArray(packet.blockers) && packet.blockers.length > 0) {
|
|
1506
|
+
lines.push("", "Blockers:", ...packet.blockers.map((item) => `- ${item}`));
|
|
1507
|
+
}
|
|
1508
|
+
if (Array.isArray(packet.block_reasons) && packet.block_reasons.length > 0) {
|
|
1509
|
+
lines.push("", "Blocker details:", ...packet.block_reasons.map((item) => `- ${item.code}: ${item.message}`));
|
|
1510
|
+
}
|
|
1511
|
+
if (Array.isArray(packet.stop_conditions) && packet.stop_conditions.length > 0) {
|
|
1512
|
+
lines.push("", "Stop conditions:", ...packet.stop_conditions.map((item) => `- ${item}`));
|
|
1513
|
+
}
|
|
1514
|
+
return `${lines.join("\n")}\n`;
|
|
1515
|
+
}
|
|
1516
|
+
function render_ref(ref) {
|
|
1517
|
+
return `- ${ref.root}:${ref.path} @ ${ref.blob_sha}`;
|
|
1518
|
+
}
|
|
1519
|
+
function render_review_prompt(ctx, packet) {
|
|
1520
|
+
const lines = [
|
|
1521
|
+
`# SuperSpec Review Packet`,
|
|
1522
|
+
"",
|
|
1523
|
+
`consumer: ${packet.consumer}`,
|
|
1524
|
+
`gate: ${packet.gate}`,
|
|
1525
|
+
`role: ${packet.role}`,
|
|
1526
|
+
`round: ${packet.round}`,
|
|
1527
|
+
"",
|
|
1528
|
+
`Target refs:`,
|
|
1529
|
+
...(packet.target_refs.length > 0 ? packet.target_refs.map(render_ref) : ["- none"]),
|
|
1530
|
+
"",
|
|
1531
|
+
`Source refs:`,
|
|
1532
|
+
...(packet.source_refs.length > 0 ? packet.source_refs.map(render_ref) : ["- none"]),
|
|
1533
|
+
];
|
|
1534
|
+
if (packet.required_load_refs && packet.required_load_refs.length > 0) {
|
|
1535
|
+
lines.push("", "Required load refs:", ...packet.required_load_refs.map(render_ref));
|
|
1536
|
+
}
|
|
1537
|
+
if (packet.required_claim_ids && packet.required_claim_ids.length > 0) {
|
|
1538
|
+
lines.push("", "Required claim ids:", ...packet.required_claim_ids.map((item) => `- ${item}`));
|
|
1539
|
+
}
|
|
1540
|
+
if (packet.must_read_verbatim_findings && packet.must_read_verbatim_findings.length > 0) {
|
|
1541
|
+
lines.push("", "Must read verbatim findings:", ...packet.must_read_verbatim_findings.map((item) => `- ${item.evidence_id} :: ${item.finding_uid} @ ${item.evidence_ref.path}`));
|
|
1542
|
+
}
|
|
1543
|
+
if (packet.must_read_verbatim_decisions && packet.must_read_verbatim_decisions.length > 0) {
|
|
1544
|
+
lines.push("", "Must read decision bindings:", ...packet.must_read_verbatim_decisions.map((item) => `- ${item.evidence_id} :: ${item.decision_scope_key} @ ${item.evidence_ref.path}`));
|
|
1545
|
+
}
|
|
1546
|
+
lines.push("", `Required output kind: ${packet.required_output_kind}`, "Output contract fields:", ...packet.output_contract_fields.map((item) => `- ${item}`));
|
|
1547
|
+
if (packet.required_review_scope && packet.required_review_scope.length > 0) {
|
|
1548
|
+
lines.push("", "Required review scope:", ...packet.required_review_scope.map((item) => `- ${item}`));
|
|
1549
|
+
}
|
|
1550
|
+
lines.push("", "Stop conditions:", ...packet.stop_conditions.map((item) => `- ${item}`));
|
|
1551
|
+
if (packet.round > 1 && packet.gate in REVIEW_TARGETS_BY_GATE) {
|
|
1552
|
+
lines.push("", render_finding_ledger(packet.gate, build_finding_ledger(packet.gate, ctx.evidences, packet.round)));
|
|
1553
|
+
}
|
|
1554
|
+
return `${lines.join("\n")}\n`;
|
|
1555
|
+
}
|
|
1556
|
+
function render_ledger(ctx, gateRaw, round) {
|
|
1557
|
+
const gate = normalize_gate(gateRaw);
|
|
1558
|
+
const entries = build_finding_ledger(gate, ctx.evidences, round ?? Number.POSITIVE_INFINITY);
|
|
1559
|
+
return `${render_finding_ledger(gate, entries)}\n`;
|
|
1560
|
+
}
|
|
1561
|
+
export function dispatch_packet(args) {
|
|
1562
|
+
const ctx = load_packet_context(args.change);
|
|
1563
|
+
if (args.command === "workflow-packet") {
|
|
1564
|
+
const gate = normalize_gate(args.gate ?? "");
|
|
1565
|
+
if ((gate === "task_edit" || gate === "task_complete" || gate === "task_reopen") && !args.task_id) {
|
|
1566
|
+
throw new GuardError(`workflow-packet requires --task-id for gate ${gate}`);
|
|
1567
|
+
}
|
|
1568
|
+
return { output_format: "agent", payload: workflow_packet(ctx, gate, args.task_id) };
|
|
1569
|
+
}
|
|
1570
|
+
if (args.command === "review-packet") {
|
|
1571
|
+
assert_packet_context_clean(ctx);
|
|
1572
|
+
const round = args.round ?? 0;
|
|
1573
|
+
if (round < 1)
|
|
1574
|
+
throw new GuardError("review-packet requires --round >= 1");
|
|
1575
|
+
const packet = review_packet(ctx, args.gate ?? "", args.role ?? "", round, args.evidence_kind);
|
|
1576
|
+
if (args.packet_format === "prompt") {
|
|
1577
|
+
return { output_format: "prompt", payload: render_review_prompt(ctx, packet) };
|
|
1578
|
+
}
|
|
1579
|
+
return { output_format: "agent", payload: packet };
|
|
1580
|
+
}
|
|
1581
|
+
if (args.command === "apply-test-packet") {
|
|
1582
|
+
assert_packet_context_clean(ctx);
|
|
1583
|
+
const packet = apply_test_packet(ctx, args);
|
|
1584
|
+
if (args.packet_format === "prompt")
|
|
1585
|
+
return { output_format: "prompt", payload: render_apply_worker_prompt(packet) };
|
|
1586
|
+
return { output_format: "agent", payload: packet };
|
|
1587
|
+
}
|
|
1588
|
+
if (args.command === "apply-executor-packet") {
|
|
1589
|
+
assert_packet_context_clean(ctx);
|
|
1590
|
+
const packet = apply_executor_packet(ctx, args);
|
|
1591
|
+
if (args.packet_format === "prompt")
|
|
1592
|
+
return { output_format: "prompt", payload: render_apply_worker_prompt(packet) };
|
|
1593
|
+
return { output_format: "agent", payload: packet };
|
|
1594
|
+
}
|
|
1595
|
+
if (args.command === "apply-code-review-packet") {
|
|
1596
|
+
assert_packet_context_clean(ctx);
|
|
1597
|
+
const packet = apply_code_review_packet(ctx, args);
|
|
1598
|
+
if (args.packet_format === "prompt")
|
|
1599
|
+
return { output_format: "prompt", payload: render_apply_worker_prompt(packet) };
|
|
1600
|
+
return { output_format: "agent", payload: packet };
|
|
1601
|
+
}
|
|
1602
|
+
if (args.command === "apply-verify-packet") {
|
|
1603
|
+
assert_packet_context_clean(ctx);
|
|
1604
|
+
const packet = apply_verify_packet(ctx, args);
|
|
1605
|
+
if (args.packet_format === "prompt")
|
|
1606
|
+
return { output_format: "prompt", payload: render_apply_worker_prompt(packet) };
|
|
1607
|
+
return { output_format: "agent", payload: packet };
|
|
1608
|
+
}
|
|
1609
|
+
if (args.command === "ledger-render") {
|
|
1610
|
+
assert_packet_context_clean(ctx);
|
|
1611
|
+
return { output_format: "prompt", payload: render_ledger(ctx, args.gate ?? "", args.round) };
|
|
1612
|
+
}
|
|
1613
|
+
throw new GuardError(`unknown packet command: ${args.command}`);
|
|
1614
|
+
}
|
|
1615
|
+
export function is_packet_command(command) {
|
|
1616
|
+
return command === "workflow-packet"
|
|
1617
|
+
|| command === "review-packet"
|
|
1618
|
+
|| command === "apply-test-packet"
|
|
1619
|
+
|| command === "apply-executor-packet"
|
|
1620
|
+
|| command === "apply-code-review-packet"
|
|
1621
|
+
|| command === "apply-verify-packet"
|
|
1622
|
+
|| command === "ledger-render";
|
|
1623
|
+
}
|