@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.
Files changed (45) hide show
  1. package/README.md +8 -10
  2. package/dist/src/apply_worker_chain.d.ts +6 -3
  3. package/dist/src/apply_worker_chain.js +125 -30
  4. package/dist/src/apply_worker_chain_lifecycle.d.ts +7 -5
  5. package/dist/src/apply_worker_chain_lifecycle.js +153 -22
  6. package/dist/src/cli_args.d.ts +1 -0
  7. package/dist/src/cli_args.js +33 -15
  8. package/dist/src/disclosure.js +2 -2
  9. package/dist/src/evidence.js +104 -8
  10. package/dist/src/gates.d.ts +1 -0
  11. package/dist/src/gates.js +160 -27
  12. package/dist/src/hooks/adapter.js +43 -7
  13. package/dist/src/hooks/guard_api.js +2 -2
  14. package/dist/src/hooks/health.js +0 -12
  15. package/dist/src/hooks/policy_event.js +11 -1
  16. package/dist/src/i18n.js +63 -3
  17. package/dist/src/init_cli.js +1 -1
  18. package/dist/src/install_engine.js +64 -0
  19. package/dist/src/openspec.d.ts +2 -0
  20. package/dist/src/openspec.js +31 -0
  21. package/dist/src/packet_render.d.ts +1 -0
  22. package/dist/src/packet_render.js +248 -31
  23. package/dist/src/packet_schema.d.ts +2 -0
  24. package/dist/src/tasks.d.ts +5 -0
  25. package/dist/src/tasks.js +62 -0
  26. package/dist/src/util.d.ts +4 -0
  27. package/dist/src/util.js +13 -0
  28. package/dist/superspec.js +4 -4
  29. package/package.json +3 -3
  30. package/templates/hooks/codex-hooks.json +0 -26
  31. package/templates/sidecar/discovery.md +9 -0
  32. package/templates/sidecar/test-contract.md +2 -1
  33. package/templates/workflow/prompts/code-reviewer.md +1 -1
  34. package/templates/workflow/prompts/executor.md +1 -1
  35. package/templates/workflow/prompts/test-engineer.md +1 -0
  36. package/templates/workflow/prompts/test-runner.md +3 -1
  37. package/templates/workflow/prompts/verifier.md +9 -9
  38. package/templates/workflow/skills/superspec-apply/SKILL.md +58 -40
  39. package/templates/workflow/skills/superspec-archive/SKILL.md +5 -13
  40. package/templates/workflow/skills/superspec-explore/SKILL.md +17 -18
  41. package/templates/workflow/skills/superspec-propose/SKILL.md +26 -22
  42. package/templates/workflow/skills/superspec-review/SKILL.md +17 -24
  43. package/dist/src/packet_measure.d.ts +0 -43
  44. package/dist/src/packet_measure.js +0 -382
  45. /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 });
@@ -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;
@@ -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 guard check-apply-ready --change "${change}" --format ${format}`;
197
+ return `superspec check check-apply-ready --change "${change}" --format ${format}`;
172
198
  if (gate === "review_complete")
173
- return `superspec guard check-review-complete --change "${change}" --format ${format}`;
199
+ return `superspec check check-review-complete --change "${change}" --format ${format}`;
174
200
  if (gate === "archive_ready")
175
- return `superspec guard check-archive-ready --change "${change}" --format ${format}`;
201
+ return `superspec check check-archive-ready --change "${change}" --format ${format}`;
176
202
  if (gate === "task_edit")
177
- return `superspec guard check-task-edit --change "${change}" --task-id "${taskId ?? ""}" --format ${format}`;
203
+ return `superspec check check-task-edit --change "${change}" --task-id "${taskId ?? ""}" --format ${format}`;
178
204
  if (gate === "task_complete")
179
- return `superspec guard check-task-complete --change "${change}" --task-id "${taskId ?? ""}" --format ${format}`;
205
+ return `superspec check check-task-complete --change "${change}" --task-id "${taskId ?? ""}" --format ${format}`;
180
206
  if (gate === "task_reopen")
181
- return `superspec guard check-task-reopen --change "${change}" --task-id "${taskId ?? ""}" --format ${format}`;
182
- return `superspec guard check-enter --change "${change}" --gate "${gate}" --format ${format}`;
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 [...ROLE_EVIDENCE_FIELDS, "review_round_id", "findings", "acknowledged_accepted_deviation_uids"];
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
- blockers.push(...write_scope_blockers(taskId, writeScope));
1117
- if (task && (task.attrs.tdd_required ?? "true").toLowerCase() === "false") {
1118
- blockers.push(reason("unsupported_executor_tdd_mode", `task ${taskId} has tdd_required:false and must stay on main-thread apply path`));
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
- pre_edit_evidence_refs: pre_edit_evidence_refs(ctx, taskId),
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
- blockers.push(...executorRefs.blockers, ...codeReviewRefs.blockers, ...greenRefs.blockers);
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 ((args.red_test_run_evidence_refs ?? []).length === 0 && (args.characterization_test_run_evidence_refs ?? []).length === 0) {
1300
- 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"));
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
- if ((args.red_test_run_evidence_refs ?? []).length > 0)
1304
- blockers.push(...redRefs.blockers, ...pre_edit_refs_bound_to_active_chain(ctx, activeChain, redRefs.refs, "red", taskId));
1305
- if ((args.characterization_test_run_evidence_refs ?? []).length > 0)
1306
- blockers.push(...characterizationRefs.blockers, ...pre_edit_refs_bound_to_active_chain(ctx, activeChain, characterizationRefs.refs, "characterization", taskId));
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
- "red_or_characterization_pre_edit_evidence_bound_to_active_chain",
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
- "green_test_run_same_chain_and_pinned_transcripts",
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 = [...APPLY_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";
@@ -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[];