@peterxiaoyang/superspec 0.1.10 → 0.1.12
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/README.md +8 -10
- package/dist/src/apply_worker_chain.d.ts +6 -3
- package/dist/src/apply_worker_chain.js +125 -30
- package/dist/src/apply_worker_chain_lifecycle.d.ts +7 -5
- package/dist/src/apply_worker_chain_lifecycle.js +153 -22
- package/dist/src/cli_args.d.ts +1 -0
- package/dist/src/cli_args.js +33 -15
- package/dist/src/disclosure.js +2 -2
- package/dist/src/evidence.js +104 -8
- package/dist/src/gates.d.ts +1 -0
- package/dist/src/gates.js +160 -27
- package/dist/src/hooks/adapter.js +43 -7
- package/dist/src/hooks/guard_api.js +2 -2
- package/dist/src/hooks/health.js +0 -12
- package/dist/src/hooks/policy_event.js +11 -1
- package/dist/src/i18n.js +63 -3
- package/dist/src/init_cli.js +1 -1
- package/dist/src/install_engine.js +64 -0
- package/dist/src/openspec.d.ts +2 -0
- package/dist/src/openspec.js +31 -0
- package/dist/src/packet_render.d.ts +1 -0
- package/dist/src/packet_render.js +248 -31
- package/dist/src/packet_schema.d.ts +2 -0
- package/dist/src/tasks.d.ts +5 -0
- package/dist/src/tasks.js +62 -0
- package/dist/src/util.d.ts +4 -0
- package/dist/src/util.js +13 -0
- package/dist/superspec.js +4 -4
- package/package.json +3 -3
- package/templates/hooks/codex-hooks.json +0 -26
- package/templates/sidecar/discovery.md +9 -0
- package/templates/sidecar/test-contract.md +2 -1
- package/templates/workflow/prompts/code-reviewer.md +1 -1
- package/templates/workflow/prompts/executor.md +1 -1
- package/templates/workflow/prompts/test-engineer.md +1 -0
- package/templates/workflow/prompts/test-runner.md +3 -1
- package/templates/workflow/prompts/verifier.md +9 -9
- package/templates/workflow/skills/superspec-apply/SKILL.md +58 -40
- package/templates/workflow/skills/superspec-archive/SKILL.md +5 -13
- package/templates/workflow/skills/superspec-explore/SKILL.md +17 -18
- package/templates/workflow/skills/superspec-propose/SKILL.md +26 -22
- package/templates/workflow/skills/superspec-review/SKILL.md +17 -24
- package/dist/src/packet_measure.d.ts +0 -43
- package/dist/src/packet_measure.js +0 -382
- /package/bin/{superspec-guard.js → superspec-check.js} +0 -0
|
@@ -29,6 +29,13 @@ const CODEX_CONFIG_ENTRIES = [
|
|
|
29
29
|
{ table: "agents", key: "max_threads", value: String(CODEX_NATIVE_AGENT_MAX_THREADS) },
|
|
30
30
|
{ table: "agents", key: "max_depth", value: String(CODEX_NATIVE_AGENT_MAX_DEPTH) },
|
|
31
31
|
];
|
|
32
|
+
const HOOK_COMMAND = 'superspec-hook --change "$SUPERSPEC_CHANGE"';
|
|
33
|
+
const LEGACY_FOUR_HOOK_MATRIX = [
|
|
34
|
+
{ eventName: "PreToolUse", matcher: "Bash|apply_patch|Edit|Write|mcp__.*", statusMessage: "SuperSpec 写入策略检查" },
|
|
35
|
+
{ eventName: "PostToolUse", matcher: "Bash", statusMessage: "SuperSpec 运行证据记录" },
|
|
36
|
+
{ eventName: "SubagentStart", matcher: ".*", statusMessage: "SuperSpec 子智能体启动记录" },
|
|
37
|
+
{ eventName: "SubagentStop", matcher: ".*", statusMessage: "SuperSpec 子智能体停止记录" },
|
|
38
|
+
];
|
|
32
39
|
function package_version(packageRoot) {
|
|
33
40
|
try {
|
|
34
41
|
const pkg = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
|
|
@@ -38,6 +45,51 @@ function package_version(packageRoot) {
|
|
|
38
45
|
return "0.0.0";
|
|
39
46
|
}
|
|
40
47
|
}
|
|
48
|
+
function has_exact_keys(value, keys) {
|
|
49
|
+
const actual = Object.keys(value).sort();
|
|
50
|
+
const expected = [...keys].sort();
|
|
51
|
+
return actual.length === expected.length && actual.every((key, idx) => key === expected[idx]);
|
|
52
|
+
}
|
|
53
|
+
function legacy_hook_entry_matches(entry, expected) {
|
|
54
|
+
if (!isObject(entry) || !has_exact_keys(entry, ["matcher", "hooks"]))
|
|
55
|
+
return false;
|
|
56
|
+
if (entry.matcher !== expected.matcher || !Array.isArray(entry.hooks) || entry.hooks.length !== 1)
|
|
57
|
+
return false;
|
|
58
|
+
const hook = entry.hooks[0];
|
|
59
|
+
return isObject(hook)
|
|
60
|
+
&& has_exact_keys(hook, ["type", "command", "timeout", "statusMessage"])
|
|
61
|
+
&& hook.type === "command"
|
|
62
|
+
&& hook.command === HOOK_COMMAND
|
|
63
|
+
&& hook.timeout === 120
|
|
64
|
+
&& hook.statusMessage === expected.statusMessage;
|
|
65
|
+
}
|
|
66
|
+
function is_legacy_managed_four_hook_manifest(parsed) {
|
|
67
|
+
if (!isObject(parsed) || !has_exact_keys(parsed, ["superspec", "hooks"]))
|
|
68
|
+
return false;
|
|
69
|
+
if (!isObject(parsed.superspec) || !has_exact_keys(parsed.superspec, ["managed", "adapter_version", "strict_profile_default"]))
|
|
70
|
+
return false;
|
|
71
|
+
if (parsed.superspec.managed !== true
|
|
72
|
+
|| parsed.superspec.adapter_version !== "superspec-hook@2"
|
|
73
|
+
|| parsed.superspec.strict_profile_default !== "audit-only-until-r1-provenance-passes") {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if (!isObject(parsed.hooks) || !has_exact_keys(parsed.hooks, LEGACY_FOUR_HOOK_MATRIX.map((entry) => entry.eventName)))
|
|
77
|
+
return false;
|
|
78
|
+
return LEGACY_FOUR_HOOK_MATRIX.every((expected) => {
|
|
79
|
+
const entries = parsed.hooks[expected.eventName];
|
|
80
|
+
return Array.isArray(entries)
|
|
81
|
+
&& entries.length === 1
|
|
82
|
+
&& legacy_hook_entry_matches(entries[0], expected);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function target_is_legacy_managed_four_hook_manifest(targetAbs) {
|
|
86
|
+
try {
|
|
87
|
+
return is_legacy_managed_four_hook_manifest(JSON.parse(readFileSync(targetAbs, "utf8")));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
41
93
|
export function load_install_map(packageRoot = PACKAGE_ROOT) {
|
|
42
94
|
const mapPath = join(packageRoot, INSTALL_MAP_REL);
|
|
43
95
|
if (!existsSync(mapPath))
|
|
@@ -315,6 +367,13 @@ export function install_workflow(repoRoot, opts = {}) {
|
|
|
315
367
|
files.push({ path: mapping.target, sha256: sourceSha, managed: true, preexisting: false });
|
|
316
368
|
actions.push({ action: `install ${mapping.target}`, status: "ok" });
|
|
317
369
|
}
|
|
370
|
+
else if (mapping.kind === "hook" && target_is_legacy_managed_four_hook_manifest(targetAbs)) {
|
|
371
|
+
// Previous SuperSpec default hooks installed PreToolUse/PostToolUse. Re-init must migrate
|
|
372
|
+
// that unmodified managed baseline so old blocking hooks do not stay in the workflow.
|
|
373
|
+
copyFileSync(sourceAbs, targetAbs);
|
|
374
|
+
files.push({ path: mapping.target, sha256: sourceSha, managed: true, preexisting: false });
|
|
375
|
+
actions.push({ action: `install ${mapping.target}`, status: "updated", detail: "legacy managed four-hook manifest migrated to subagent-only manifest" });
|
|
376
|
+
}
|
|
318
377
|
else if (opts.force) {
|
|
319
378
|
copyFileSync(targetAbs, `${targetAbs}.bak`);
|
|
320
379
|
copyFileSync(sourceAbs, targetAbs);
|
|
@@ -323,6 +382,11 @@ export function install_workflow(repoRoot, opts = {}) {
|
|
|
323
382
|
files.push({ path: mapping.target, sha256: sourceSha, managed: true, preexisting: false });
|
|
324
383
|
actions.push({ action: `install ${mapping.target}`, status: "updated", detail: `existing file backed up to ${mapping.target}.bak` });
|
|
325
384
|
}
|
|
385
|
+
else if (mapping.kind === "hook") {
|
|
386
|
+
copyFileSync(sourceAbs, `${targetAbs}.new`);
|
|
387
|
+
files.push({ path: mapping.target, sha256: targetSha, managed: false, preexisting: true });
|
|
388
|
+
actions.push({ action: `install ${mapping.target}`, status: "skipped", detail: `pre-existing hooks manifest kept; current SuperSpec manifest written to ${mapping.target}.new` });
|
|
389
|
+
}
|
|
326
390
|
else {
|
|
327
391
|
// Pre-existing different file: never overwrite, never delete (DISTRIBUTION §5 red line).
|
|
328
392
|
files.push({ path: mapping.target, sha256: targetSha, managed: false, preexisting: true });
|
package/dist/src/openspec.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export declare function all_done(status: JsonMap): boolean;
|
|
|
27
27
|
export declare function openspec_floor_route(status: JsonMap): string;
|
|
28
28
|
export declare function normalize_route_phase(route: string): string;
|
|
29
29
|
export declare function normalize_gate(gate: string): string;
|
|
30
|
+
export declare function authorized_supersede_target_ids(evidences: JsonMap[]): Set<string>;
|
|
31
|
+
export declare function effective_superseded_ids(evidences: JsonMap[]): Set<string>;
|
|
30
32
|
export declare function gate_route_phase(gate: string): string;
|
|
31
33
|
export declare function effective_route_phase(status: JsonMap, requestedRoute: string, decision: JsonMap): string;
|
|
32
34
|
export declare function status_fingerprint(status: JsonMap): string;
|
package/dist/src/openspec.js
CHANGED
|
@@ -188,6 +188,37 @@ export function normalize_route_phase(route) {
|
|
|
188
188
|
export function normalize_gate(gate) {
|
|
189
189
|
return GATE_ALIASES[gate] ?? gate;
|
|
190
190
|
}
|
|
191
|
+
export function authorized_supersede_target_ids(evidences) {
|
|
192
|
+
const byId = new Map();
|
|
193
|
+
for (const ev of evidences) {
|
|
194
|
+
if (!isObject(ev) || ev._invalid)
|
|
195
|
+
continue;
|
|
196
|
+
const id = typeof ev.evidence_id === "string" ? ev.evidence_id : "";
|
|
197
|
+
if (id && !byId.has(id))
|
|
198
|
+
byId.set(id, ev);
|
|
199
|
+
}
|
|
200
|
+
const out = new Set();
|
|
201
|
+
for (const ev of evidences) {
|
|
202
|
+
if (!isObject(ev) || ev._invalid || ev.status !== "superseded")
|
|
203
|
+
continue;
|
|
204
|
+
const targetId = typeof ev.supersedes === "string" ? ev.supersedes : "";
|
|
205
|
+
if (!targetId)
|
|
206
|
+
continue;
|
|
207
|
+
const target = byId.get(targetId);
|
|
208
|
+
if (!target)
|
|
209
|
+
continue;
|
|
210
|
+
if (target.status !== "pass")
|
|
211
|
+
continue;
|
|
212
|
+
const sameGate = normalize_gate(String(ev.gate ?? "")) === normalize_gate(String(target.gate ?? ""));
|
|
213
|
+
const supersedeReason = typeof ev.supersede_reason === "string" ? ev.supersede_reason.trim() : "";
|
|
214
|
+
if (sameGate || supersedeReason)
|
|
215
|
+
out.add(targetId);
|
|
216
|
+
}
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
export function effective_superseded_ids(evidences) {
|
|
220
|
+
return authorized_supersede_target_ids(evidences);
|
|
221
|
+
}
|
|
191
222
|
export function gate_route_phase(gate) {
|
|
192
223
|
return GATE_ROUTE[normalize_gate(gate)] ?? "propose";
|
|
193
224
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ParsedArgs } from "./cli_args.ts";
|
|
2
2
|
import type { PacketDispatchResult } from "./packet_schema.ts";
|
|
3
|
+
export declare function read_discovery_template(packageRoot?: string): string | null;
|
|
3
4
|
export declare function dispatch_packet(args: ParsedArgs): PacketDispatchResult;
|
|
4
5
|
export declare function is_packet_command(command: string): boolean;
|
|
@@ -2,17 +2,43 @@ import { existsSync, readFileSync, statSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { load_config, sidecar_business_invariants_path, sidecar_discovery_path, sidecar_test_contract_path } from "./paths.js";
|
|
4
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";
|
|
5
|
+
import { CLAIM_ADJUDICATION_DECISIONS, FINDING_ADJUDICATION_DECISIONS, fingerprint_obj, GuardError, MAIN_ADJUDICATION_REQUIRED_FIELDS, MAIN_ADJUDICATION_DECISIONS, NO_TDD_REASONS, 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
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";
|
|
7
|
+
import { build_finding_ledger, enumerate_review_targets, FINDING_CATEGORIES, FINDING_TYPES, MATERIAL_CATEGORIES, render_finding_ledger, REVIEW_TARGETS_BY_GATE, review_round_number, } from "./disclosure.js";
|
|
8
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, } from "./gates.js";
|
|
9
9
|
import { final_verification_evidences, index_evidence, live_pass, live_user_confirmations } from "./evidence.js";
|
|
10
10
|
import { file_blob_sha, dirty_worktree_paths } from "./git.js";
|
|
11
11
|
import { preset_upgrade_reasons, preset_upgrade_required_from_context } from "./archive.js";
|
|
12
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";
|
|
13
|
+
import { parse_tasks, resolve_test_contract_command, splitList, task_apply_execution_surface, task_apply_execution_surface_reasons, 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, pre_edit_worker_test_run_reasons, read_pinned_artifact_json, worker_input_ref_digest, worker_test_run_reasons, } from "./apply_worker_chain.js";
|
|
15
15
|
import { apply_worker_chain_lifecycle_state } from "./apply_worker_chain_lifecycle.js";
|
|
16
|
+
import { PACKAGE_ROOT } from "./install_engine.js";
|
|
17
|
+
// B (template serving): discovery.md fill rules handed to the model alongside the template skeleton
|
|
18
|
+
// so every model produces the same structure (instead of freehanding a section the gate then has to
|
|
19
|
+
// parse heuristically). Kept here, not in the .md template, so they are versioned with the code that
|
|
20
|
+
// depends on them (the open-questions checkbox convention consumed by discovery_open_question_count).
|
|
21
|
+
const DISCOVERY_RULES = [
|
|
22
|
+
"按模板骨架填写 discovery.md,保留全部段落(调查范围 / 现有实现事实 / 隐性合约 / 风险与歧义 / 待确认问题 / Subagent Evidence),不得自创或删减段落骨架。",
|
|
23
|
+
"所有需要用户拍板的问题写进「## 待确认问题」段,每条用「- [ ]」;用户确认后改成「- [x]」或在项内写「已确认:」。",
|
|
24
|
+
"explore_complete 检查在该段仍有「- [ ]」或「仍需确认/待确认」项时不会通过。",
|
|
25
|
+
"段标题含「确认」字样即可被识别(待确认问题 / 需要用户确认的问题 等),不要改成无「确认」字样的标题。",
|
|
26
|
+
];
|
|
27
|
+
// Reads the canonical discovery template from the package. packageRoot is parameterized so tests can
|
|
28
|
+
// inject an empty dir to exercise the "unreadable → omit field" path; PACKAGE_ROOT itself is a
|
|
29
|
+
// module-load-time const and cannot be mocked. Returns null (not throws) when missing so a transient
|
|
30
|
+
// read failure never turns the whole packet into an error response.
|
|
31
|
+
export function read_discovery_template(packageRoot = PACKAGE_ROOT) {
|
|
32
|
+
const p = join(packageRoot, "templates", "sidecar", "discovery.md");
|
|
33
|
+
if (!existsSync(p) || !statSync(p).isFile())
|
|
34
|
+
return null;
|
|
35
|
+
try {
|
|
36
|
+
return readFileSync(p, "utf8");
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
16
42
|
function load_packet_context(change) {
|
|
17
43
|
if (typeof runtime.load_context === "function") {
|
|
18
44
|
const [status, repoRoot, changeRoot, evidences] = runtime.load_context(change);
|
|
@@ -168,18 +194,18 @@ function evaluate_workflow_decision(ctx, gateRaw, taskId) {
|
|
|
168
194
|
function gate_recheck_command(change, gate, taskId, diagnostic = false) {
|
|
169
195
|
const format = diagnostic ? "json" : "agent";
|
|
170
196
|
if (gate === "apply_ready")
|
|
171
|
-
return `superspec
|
|
197
|
+
return `superspec check check-apply-ready --change "${change}" --format ${format}`;
|
|
172
198
|
if (gate === "review_complete")
|
|
173
|
-
return `superspec
|
|
199
|
+
return `superspec check check-review-complete --change "${change}" --format ${format}`;
|
|
174
200
|
if (gate === "archive_ready")
|
|
175
|
-
return `superspec
|
|
201
|
+
return `superspec check check-archive-ready --change "${change}" --format ${format}`;
|
|
176
202
|
if (gate === "task_edit")
|
|
177
|
-
return `superspec
|
|
203
|
+
return `superspec check check-task-edit --change "${change}" --task-id "${taskId ?? ""}" --format ${format}`;
|
|
178
204
|
if (gate === "task_complete")
|
|
179
|
-
return `superspec
|
|
205
|
+
return `superspec check check-task-complete --change "${change}" --task-id "${taskId ?? ""}" --format ${format}`;
|
|
180
206
|
if (gate === "task_reopen")
|
|
181
|
-
return `superspec
|
|
182
|
-
return `superspec
|
|
207
|
+
return `superspec check check-task-reopen --change "${change}" --task-id "${taskId ?? ""}" --format ${format}`;
|
|
208
|
+
return `superspec check check-enter --change "${change}" --gate "${gate}" --format ${format}`;
|
|
183
209
|
}
|
|
184
210
|
function default_allowed_next_action(gate) {
|
|
185
211
|
if (gate === "review_complete")
|
|
@@ -390,6 +416,14 @@ function workflow_packet(ctx, gateRaw, taskId) {
|
|
|
390
416
|
const decisions = decision_selectors_for_gate(ctx, gate);
|
|
391
417
|
if (decisions.length > 0)
|
|
392
418
|
packet.must_read_verbatim_decisions = decisions;
|
|
419
|
+
// B (template serving): hand the model the discovery skeleton + fill rules so its output structure
|
|
420
|
+
// is uniform. Gate-scoped to explore_complete (the only gate that produces discovery.md).
|
|
421
|
+
if (gate === "explore_complete") {
|
|
422
|
+
const tpl = read_discovery_template();
|
|
423
|
+
if (tpl)
|
|
424
|
+
packet.discovery_template = tpl;
|
|
425
|
+
packet.discovery_rules = [...DISCOVERY_RULES];
|
|
426
|
+
}
|
|
393
427
|
return packet;
|
|
394
428
|
}
|
|
395
429
|
function live_output_refs(changeRoot, evidences) {
|
|
@@ -428,7 +462,19 @@ function verification_output_fields() {
|
|
|
428
462
|
return [...ROLE_EVIDENCE_FIELDS, ...VERIFY_EVIDENCE_REQUIRED_FIELDS, "scope_drift"];
|
|
429
463
|
}
|
|
430
464
|
function review_output_fields() {
|
|
431
|
-
return [
|
|
465
|
+
return [
|
|
466
|
+
...ROLE_EVIDENCE_FIELDS,
|
|
467
|
+
"review_round_id format: <gate>-r<round>",
|
|
468
|
+
"findings[] object fields: finding_id, finding_uid, finding_type, category, summary",
|
|
469
|
+
"finding_uid format: <gate>:<evidence_id>:<finding_id>",
|
|
470
|
+
`finding_type values: ${renderList([...FINDING_TYPES].sort())}`,
|
|
471
|
+
`category values: ${renderList([...FINDING_CATEGORIES].sort())}`,
|
|
472
|
+
`material_categories values: ${renderList([...MATERIAL_CATEGORIES].sort())}`,
|
|
473
|
+
"decision_scope_key required when material_categories is non-empty",
|
|
474
|
+
"summary must be verbatim disclosure source text",
|
|
475
|
+
"acknowledged_accepted_deviation_uids must be a string array when present",
|
|
476
|
+
"findings[].supersedes_finding_uids must be a string array when present",
|
|
477
|
+
];
|
|
432
478
|
}
|
|
433
479
|
function source_guidance_output_fields() {
|
|
434
480
|
return [
|
|
@@ -617,6 +663,7 @@ function apply_worker_stop_conditions(kind) {
|
|
|
617
663
|
if (kind === "apply_test") {
|
|
618
664
|
return [
|
|
619
665
|
"Stop after reporting the bounded test command result only.",
|
|
666
|
+
"Do not let the main thread run or forge formal RED/characterization/GREEN evidence.",
|
|
620
667
|
"Do not change implementation files.",
|
|
621
668
|
"Return raw logs as pinned artifact refs when output is long.",
|
|
622
669
|
];
|
|
@@ -705,7 +752,7 @@ function require_active_apply_worker_chain(ctx, taskId, blockers) {
|
|
|
705
752
|
blockers.push(reason("missing_apply_worker_chain_active", `task ${taskId} requires an active apply_worker_chain marker before downstream worker packet generation`));
|
|
706
753
|
}
|
|
707
754
|
else {
|
|
708
|
-
blockers.push(...pre_edit_evidence_ref_reasons(ctx.evidences, active, taskId, "apply_worker_chain_active_invalid"));
|
|
755
|
+
blockers.push(...pre_edit_evidence_ref_reasons(ctx.changeRoot, ctx.evidences, active, taskId, "apply_worker_chain_active_invalid"));
|
|
709
756
|
}
|
|
710
757
|
return chainId;
|
|
711
758
|
}
|
|
@@ -871,7 +918,7 @@ function validate_test_run_evidence_refs(ctx, refs, expected) {
|
|
|
871
918
|
}
|
|
872
919
|
return { refs: resolved, blockers };
|
|
873
920
|
}
|
|
874
|
-
function validate_active_apply_worker_chain_refs(ctx, refs, taskId, expectedChainId, expectedPacketFingerprint, expectedSourceImplementationFingerprint, expectedDeclaredTaskWriteScope, expectedPreEditEvidenceRefs) {
|
|
921
|
+
function validate_active_apply_worker_chain_refs(ctx, refs, taskId, expectedChainId, expectedPacketFingerprint, expectedSourceImplementationFingerprint, expectedDeclaredTaskWriteScope, expectedPreEditEvidenceRefs, expectedActiveFields = {}) {
|
|
875
922
|
if (refs.length === 0) {
|
|
876
923
|
return {
|
|
877
924
|
refs: [],
|
|
@@ -902,6 +949,11 @@ function validate_active_apply_worker_chain_refs(ctx, refs, taskId, expectedChai
|
|
|
902
949
|
if (!deepEqual(stringList(value.pre_edit_evidence_refs), stringList(expectedPreEditEvidenceRefs))) {
|
|
903
950
|
fieldBlockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: pre_edit_evidence_refs mismatch`, [ref]));
|
|
904
951
|
}
|
|
952
|
+
for (const field of ["pre_edit_proof_kind", "apply_execution_surface", "tdd_required", "no_tdd_reason"]) {
|
|
953
|
+
if (field in expectedActiveFields && !deepEqual(value[field], expectedActiveFields[field])) {
|
|
954
|
+
fieldBlockers.push(reason("apply_worker_chain_ref_invalid", `${ref}: ${field} mismatch`, [ref]));
|
|
955
|
+
}
|
|
956
|
+
}
|
|
905
957
|
return fieldBlockers;
|
|
906
958
|
};
|
|
907
959
|
for (const ref of refs) {
|
|
@@ -961,9 +1013,69 @@ function pre_edit_refs_bound_to_active_chain(ctx, active, refs, label, taskId) {
|
|
|
961
1013
|
if (!allowed.has(evidenceId))
|
|
962
1014
|
blockers.push(reason("pinned_evidence_ref_invalid", `${label} evidence ref is not part of active apply_worker_chain pre_edit_evidence_refs: ${evidenceId}`, [evidenceId]));
|
|
963
1015
|
}
|
|
964
|
-
blockers.push(...pre_edit_evidence_ref_reasons(ctx.evidences, active, taskId, "pinned_evidence_ref_invalid"));
|
|
1016
|
+
blockers.push(...pre_edit_evidence_ref_reasons(ctx.changeRoot, ctx.evidences, active, taskId, "pinned_evidence_ref_invalid"));
|
|
965
1017
|
return blockers;
|
|
966
1018
|
}
|
|
1019
|
+
function validate_alternative_verification_evidence_refs(ctx, refs, taskId) {
|
|
1020
|
+
if (refs.length === 0) {
|
|
1021
|
+
return {
|
|
1022
|
+
refs: [],
|
|
1023
|
+
blockers: [reason("missing_alternative_verification_evidence_ref", "apply-verify-packet requires --alternative-verification-evidence-ref for no-TDD alternative verification")],
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
const resolved = [];
|
|
1027
|
+
const blockers = [];
|
|
1028
|
+
for (const ref of refs) {
|
|
1029
|
+
let evidenceId = ref;
|
|
1030
|
+
const maybePath = safe_within(ctx.changeRoot, ref);
|
|
1031
|
+
if (maybePath !== null && existsSync(maybePath) && statSync(maybePath).isFile()) {
|
|
1032
|
+
const loaded = read_json_ref(ctx, ref);
|
|
1033
|
+
blockers.push(...loaded.blockers);
|
|
1034
|
+
if (loaded.value) {
|
|
1035
|
+
if (loaded.value.kind !== "alternative_verification" && loaded.value.kind !== "manual_verification") {
|
|
1036
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: kind must be alternative_verification or manual_verification`, [ref]));
|
|
1037
|
+
}
|
|
1038
|
+
if (normalize_gate(String(loaded.value.gate ?? "")) !== "task_complete")
|
|
1039
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: gate must be task_complete`, [ref]));
|
|
1040
|
+
if (loaded.value.task_id !== taskId)
|
|
1041
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: task_id must be ${taskId}`, [ref]));
|
|
1042
|
+
if (typeof loaded.value.evidence_id === "string" && loaded.value.evidence_id)
|
|
1043
|
+
evidenceId = loaded.value.evidence_id;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
const ev = live_pass(ctx.evidences, { task_id: taskId })
|
|
1047
|
+
.find((item) => String(item.evidence_id ?? "") === evidenceId && (item.kind === "alternative_verification" || item.kind === "manual_verification"));
|
|
1048
|
+
if (!ev) {
|
|
1049
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `alternative verification evidence ref is not live/pass for ${taskId}: ${ref}`, [ref]));
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
if (normalize_gate(String(ev.gate ?? "")) !== "task_complete")
|
|
1053
|
+
blockers.push(reason("pinned_evidence_ref_invalid", `${ref}: gate must be task_complete`, [ref]));
|
|
1054
|
+
resolved.push(ev);
|
|
1055
|
+
}
|
|
1056
|
+
return { refs: resolved, blockers };
|
|
1057
|
+
}
|
|
1058
|
+
function active_chain_verifier_input_ref(active) {
|
|
1059
|
+
return {
|
|
1060
|
+
kind: "apply_worker_chain",
|
|
1061
|
+
evidence_id: active?.evidence_id,
|
|
1062
|
+
task_id: active?.task_id,
|
|
1063
|
+
apply_worker_chain_id: active?.apply_worker_chain_id,
|
|
1064
|
+
pre_edit_proof_kind: active?.pre_edit_proof_kind ?? "red_or_characterization",
|
|
1065
|
+
pre_edit_evidence_refs: Array.isArray(active?.pre_edit_evidence_refs) ? active.pre_edit_evidence_refs.map(String).filter(Boolean).sort() : [],
|
|
1066
|
+
...(active?.apply_execution_surface !== undefined ? { apply_execution_surface: active.apply_execution_surface } : {}),
|
|
1067
|
+
...(active?.tdd_required !== undefined ? { tdd_required: active.tdd_required } : {}),
|
|
1068
|
+
...(active?.no_tdd_reason !== undefined ? { no_tdd_reason: active.no_tdd_reason } : {}),
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
function alternative_evidence_input_ref(ev) {
|
|
1072
|
+
return {
|
|
1073
|
+
kind: ev.kind,
|
|
1074
|
+
evidence_id: ev.evidence_id,
|
|
1075
|
+
gate: ev.gate,
|
|
1076
|
+
task_id: ev.task_id,
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
967
1079
|
function write_scope_blockers(taskId, writeScope) {
|
|
968
1080
|
const blockers = [];
|
|
969
1081
|
if (writeScope.length === 0) {
|
|
@@ -987,13 +1099,23 @@ function write_scope_blockers(taskId, writeScope) {
|
|
|
987
1099
|
function pre_edit_evidence_refs(ctx, taskId) {
|
|
988
1100
|
return live_pass(ctx.evidences, { gate: "task_edit", kind: "test_run", task_id: taskId })
|
|
989
1101
|
.filter((ev) => ev.semantic_status === "expected_failure" || ev.semantic_status === "expected_success")
|
|
1102
|
+
.filter((ev) => pre_edit_worker_test_run_reasons(ctx.changeRoot, ev, taskId).length === 0)
|
|
990
1103
|
.map((ev) => String(ev.evidence_id ?? ""))
|
|
991
1104
|
.filter(Boolean)
|
|
992
1105
|
.sort();
|
|
993
1106
|
}
|
|
994
1107
|
function apply_task_context_fields(ctx, taskId, task, writeScope, expectedGuardRefs = []) {
|
|
1108
|
+
const executionSurface = task ? task_apply_execution_surface(task) : "";
|
|
995
1109
|
return {
|
|
996
1110
|
task_content_ref: change_pinned_ref(ctx.changeRoot, "tasks.md"),
|
|
1111
|
+
apply_execution_surface: executionSurface,
|
|
1112
|
+
apply_execution_surface_source: task?.attrs.apply_execution_surface ? "explicit" : "default",
|
|
1113
|
+
apply_execution_surface_policy: {
|
|
1114
|
+
implementation_requires_executor_worker_chain: true,
|
|
1115
|
+
runtime_config_requires_executor_worker_chain: true,
|
|
1116
|
+
docs_generated_allows_direct_alternative_verification: true,
|
|
1117
|
+
no_code_requires_empty_write_scope: true,
|
|
1118
|
+
},
|
|
997
1119
|
task_acceptance_refs: task ? splitList(task.attrs.requirement_refs ?? "") : [],
|
|
998
1120
|
task_invariant_refs: task ? splitList(task.attrs.invariant_refs ?? "") : [],
|
|
999
1121
|
task_test_refs: task ? splitList(task.attrs.test_refs ?? "") : [],
|
|
@@ -1113,15 +1235,32 @@ function apply_executor_packet(ctx, args) {
|
|
|
1113
1235
|
const { task, blockers: taskBlockers } = task_lookup_blockers(ctx, taskId);
|
|
1114
1236
|
blockers.push(...taskBlockers);
|
|
1115
1237
|
const writeScope = task ? splitList(task.attrs.write_scope ?? "") : [];
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1238
|
+
const executionSurface = task ? task_apply_execution_surface(task) : "";
|
|
1239
|
+
const tddRequired = task ? (task.attrs.tdd_required ?? "true").toLowerCase() !== "false" : true;
|
|
1240
|
+
const noTddReason = task?.attrs.no_tdd_reason;
|
|
1241
|
+
const preEditProofKind = tddRequired ? "red_or_characterization" : "no_tdd_declared";
|
|
1242
|
+
const activePreEditRefs = preEditProofKind === "no_tdd_declared" ? [] : pre_edit_evidence_refs(ctx, taskId);
|
|
1243
|
+
if (task) {
|
|
1244
|
+
blockers.push(...task_apply_execution_surface_reasons(taskId, task));
|
|
1245
|
+
if (executionSurface === "implementation" || executionSurface === "runtime_config") {
|
|
1246
|
+
if (!tddRequired && !NO_TDD_REASONS.has(String(noTddReason ?? ""))) {
|
|
1247
|
+
blockers.push(reason("invalid_no_tdd_reason", `task ${taskId}: no_tdd_reason=${String(noTddReason ?? "")}`));
|
|
1248
|
+
}
|
|
1249
|
+
blockers.push(...write_scope_blockers(taskId, writeScope));
|
|
1250
|
+
}
|
|
1251
|
+
else {
|
|
1252
|
+
blockers.push(reason("executor_handoff_not_required", `task ${taskId} apply_execution_surface=${executionSurface} does not use executor-worker implementation handoff`));
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
else {
|
|
1256
|
+
blockers.push(...write_scope_blockers(taskId, writeScope));
|
|
1119
1257
|
}
|
|
1120
1258
|
blockers.push(...apply_worker_chain_packet_state(ctx, taskId).blockers);
|
|
1121
1259
|
const packet = apply_packet_common(ctx, "apply_executor", taskId, "executor_worker", blockers);
|
|
1122
1260
|
const chainId = active_apply_worker_chain_id(ctx, taskId) ?? generated_apply_worker_chain_id(ctx, taskId, writeScope);
|
|
1123
1261
|
packet.worker_chain_context = "executor_worker";
|
|
1124
1262
|
packet.apply_worker_chain_id = chainId;
|
|
1263
|
+
packet.apply_execution_surface = executionSurface;
|
|
1125
1264
|
packet.declared_task_write_scope = writeScope;
|
|
1126
1265
|
packet.task_edit = decision_status(taskEdit);
|
|
1127
1266
|
packet.task_complete = safe_decision_status(ctx, "task_complete", taskId);
|
|
@@ -1145,11 +1284,20 @@ function apply_executor_packet(ctx, args) {
|
|
|
1145
1284
|
executor_packet_fingerprint: "",
|
|
1146
1285
|
source_implementation_fingerprint: implementation_fingerprint(ctx, writeScope),
|
|
1147
1286
|
declared_task_write_scope: writeScope,
|
|
1148
|
-
|
|
1287
|
+
pre_edit_proof_kind: preEditProofKind,
|
|
1288
|
+
apply_execution_surface: executionSurface,
|
|
1289
|
+
tdd_required: tddRequired,
|
|
1290
|
+
...(tddRequired ? {} : { no_tdd_reason: noTddReason }),
|
|
1291
|
+
pre_edit_evidence_refs: activePreEditRefs,
|
|
1149
1292
|
};
|
|
1150
1293
|
if (args.packet_format === "prompt") {
|
|
1151
1294
|
const expectedPacketFingerprint = apply_worker_packet_fingerprint(packet);
|
|
1152
|
-
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
|
|
1295
|
+
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, {
|
|
1296
|
+
pre_edit_proof_kind: packet.chain_activation_template.pre_edit_proof_kind,
|
|
1297
|
+
apply_execution_surface: packet.chain_activation_template.apply_execution_surface,
|
|
1298
|
+
tdd_required: packet.chain_activation_template.tdd_required,
|
|
1299
|
+
...(packet.chain_activation_template.no_tdd_reason !== undefined ? { no_tdd_reason: packet.chain_activation_template.no_tdd_reason } : {}),
|
|
1300
|
+
});
|
|
1153
1301
|
blockers.push(...activeRefs.blockers);
|
|
1154
1302
|
packet.apply_worker_chain_refs = args.apply_worker_chain_refs ?? [];
|
|
1155
1303
|
packet.apply_worker_chain_active_refs = activeRefs.refs;
|
|
@@ -1240,10 +1388,13 @@ function apply_verify_packet(ctx, args) {
|
|
|
1240
1388
|
const { task, blockers: taskBlockers } = task_lookup_blockers(ctx, taskId);
|
|
1241
1389
|
blockers.push(...taskBlockers);
|
|
1242
1390
|
const writeScope = task ? splitList(task.attrs.write_scope ?? "") : [];
|
|
1391
|
+
const executionSurface = task ? task_apply_execution_surface(task) : "";
|
|
1392
|
+
const tddRequired = task ? (task.attrs.tdd_required ?? "true").toLowerCase() !== "false" : true;
|
|
1243
1393
|
blockers.push(...write_scope_blockers(taskId, writeScope));
|
|
1244
1394
|
const chainId = require_active_apply_worker_chain(ctx, taskId, blockers);
|
|
1245
1395
|
const activeChain = active_apply_worker_chain(ctx, taskId).active;
|
|
1246
1396
|
const packet = apply_packet_common(ctx, "apply_verify", taskId, "executor_worker", blockers);
|
|
1397
|
+
const completionProofKind = (args.alternative_verification_evidence_refs ?? []).length > 0 ? "alternative_verification" : "green_tests";
|
|
1247
1398
|
if (chainId) {
|
|
1248
1399
|
const executorRefs = validate_worker_report_refs(ctx, args.executor_report_refs ?? [], {
|
|
1249
1400
|
role: "executor",
|
|
@@ -1286,7 +1437,8 @@ function apply_verify_packet(ctx, args) {
|
|
|
1286
1437
|
missingCode: "missing_characterization_test_run_evidence_ref",
|
|
1287
1438
|
missingMessage: "apply-verify-packet requires --characterization-test-run-evidence-ref",
|
|
1288
1439
|
});
|
|
1289
|
-
|
|
1440
|
+
const alternativeRefs = validate_alternative_verification_evidence_refs(ctx, args.alternative_verification_evidence_refs ?? [], taskId);
|
|
1441
|
+
blockers.push(...executorRefs.blockers, ...codeReviewRefs.blockers);
|
|
1290
1442
|
if (executorRefs.refs[0]) {
|
|
1291
1443
|
blockers.push(...worker_report_origin_blockers(executorRefs.refs[0], activeChain?.executor_packet_fingerprint, "executor_report_ref"));
|
|
1292
1444
|
if (activeChain) {
|
|
@@ -1296,19 +1448,47 @@ function apply_verify_packet(ctx, args) {
|
|
|
1296
1448
|
if (executorRefs.refs[0] && codeReviewRefs.refs[0]) {
|
|
1297
1449
|
blockers.push(...worker_report_input_blockers(codeReviewRefs.refs[0], [executorRefs.refs[0]], "task_code_review_report_ref"));
|
|
1298
1450
|
}
|
|
1299
|
-
if (
|
|
1300
|
-
blockers.push(
|
|
1451
|
+
if (completionProofKind === "green_tests") {
|
|
1452
|
+
blockers.push(...greenRefs.blockers);
|
|
1453
|
+
if ((args.red_test_run_evidence_refs ?? []).length === 0 && (args.characterization_test_run_evidence_refs ?? []).length === 0) {
|
|
1454
|
+
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"));
|
|
1455
|
+
}
|
|
1456
|
+
else {
|
|
1457
|
+
if ((args.red_test_run_evidence_refs ?? []).length > 0)
|
|
1458
|
+
blockers.push(...redRefs.blockers, ...pre_edit_refs_bound_to_active_chain(ctx, activeChain, redRefs.refs, "red", taskId));
|
|
1459
|
+
if ((args.characterization_test_run_evidence_refs ?? []).length > 0)
|
|
1460
|
+
blockers.push(...characterizationRefs.blockers, ...pre_edit_refs_bound_to_active_chain(ctx, activeChain, characterizationRefs.refs, "characterization", taskId));
|
|
1461
|
+
}
|
|
1301
1462
|
}
|
|
1302
1463
|
else {
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
if (
|
|
1306
|
-
blockers.push(
|
|
1464
|
+
blockers.push(...alternativeRefs.blockers);
|
|
1465
|
+
const activePreIds = Array.isArray(activeChain?.pre_edit_evidence_refs) ? activeChain.pre_edit_evidence_refs.map(String).filter(Boolean) : [];
|
|
1466
|
+
if (activeChain?.pre_edit_proof_kind !== "no_tdd_declared") {
|
|
1467
|
+
blockers.push(reason("apply_worker_chain_active_invalid", "alternative verification requires active apply_worker_chain pre_edit_proof_kind=no_tdd_declared"));
|
|
1468
|
+
}
|
|
1469
|
+
if (activePreIds.length > 0) {
|
|
1470
|
+
blockers.push(reason("apply_worker_chain_active_invalid", `alternative verification requires empty active pre_edit_evidence_refs: ${renderList(activePreIds)}`, activePreIds));
|
|
1471
|
+
}
|
|
1472
|
+
if (tddRequired || activeChain?.tdd_required !== false) {
|
|
1473
|
+
blockers.push(reason("apply_worker_chain_active_invalid", "alternative verification requires task and active apply_worker_chain tdd_required=false"));
|
|
1474
|
+
}
|
|
1475
|
+
if (executionSurface !== "implementation" && executionSurface !== "runtime_config") {
|
|
1476
|
+
blockers.push(reason("alternative_verification_surface_invalid", `alternative verification worker-chain proof requires implementation/runtime_config surface, got ${executionSurface}`));
|
|
1477
|
+
}
|
|
1478
|
+
if (activeChain?.apply_execution_surface !== executionSurface) {
|
|
1479
|
+
blockers.push(reason("apply_worker_chain_active_invalid", "active apply_worker_chain apply_execution_surface must match task surface"));
|
|
1480
|
+
}
|
|
1481
|
+
if (!NO_TDD_REASONS.has(String(activeChain?.no_tdd_reason ?? "")) || activeChain?.no_tdd_reason !== task?.attrs.no_tdd_reason) {
|
|
1482
|
+
blockers.push(reason("apply_worker_chain_active_invalid", "active apply_worker_chain no_tdd_reason must be valid and match task"));
|
|
1483
|
+
}
|
|
1307
1484
|
}
|
|
1308
1485
|
packet.apply_worker_chain_id = chainId;
|
|
1486
|
+
packet.completion_proof_kind = completionProofKind;
|
|
1487
|
+
packet.pre_edit_proof_kind = activeChain?.pre_edit_proof_kind ?? "red_or_characterization";
|
|
1309
1488
|
packet.executor_report_pinned_refs = executorRefs.refs;
|
|
1310
1489
|
packet.task_code_review_report_pinned_refs = codeReviewRefs.refs;
|
|
1311
1490
|
packet.green_test_run_evidence_pinned_refs = greenRefs.refs;
|
|
1491
|
+
packet.alternative_verification_evidence_pinned_refs = alternativeRefs.refs;
|
|
1312
1492
|
packet.red_test_run_evidence_pinned_refs = redRefs.refs;
|
|
1313
1493
|
packet.characterization_test_run_evidence_pinned_refs = characterizationRefs.refs;
|
|
1314
1494
|
if (executorRefs.refs[0])
|
|
@@ -1318,13 +1498,14 @@ function apply_verify_packet(ctx, args) {
|
|
|
1318
1498
|
if (executorRefs.refs[0] && codeReviewRefs.refs[0]) {
|
|
1319
1499
|
packet.expected_code_review_input_ref_digest = worker_input_ref_digest([executorRefs.refs[0]]);
|
|
1320
1500
|
}
|
|
1321
|
-
if (executorRefs.refs[0] && codeReviewRefs.refs[0] && greenRefs.refs[0]) {
|
|
1501
|
+
if (completionProofKind === "green_tests" && executorRefs.refs[0] && codeReviewRefs.refs[0] && greenRefs.refs[0]) {
|
|
1322
1502
|
const canonicalGreenRefs = [...greenRefs.refs].sort((a, b) => String(a.evidence_id ?? "").localeCompare(String(b.evidence_id ?? "")));
|
|
1323
1503
|
const expectedFreshness = compute_apply_worker_freshness(ctx.repoRoot, ctx.changeRoot, ctx.evidences, taskId, chainId, {
|
|
1324
1504
|
executor_report_ref: executorRefs.refs[0],
|
|
1325
1505
|
task_code_review_report_ref: codeReviewRefs.refs[0],
|
|
1326
1506
|
green_test_run_evidence_ref: String(canonicalGreenRefs[0].evidence_id ?? ""),
|
|
1327
1507
|
green_test_run_evidence_refs: canonicalGreenRefs.map((ev) => String(ev.evidence_id ?? "")).filter(Boolean),
|
|
1508
|
+
completion_proof_kind: "green_tests",
|
|
1328
1509
|
});
|
|
1329
1510
|
packet.expected_freshness_fingerprint = expectedFreshness;
|
|
1330
1511
|
const preEditRefs = [
|
|
@@ -1351,8 +1532,34 @@ function apply_verify_packet(ctx, args) {
|
|
|
1351
1532
|
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)));
|
|
1352
1533
|
}
|
|
1353
1534
|
}
|
|
1535
|
+
else if (completionProofKind === "alternative_verification" && executorRefs.refs[0] && codeReviewRefs.refs[0] && alternativeRefs.refs[0]) {
|
|
1536
|
+
const canonicalAlternativeRefs = [...alternativeRefs.refs].sort((a, b) => String(a.evidence_id ?? "").localeCompare(String(b.evidence_id ?? "")));
|
|
1537
|
+
const expectedFreshness = compute_apply_worker_freshness(ctx.repoRoot, ctx.changeRoot, ctx.evidences, taskId, chainId, {
|
|
1538
|
+
executor_report_ref: executorRefs.refs[0],
|
|
1539
|
+
task_code_review_report_ref: codeReviewRefs.refs[0],
|
|
1540
|
+
alternative_verification_evidence_refs: canonicalAlternativeRefs.map((ev) => String(ev.evidence_id ?? "")).filter(Boolean),
|
|
1541
|
+
completion_proof_kind: "alternative_verification",
|
|
1542
|
+
});
|
|
1543
|
+
packet.expected_freshness_fingerprint = expectedFreshness;
|
|
1544
|
+
packet.expected_verifier_input_ref_digest = worker_input_ref_digest([
|
|
1545
|
+
executorRefs.refs[0],
|
|
1546
|
+
codeReviewRefs.refs[0],
|
|
1547
|
+
...canonicalAlternativeRefs.map(alternative_evidence_input_ref),
|
|
1548
|
+
active_chain_verifier_input_ref(activeChain),
|
|
1549
|
+
]);
|
|
1550
|
+
if (!fingerprint_matches(codeReviewRefs.refs[0].observed_implementation_fingerprint, expectedFreshness.implementation_fingerprint)) {
|
|
1551
|
+
blockers.push(reason("task_code_review_implementation_fingerprint_mismatch", "apply-verify-packet requires code-reviewer observed_implementation_fingerprint to match current implementation fingerprint"));
|
|
1552
|
+
}
|
|
1553
|
+
if (Array.isArray(expectedFreshness.protected_dirty_paths) && expectedFreshness.protected_dirty_paths.length > 0) {
|
|
1554
|
+
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)));
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1354
1557
|
}
|
|
1355
1558
|
packet.declared_task_write_scope = writeScope;
|
|
1559
|
+
packet.apply_execution_surface = executionSurface;
|
|
1560
|
+
packet.tdd_required = tddRequired;
|
|
1561
|
+
if (!tddRequired)
|
|
1562
|
+
packet.no_tdd_reason = task?.attrs.no_tdd_reason;
|
|
1356
1563
|
packet.task_edit = decision_status(taskEdit);
|
|
1357
1564
|
packet.task_complete = safe_decision_status(ctx, "task_complete", taskId);
|
|
1358
1565
|
Object.assign(packet, apply_task_context_fields(ctx, taskId, task, writeScope, [
|
|
@@ -1369,20 +1576,30 @@ function apply_verify_packet(ctx, args) {
|
|
|
1369
1576
|
require_scope_verdict: true,
|
|
1370
1577
|
};
|
|
1371
1578
|
packet.verification_checks = [
|
|
1372
|
-
"
|
|
1579
|
+
completionProofKind === "green_tests"
|
|
1580
|
+
? "red_or_characterization_pre_edit_evidence_bound_to_active_chain"
|
|
1581
|
+
: "no_tdd_declared_active_chain_has_empty_pre_edit_refs",
|
|
1373
1582
|
"executor_report_same_chain_and_fresh",
|
|
1374
1583
|
"task_code_review_report_same_chain_and_fresh",
|
|
1375
|
-
"
|
|
1584
|
+
completionProofKind === "green_tests"
|
|
1585
|
+
? "green_test_run_same_chain_and_pinned_transcripts"
|
|
1586
|
+
: "alternative_or_manual_verification_refs_live_pass_and_digest_bound",
|
|
1376
1587
|
"current_freshness_matches_expected_freshness_fingerprint",
|
|
1377
1588
|
"protected_change_artifacts_clean",
|
|
1378
1589
|
];
|
|
1379
1590
|
packet.executor_report_required_fields = [...APPLY_EXECUTOR_REPORT_REQUIRED_FIELDS];
|
|
1380
1591
|
packet.code_review_report_required_fields = [...APPLY_CODE_REVIEW_REPORT_REQUIRED_FIELDS];
|
|
1381
|
-
packet.verifier_report_required_fields = [
|
|
1592
|
+
packet.verifier_report_required_fields = [
|
|
1593
|
+
...APPLY_VERIFIER_REPORT_REQUIRED_FIELDS,
|
|
1594
|
+
...(completionProofKind === "green_tests"
|
|
1595
|
+
? ["green_test_run_evidence_refs", "red_test_run_evidence_refs_or_characterization_test_run_evidence_refs"]
|
|
1596
|
+
: ["alternative_verification_evidence_refs", "apply_execution_surface", "tdd_required", "no_tdd_reason"]),
|
|
1597
|
+
];
|
|
1382
1598
|
packet.test_evidence_required_fields = ["command", "cwd", "exit_code", "repo_head", "implementation_fingerprint", "guard_artifact_manifest_fingerprint", "raw_log_pinned_refs"];
|
|
1383
1599
|
packet.executor_report_refs = args.executor_report_refs ?? [];
|
|
1384
1600
|
packet.task_code_review_report_refs = args.task_code_review_report_refs ?? [];
|
|
1385
1601
|
packet.green_test_run_evidence_refs = args.green_test_run_evidence_refs ?? [];
|
|
1602
|
+
packet.alternative_verification_evidence_refs = args.alternative_verification_evidence_refs ?? [];
|
|
1386
1603
|
packet.red_test_run_evidence_refs = args.red_test_run_evidence_refs ?? [];
|
|
1387
1604
|
packet.characterization_test_run_evidence_refs = args.characterization_test_run_evidence_refs ?? [];
|
|
1388
1605
|
packet.worker_state = blockers.length === 0 ? "ready" : "blocked";
|
|
@@ -29,6 +29,8 @@ export type WorkflowPacket = {
|
|
|
29
29
|
must_read_verbatim_findings?: FindingSelector[];
|
|
30
30
|
must_read_verbatim_decisions?: DecisionSelector[];
|
|
31
31
|
diagnostic_command?: string;
|
|
32
|
+
discovery_template?: string;
|
|
33
|
+
discovery_rules?: string[];
|
|
32
34
|
};
|
|
33
35
|
export type ReviewPacket = {
|
|
34
36
|
consumer: "role" | "main-thread";
|
package/dist/src/tasks.d.ts
CHANGED
|
@@ -25,6 +25,11 @@ export declare function parse_spec_scenarios(changeRoot: string): string[];
|
|
|
25
25
|
export declare function test_contract_covers_scenario(changeRoot: string, scenario: string): boolean;
|
|
26
26
|
export declare function test_contract_invariant_refs_by_test(changeRoot: string): Map<string, Set<string>>;
|
|
27
27
|
export declare function task_test_refs(tasks: Record<string, TaskInfo>): Set<string>;
|
|
28
|
+
export declare const APPLY_EXECUTION_SURFACES: Set<string>;
|
|
29
|
+
export type ApplyExecutionSurface = "implementation" | "runtime_config" | "docs_generated" | "no_code";
|
|
30
|
+
export declare function docs_generated_write_scope_reasons(writeScope: string[], taskId?: string): Reason[];
|
|
31
|
+
export declare function task_apply_execution_surface(task: TaskInfo): ApplyExecutionSurface | string;
|
|
32
|
+
export declare function task_apply_execution_surface_reasons(taskId: string, task: TaskInfo): Reason[];
|
|
28
33
|
export declare function write_scope_conflict_reasons(tasks: Record<string, TaskInfo>): Reason[];
|
|
29
34
|
export declare function red_green_test_ids(evidences: JsonMap[]): Set<string>;
|
|
30
35
|
export declare function task_test_evidence(evidences: JsonMap[], taskId: string, semanticStatus: string, gate?: string | null): JsonMap[];
|