@deksden-com/dd-flow-cli 0.9.0-beta.121 → 0.9.0-beta.123

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 (49) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/build-info.json +5 -5
  3. package/dist/cli/hook-ingress.js +4 -3
  4. package/dist/cli/native-hook-ingress.js +34 -2
  5. package/dist/cli/run-cli.js +3 -0
  6. package/dist/harness-runtime/bin/dd-agy.mjs +6 -3
  7. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +68 -6
  8. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +21 -5
  9. package/dist/harness-runtime/lib/dd-codex.mjs +5 -1
  10. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +60 -8
  11. package/dist/harness-runtime/lib/dd-zcode.mjs +40 -16
  12. package/dist/harness-runtime/lib/delegation-instructions.mjs +3 -3
  13. package/dist/harness-runtime/lib/lifecycle-command.d.mts +20 -0
  14. package/dist/harness-runtime/lib/lifecycle-command.mjs +218 -0
  15. package/dist/harness-runtime/lib/lifecycle-contract.d.mts +10 -0
  16. package/dist/harness-runtime/lib/lifecycle-contract.mjs +19 -0
  17. package/dist/harness-runtime/lib/managed-daemon.mjs +3 -2
  18. package/dist/harness-runtime/lib/model-observations.mjs +16 -9
  19. package/dist/harness-runtime/lib/native-hook-command.d.mts +2 -2
  20. package/dist/harness-runtime/lib/native-hook-command.mjs +36 -5
  21. package/dist/harness-runtime/lib/tool-observations.d.mts +1 -0
  22. package/dist/harness-runtime/lib/tool-observations.mjs +52 -11
  23. package/dist/runtime/context.js +1 -1
  24. package/dist/schemas/flow-run.schema.json +1 -0
  25. package/dist/services/codex-capacity.js +131 -12
  26. package/dist/services/codex-hook-delivery.js +6 -2
  27. package/dist/services/eval-snapshots.js +13 -8
  28. package/dist/services/external-work-launch.js +27 -9
  29. package/dist/services/hooks.js +51 -59
  30. package/dist/services/lifecycle-command.js +1 -383
  31. package/dist/services/lifecycle-contract.js +1 -30
  32. package/dist/services/lifecycle-invocations.js +34 -13
  33. package/dist/services/merge-server.js +26 -9
  34. package/dist/services/review-copy.js +21 -0
  35. package/dist/services/run-controller-adapter.js +25 -3
  36. package/dist/services/run-controller-recovery.js +9 -2
  37. package/dist/services/run-controller.js +59 -20
  38. package/dist/services/run-observations.js +7 -2
  39. package/dist/services/runs.js +60 -47
  40. package/dist/services/stage-report-renderer.js +44 -2
  41. package/dist/services/verification-matrix.js +345 -0
  42. package/dist/services/vnext-code-review.js +30 -11
  43. package/dist/services/vnext-code.js +31 -10
  44. package/dist/services/vnext-merge.js +30 -7
  45. package/dist/services/vnext-plan-review.js +28 -7
  46. package/dist/services/vnext-plan.js +16 -3
  47. package/dist/services/work-registry.js +15 -1
  48. package/dist/storage/database.js +43 -9
  49. package/package.json +4 -1
@@ -17,6 +17,7 @@ import { addWorkBatch, prepareWorkBatch, bindStageCoordinatorWork, finishStageCo
17
17
  import { withStageSettlement, prepareFlowRunStageAttachment } from "./runs.js";
18
18
  import { vnextStageDirectory } from "../domain/stage-catalog.js";
19
19
  import { acceptStageReport, prepareStageReport } from "./stage-report-renderer.js";
20
+ import { prepareRunVerificationMatrix, acceptVerificationMatrix, commitVerificationMatrixStart, resumeVerificationMatrix, verificationMatrixInstruction, latestRunVerificationMatrix, publishVerificationMatrix } from "./verification-matrix.js";
20
21
  import { applyExternalStageContext, prepareExternalStageContext } from "./stage-context.js";
21
22
  import { readFanoutDescriptor, writeFanoutDescriptor } from "./vnext-fanout.js";
22
23
  import { stageGraphScope } from "./stage-work-graph.js";
@@ -24,6 +25,7 @@ import { prepareVnextMergeRequest } from "./vnext-merge.js";
24
25
  import { validateVnextCodeHandoff } from "./vnext-plan.js";
25
26
  import { assertRunMutationAllowed } from "./run-recovery.js";
26
27
  import { prepareJsonFile, prepareOutputFile } from "../cli/input-preparation.js";
28
+ import { reviewerSourceInputs, reviewerSourceInstruction } from "./review-copy.js";
27
29
  const stage = "code-review";
28
30
  const stageDir = vnextStageDirectory(stage);
29
31
  const baselineAspects = ["goal_traceability", "coding_standards_design_review", "verification_evidence_review"];
@@ -50,18 +52,20 @@ export function prepareVnextCodeReviewStart(context, input) {
50
52
  const common = { projectRoot, project, run, home, root, rootWork, existingPrompt };
51
53
  if (prior && fs.existsSync(existingPrompt))
52
54
  return { ...common, resumed: true, prior, prompt: fs.readFileSync(existingPrompt, "utf8"), orchestration: readFanoutDescriptor(root) };
53
- prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@1" });
55
+ const attachment = prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@1" });
54
56
  const mode = effectiveMode(home, run);
55
57
  const cycle = reviewCycle(run);
56
58
  const codeReportSha256 = sha256File(path.join(home, "05-code", "stage-report.json"));
57
59
  const groups = mode.mode === "off" ? [] : reviewGroups(home);
58
- const batch = { works: groups.map((group, index) => ({ key: `code-review-${index + 1}`, task: reviewerTask(home, group), launch_policy: "fresh_agent_required", result_schema: "dd-flow/code-review-result@1", payload: { kind: "code-review", review_cycle: cycle, group, read_only: true } })) };
60
+ const reviewerInputs = mode.mode === "off" ? [] : reviewerSourceInputs(run.workspace_root, groups.flatMap(group => group.aspect_ids));
61
+ const batch = { works: groups.map((group, index) => ({ key: `code-review-${index + 1}`, task: `${reviewerTask(home, group)}\n${reviewerSourceInstruction(run.workspace_root, group.aspect_ids)}`, launch_policy: "fresh_agent_required", result_schema: "dd-flow/code-review-result@1", payload: { kind: "code-review", review_cycle: cycle, group, read_only: true } })) };
59
62
  const items = mode.mode === "off" ? [] : prepareWorkBatch(context, { parentWorkId: rootWork.work_id, value: batch });
60
63
  const template = mode.mode === "off" ? "" : read(path.join(run.workspace_root, ".memory-bank", "dd-flow", "vnext", "code-review.md"));
61
64
  const checks = mode.mode === "off" ? [] : acceptedCodeChecks(context, project.id, run.id).map(({ id, purpose }) => ({ id, purpose }));
62
65
  for (const name of ["review-context.json", ...(mode.mode === "off" ? ["decision.json"] : ["review-work-batch.json", "fanout.json"])])
63
66
  prepareOutputFile(path.join(root, name));
64
- return { ...common, resumed: false, mode, cycle, codeReportSha256, groups, batch, items, template, checks };
67
+ const matrix = mode.mode === "off" ? null : prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "input", stageAttempt: attachment.attempt });
68
+ return { ...common, resumed: false, mode, cycle, codeReportSha256, groups, batch, items, template, checks, matrix, reviewerInputs };
65
69
  }
66
70
  export function startVnextCodeReview(context, input, prepared = prepareVnextCodeReviewStart(context, input)) {
67
71
  const { projectRoot, project, run, root, rootWork, existingPrompt } = prepared;
@@ -69,6 +73,8 @@ export function startVnextCodeReview(context, input, prepared = prepareVnextCode
69
73
  assertStageStartHookEvent(context, { projectId: project.id, eventKey: input.hookEventId, runId: run.id, stage, projectRoot, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
70
74
  fs.mkdirSync(root, { recursive: true });
71
75
  if (prepared.resumed) {
76
+ if (effectiveMode(prepared.home, run).mode !== "off")
77
+ resumeVerificationMatrix(context, project.id, run.id, stage);
72
78
  const externalContext = applyExternalStageContext({ stageRoot: root, promptPath: existingPrompt, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
73
79
  const binding = bindStageCoordinatorWork(context, { workId: rootWork.work_id, hookEventId: input.hookEventId, stage, promptPath: existingPrompt, resultPath: path.join(root, "stage-report.json"), ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
74
80
  const prompt = fs.readFileSync(existingPrompt, "utf8");
@@ -76,6 +82,7 @@ export function startVnextCodeReview(context, input, prepared = prepareVnextCode
76
82
  return { ok: true, resumed: true, run_id: run.id, stage, stage_status: prepared.prior, id: binding.work_session_id, prompt_path: existingPrompt, worker_prompt_markdown: prompt, ...(externalContext ? { external_context: externalContext } : {}), ...(orchestration ? { orchestration } : {}), next: { finish_command: finishCommand(context, run.id, projectRoot, path.join(root, "decision.json")) } };
77
83
  }
78
84
  const { mode, cycle, groups, batch, items, template, checks, codeReportSha256 } = prepared;
85
+ prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@1" }, true);
79
86
  const aspectMapChecksums = mode.mode === "off" ? [] : fs.readdirSync(path.join(run.run_root, "03-plan"), { withFileTypes: true })
80
87
  .filter(entry => entry.isDirectory() && fs.existsSync(path.join(run.run_root, "03-plan", entry.name, "aspect-map.json")))
81
88
  .map(entry => ({ path: `03-plan/${entry.name}/aspect-map.json`, sha256: sha256File(path.join(run.run_root, "03-plan", entry.name, "aspect-map.json")) }))
@@ -83,25 +90,28 @@ export function startVnextCodeReview(context, input, prepared = prepareVnextCode
83
90
  freezeFlowRunReviewMode(context, { projectRoot, runId: run.id, review: "code", mode: mode.mode, source: mode.source, reason: mode.reason });
84
91
  const promptPath = path.join(root, "stage-prompt.md");
85
92
  const reviewContextPath = path.join(root, "review-context.json");
86
- fs.writeFileSync(reviewContextPath, `${JSON.stringify({ schema_id: "dd-flow/code-review-context@1", cycle, mode: mode.mode, mode_source: mode.source, mode_reason: mode.reason, ...(mode.mode !== "off" ? { read_only_input: { parent_work_id: rootWork.work_id, version: 3 } } : {}), code_report_sha256: codeReportSha256, review_batch_sha256: sha256Value(`${JSON.stringify(batch, null, 2)}\n`), aspect_map_checksums: aspectMapChecksums }, null, 2)}\n`);
93
+ fs.writeFileSync(reviewContextPath, `${JSON.stringify({ schema_id: "dd-flow/code-review-context@1", reviewer_inputs: prepared.reviewerInputs, cycle, mode: mode.mode, mode_source: mode.source, mode_reason: mode.reason, ...(mode.mode !== "off" ? { read_only_input: { parent_work_id: rootWork.work_id, version: 3 } } : {}), code_report_sha256: codeReportSha256, review_batch_sha256: sha256Value(`${JSON.stringify(batch, null, 2)}\n`), aspect_map_checksums: aspectMapChecksums }, null, 2)}\n`);
87
94
  if (mode.mode === "off") {
88
95
  fs.writeFileSync(path.join(root, "decision.json"), `${JSON.stringify(disabledReviewDecision(), null, 2)}\n`);
89
96
  const prompt = `<stage_identity>\n- RUN: ${run.id}\n- stage: code-review\n- mode: off\n</stage_identity>\n\nCODE-REVIEW is disabled by the frozen RUN configuration. Finish with: ${finishCommand(context, run.id, projectRoot, path.join(root, "decision.json"))}\n${stageLifecycleInstruction}\n`;
90
97
  fs.writeFileSync(promptPath, prompt);
91
98
  const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
92
99
  const binding = bindStageCoordinatorWork(context, { workId: rootWork.work_id, hookEventId: input.hookEventId, stage, promptPath, resultPath: path.join(root, "stage-report.json"), ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
93
- attachFlowRunStage(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@1" });
100
+ const attachmentInput = { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@1" };
101
+ attachFlowRunStage(context, attachmentInput, prepareFlowRunStageAttachment(context, attachmentInput));
94
102
  return { ok: true, run_id: run.id, stage, mode: mode.mode, mode_source: mode.source, id: binding.work_session_id, prompt_path: promptPath, worker_prompt_markdown: fs.readFileSync(promptPath, "utf8"), ...(externalContext ? { external_context: externalContext } : {}), next: { finish_command: finishCommand(context, run.id, projectRoot, path.join(root, "decision.json")) } };
95
103
  }
96
104
  const batchFile = path.join(root, "review-work-batch.json");
97
105
  fs.writeFileSync(batchFile, `${JSON.stringify(batch, null, 2)}\n`);
98
- const prompt = `${orchestratorPrompt(context, { projectRoot, run, root, rootWork, mode: mode.mode, groups, template, checks })}\n${stageLifecycleInstruction}\n`;
106
+ const prompt = `${orchestratorPrompt(context, { projectRoot, run, root, rootWork, mode: mode.mode, groups, template, checks })}\n${stageLifecycleInstruction}\n${verificationMatrixInstruction(prepared.matrix?.metadata ?? null, prepared.home, "reviewing whether current evidence proves the accepted obligations")}\n`;
99
107
  fs.writeFileSync(promptPath, prompt);
100
108
  const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
101
109
  const binding = bindStageCoordinatorWork(context, { workId: rootWork.work_id, hookEventId: input.hookEventId, stage, promptPath, resultPath: path.join(root, "stage-report.json"), ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
102
110
  const registered = addWorkBatch(context, { parentWorkId: rootWork.work_id, value: batch, prepared: items, stage, cycle });
103
111
  const orchestration = writeFanoutDescriptor(root, { stage, parent_work_id: rootWork.work_id, dispatch: "none", capacity_required: true, scope: stageGraphScope(context, project.id, run.id, stage) });
104
- attachFlowRunStage(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@1", inputSha256: sha256Value(JSON.stringify({ batch: sha256File(batchFile), aspect_maps: aspectMapChecksums })) });
112
+ const attachmentInput = { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@1", inputSha256: sha256Value(JSON.stringify({ batch: sha256File(batchFile), aspect_maps: aspectMapChecksums })) };
113
+ const attachment = prepareFlowRunStageAttachment(context, attachmentInput);
114
+ commitVerificationMatrixStart(context, prepared.matrix, () => attachFlowRunStage(context, attachmentInput, attachment));
105
115
  appendFlowRunTimelineEvent(context, project.id, run.id, { type: "code_review_started", work_id: rootWork.work_id, mode: mode.mode, mode_source: mode.source, groups, registered });
106
116
  return { ok: true, run_id: run.id, stage, mode: mode.mode, mode_source: mode.source, id: binding.work_session_id, prompt_path: promptPath, worker_prompt_markdown: fs.readFileSync(promptPath, "utf8"), ...(externalContext ? { external_context: externalContext } : {}), orchestration, review: { groups, works: registered }, next: { finish_command: finishCommand(context, run.id, projectRoot, path.join(root, "decision.json")) } };
107
117
  }
@@ -208,7 +218,8 @@ export async function finishVnextCodeReview(context, input) {
208
218
  fs.writeFileSync(temporary, `${JSON.stringify({ ...reviewContext, accepted_decision: decision }, null, 2)}\n`);
209
219
  fs.renameSync(temporary, contextFile);
210
220
  const repair = addVnextCodeReviewRepair(context, { projectRoot, runId: run.id, findingIds: fixIds, checkRefsByFinding, decisionReasonsByFinding: Object.fromEntries(canonical.decision.findings.map((item) => [item.finding_ref, item.reason])), objective: `Resolve accepted CODE-REVIEW findings: ${fixIds.join(", ")}` });
211
- return { ok: true, run_id: run.id, stage, outcome: "repair_required", repair, instruction: "The repair Work is already registered: do not invoke its start_command in this coordinator Session. End this Turn so the controller can launch it in a fresh child Session. When it completes, invoke next.finish_command; this review cycle uses the decision already accepted by the runtime.", next: { finish_command: finishCommand(context, run.id, projectRoot, decisionFile) } };
221
+ const retained = latestRunVerificationMatrix(context, project.id, run.id, stage, "input");
222
+ return { ok: true, run_id: run.id, stage, outcome: "repair_required", repair, ...(retained ? { verification_matrix: retained.metadata } : {}), instruction: `The repair Work is already registered: do not invoke its start_command in this coordinator Session. End this Turn so the controller can launch it in a fresh child Session. When it completes, invoke next.finish_command; this review cycle uses the decision already accepted by the runtime.\n${verificationMatrixInstruction(retained?.metadata ?? null, home, "the accepted review decision and repair")}`, next: { finish_command: finishCommand(context, run.id, projectRoot, decisionFile) } };
212
223
  }
213
224
  repairs = reviewRepairWorks(context, project.id, run.id, root, cycle);
214
225
  const incompleteRepairs = repairs.filter((work) => work.status !== "completed");
@@ -224,16 +235,21 @@ export async function finishVnextCodeReview(context, input) {
224
235
  appendFlowRunTimelineEvent(context, project.id, run.id, { type: "check_retry_requested", check_receipt_id: input.retryCheckId, stage, reason: input.retryReason ?? "operator retry" });
225
236
  }
226
237
  const receipts = await runCodeChecks(context, { projectId: project.id, runId: run.id, runHome: home, workspaceRoot: run.workspace_root, artifactDir: stageDir, scope: "aggregate", checks: finalChecks, ...(input.retryCheckId ? { retryReceiptId: input.retryCheckId, retryReason: input.retryReason } : {}), ...(input.progress ? { progress: input.progress } : {}) });
238
+ const matrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "output", receipts, semanticFile: decisionFile });
227
239
  const failed = receipts.filter((receipt) => receipt.status !== "passed");
228
- if (failed.length)
240
+ if (failed.length) {
241
+ context.db.writeTransaction(() => acceptVerificationMatrix(context, matrix));
242
+ publishVerificationMatrix(context, matrix);
229
243
  throw new AppError("code_review_gate_failed", "CODE-REVIEW repair changed the project but the aggregate gate failed", 2, {
230
244
  failures: failed,
231
245
  outcome: "repair_required",
232
246
  retry_after_workspace_change: true,
233
247
  workspace_fingerprint: failed[0].workspace_fingerprint,
234
248
  continuation: registerAggregateCodeRepair(context, { projectRoot, runId: run.id, stage, failures: failed, checks: finalChecks, generation }),
235
- instruction: "Repair Work is already registered. End this Turn so the controller can launch a fresh child; after it finishes, repeat finish with the already accepted review decision. Do not redo review."
249
+ ...(matrix ? { verification_matrix: matrix.metadata } : {}),
250
+ instruction: `Repair Work is already registered. End this Turn so the controller can launch a fresh child; after it finishes, repeat finish with the already accepted review decision. Do not redo review.\n${verificationMatrixInstruction(matrix?.metadata ?? null, home, "the current aggregate failures and repair")}`
236
251
  });
252
+ }
237
253
  const stopTarget = executionStopTarget(run);
238
254
  const nextAction = stopTarget === "merge_completed" ? "start_merge" : "code_review_completed";
239
255
  const now = context.now();
@@ -245,6 +261,8 @@ export async function finishVnextCodeReview(context, input) {
245
261
  let mergeRequest = null;
246
262
  assertRunMutationAllowed(context, project.id, run.id, generation);
247
263
  const prepareMerge = nextAction === "start_merge" ? prepareVnextMergeRequest(context, { projectRoot, runId: run.id, acceptedPaths }) : null;
264
+ if (matrix)
265
+ Object.assign(report.semantic, { verification_matrix: matrix.metadata });
248
266
  const preparedReport = prepareStageReport(report);
249
267
  const completionInput = { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html", dataSha256: preparedReport.sha256, git: report.mechanical.git };
250
268
  withStageSettlement(context, () => {
@@ -256,6 +274,7 @@ export async function finishVnextCodeReview(context, input) {
256
274
  }
257
275
  mergeRequest = prepareMerge?.() ?? null;
258
276
  const preparedCompletion = prepareFlowRunStageCompletion(context, completionInput);
277
+ acceptVerificationMatrix(context, matrix);
259
278
  acceptStageReport(context, { projectId: project.id, runId: run.id, stage, root, prepared: preparedReport });
260
279
  completeFlowRunStage(context, completionInput, preparedCompletion);
261
280
  appendFlowRunTimelineEvent(context, project.id, run.id, { type: "code_review_completed", work_id: rootWork.work_id, outcome });
@@ -335,7 +354,7 @@ export function assertCodeReviewCoverage(groups, reviewers) {
335
354
  throw new AppError("reviewer_coverage_invalid", "CODE-REVIEW requires every expected reviewer group", 2, { missing_groups: missing });
336
355
  }
337
356
  export function reviewerTask(home, group) { return `Read-only independent CODE review for ${group.key}. Assess every assigned aspect exactly once: ${group.aspect_ids.join(", ")}. Read the accepted PLAN, ${path.join(home, "05-code", "stage-report.json")}, and any files needed under the bounded CODE evidence root ${path.join(home, "05-code")}. Verify that universal and exclusive constraints (only, any, all, never, whole) and stated exceptions were not narrowed. For every changed mutation guarded by membership, ownership, authorization or parent lifecycle state, trace the decision to the write boundary: the predicate must remain in the write statement or the guard and write must share one explicit transaction with the needed lock. A separate earlier read is not proof of current authority or lifecycle state; report a material finding when this invariant is broken. Evidence closes a claim only when it exercises the named failure mechanism; a sequential negative test does not prove a concurrency race, and proof limits cannot waive an accepted obligation. Report only material, evidenced defects: a violated obligation or rule, direct evidence, impact, and minimum required outcome. Do not report taste, cosmetics, or untargeted refactoring. Use local finding ids FIND-001, FIND-002, and so on; dd-flow adds the Work-qualified canonical reference. Return dd-flow/code-review-result@1.`; }
338
- function orchestratorPrompt(context, input) { const { template, checks } = input; const decision = path.join(input.root, "decision.json"); return ["<stage_identity>", `- RUN: ${input.run.id}`, `- root Work: ${input.rootWork.work_id}`, `- stage: ${stage}`, `- mode: ${input.mode}`, "</stage_identity>", "", "<trusted_runtime_context>", `- project root: ${input.projectRoot}`, `- immutable write workspace: ${input.run.workspace_root}`, `- stage workspace: ${input.root}`, `- bounded CODE evidence root: ${path.join(input.run.run_root, "05-code")}`, "CODE is already semantically verified and all declared checks passed. Do not redo CODE verification; conduct independent quality review.", "</trusted_runtime_context>", "", "<review_groups>", ...input.groups.map((group) => `- ${group.key}: ${group.aspect_ids.join(", ")}`), "</review_groups>", "", "<execution_commands>", "Do not choose a provider delegation tool from this stage prompt. At the Work-graph boundary, return immediately; the shared controller supplies the selected adapter's native tool, exact parameters, child task, and wait contract. Each reviewer Work must run in one fresh child session. The coordinator must never invoke a Work start_command itself: a child runs its exact start_command after native delegation. Reviewers are read-only and must not create subagents. Do not interrupt a quiet worker.", "Ready reviewer Works: " + runtimeCommand(context, ["work", "ls", "--run", input.run.id, "--ready", "--project-root", input.projectRoot, "--json"]), `Write the decision to this exact absolute path, not a relative look-alike: ${decision}`, `Finish only after every reviewer Work settles and that exact file exists: ${finishCommand(context, input.run.id, input.projectRoot, decision)}`, "Reviewer results use local FIND-NNN ids. Classify them by the canonical WRK-.../FIND-NNN finding_ref returned by dd-flow. Fix P0/P1. Fix bounded safe P2 by default; defer only a legitimate P2 with a named DEF. P3 is an observation or a reasoned rejection, not automatic repair/DEF. For every disposition fix, check_refs is mandatory: choose the one or more causal checks from the accepted list below that the repair must rerun. Do not guess a CHK id or copy every check. The first successful Finish freezes this decision and creates one repair Work when needed. Do not invoke that Work's start_command from the coordinator Session: end this Turn so the controller can launch a fresh child, then call the returned next.finish_command after it completes. Internal invocation IDs are runtime-owned and must not be copied or reused. Review is not repeated.", "If the aggregate gate fails with repair_required, its repair Work is already registered with all causal checks. End this Turn so the controller launches a fresh child. Do not copy receipt IDs, create another repair, invoke its start_command in the coordinator, or edit invisibly in the root orchestrator.", `Accepted repair checks: ${JSON.stringify(checks)}`, "```json", JSON.stringify({ schema_id: "dd-flow/code-review-decision@3", summary: "Evidence-backed conclusion.", findings: [{ finding_ref: "WRK-001-review/FIND-001", disposition: "fix | defer | reject | duplicate", reason: "Why this classification is correct.", check_refs: ["CHK-CAUSAL-CHECK only when disposition is fix"], def_id: "DEF-0001 only for an allowed P2 deferral", duplicate_of: "canonical finding_ref only for duplicate" }] }, null, 2), "```", "</execution_commands>", "", "<stage_instructions>", template, "</stage_instructions>", ""].join("\n"); }
357
+ function orchestratorPrompt(context, input) { const { template, checks } = input; const decision = path.join(input.root, "decision.json"); return ["<stage_identity>", `- RUN: ${input.run.id}`, `- root Work: ${input.rootWork.work_id}`, `- stage: ${stage}`, `- mode: ${input.mode}`, "</stage_identity>", "", "<trusted_runtime_context>", `- project root: ${input.projectRoot}`, `- immutable write workspace: ${input.run.workspace_root}`, `- stage workspace: ${input.root}`, `- bounded CODE evidence root: ${path.join(input.run.run_root, "05-code")}`, "CODE passed its declared checks; each check proves only its named contour and proof limits, not complete semantic acceptance. Conduct independent quality review and do not manually rerun every declared check.", "</trusted_runtime_context>", "", "<review_groups>", ...input.groups.map((group) => `- ${group.key}: ${group.aspect_ids.join(", ")}`), "</review_groups>", "", "<execution_commands>", "Do not choose a provider delegation tool from this stage prompt. At the Work-graph boundary, return immediately; the shared controller supplies the selected adapter's native tool, exact parameters, child task, and wait contract. Each reviewer Work must run in one fresh child session. The coordinator must never invoke a Work start_command itself: a child runs its exact start_command after native delegation. Reviewers are read-only and must not create subagents. Do not interrupt a quiet worker.", "Ready reviewer Works: " + runtimeCommand(context, ["work", "ls", "--run", input.run.id, "--ready", "--project-root", input.projectRoot, "--json"]), `Write the decision to this exact absolute path, not a relative look-alike: ${decision}`, `Finish only after every reviewer Work settles and that exact file exists: ${finishCommand(context, input.run.id, input.projectRoot, decision)}`, "Reviewer results use local FIND-NNN ids. Classify them by the canonical WRK-.../FIND-NNN finding_ref returned by dd-flow. Fix P0/P1. Fix bounded safe P2 by default; defer only a legitimate P2 with a named DEF. P3 is an observation or a reasoned rejection, not automatic repair/DEF. For every disposition fix, check_refs is mandatory: choose the one or more causal checks from the accepted list below that the repair must rerun. Do not guess a CHK id or copy every check. The first successful Finish freezes this decision and creates one repair Work when needed. Do not invoke that Work's start_command from the coordinator Session: end this Turn so the controller can launch a fresh child, then call the returned next.finish_command after it completes. Internal invocation IDs are runtime-owned and must not be copied or reused. Review is not repeated.", "If the aggregate gate fails with repair_required, its repair Work is already registered with all causal checks. End this Turn so the controller launches a fresh child. Do not copy receipt IDs, create another repair, invoke its start_command in the coordinator, or edit invisibly in the root orchestrator.", `Accepted repair checks: ${JSON.stringify(checks)}`, "```json", JSON.stringify({ schema_id: "dd-flow/code-review-decision@3", summary: "Evidence-backed conclusion.", findings: [{ finding_ref: "WRK-001-review/FIND-001", disposition: "fix | defer | reject | duplicate", reason: "Why this classification is correct.", check_refs: ["CHK-CAUSAL-CHECK only when disposition is fix"], def_id: "DEF-0001 only for an allowed P2 deferral", duplicate_of: "canonical finding_ref only for duplicate" }] }, null, 2), "```", "</execution_commands>", "", "<stage_instructions>", template, "</stage_instructions>", ""].join("\n"); }
339
358
  function canonicalReviewEvidence(decision, reviewers) {
340
359
  const known = new Set(canonicalCodeFindings(reviewers).map((item) => item.finding_ref));
341
360
  const items = new Map(decision.findings.map((item) => [item.finding_ref, item]));
@@ -25,6 +25,7 @@ import { applyExternalStageContext, prepareExternalStageContext } from "./stage-
25
25
  import { readFanoutDescriptor, writeFanoutDescriptor } from "./vnext-fanout.js";
26
26
  import { assertRunMutationAllowed } from "./run-recovery.js";
27
27
  import { prepareJsonSchema, prepareOutputFile } from "../cli/input-preparation.js";
28
+ import { prepareRunVerificationMatrix, acceptVerificationMatrix, commitVerificationMatrixStart, resumeVerificationMatrix, verificationMatrixInstruction, publishVerificationMatrix, latestRunVerificationMatrix } from "./verification-matrix.js";
28
29
  const stage = "code";
29
30
  const stageDir = vnextStageDirectory("code");
30
31
  /** A controller prepares slow, deterministic project tooling before it asks an
@@ -63,8 +64,7 @@ export function prepareVnextCodeStart(context, input) {
63
64
  if (existingStage && fs.existsSync(existingPrompt)) {
64
65
  return { ...common, resumed: true, existingStage, prompt: fs.readFileSync(existingPrompt, "utf8"), orchestration: readFanoutDescriptor(existingRoot) };
65
66
  }
66
- if (!sourceRepairAttempt)
67
- prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@1" });
67
+ const attachment = sourceRepairAttempt ? null : prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@1" });
68
68
  const batch = path.join(home, "03-plan", "code-work-batch.json");
69
69
  const handoffFailures = validateVnextCodeHandoff(context, {
70
70
  projectRoot,
@@ -86,23 +86,27 @@ export function prepareVnextCodeStart(context, input) {
86
86
  validateWorkBatchFile(batch);
87
87
  const template = fs.readFileSync(path.join(run.workspace_root, ".memory-bank", "dd-flow", "vnext", "code.md"), "utf8");
88
88
  const variables = getFlowRunVariables(context, { projectRoot, runId: run.id });
89
- return { ...common, resumed: false, template, capacity: variables.variables?.["runtime.subagent.available_slots"] };
89
+ const matrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "input", completeness: "declared", ...(attachment ? { stageAttempt: attachment.attempt } : {}) });
90
+ return { ...common, resumed: false, template, capacity: variables.variables?.["runtime.subagent.available_slots"], matrix };
90
91
  }
91
92
  export async function startVnextCode(context, input, prepared = prepareVnextCodeStart(context, input)) {
92
93
  const { projectRoot, project, run, root, promptPath, rootWork, sourceRepairAttempt } = prepared;
93
94
  assertRunMutationAllowed(context, project.id, run.id);
94
95
  assertStageStartHookEvent(context, { projectId: project.id, eventKey: input.hookEventId, runId: run.id, stage, projectRoot, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
95
96
  if (prepared.resumed) {
97
+ resumeVerificationMatrix(context, project.id, run.id, stage);
96
98
  const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
97
99
  bindStageCoordinatorWork(context, { workId: rootWork.work_id, hookEventId: input.hookEventId, stage, promptPath, resultPath: path.join(root, "stage-report.json"), ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
98
100
  const orchestration = prepared.orchestration ?? writeFanoutDescriptor(root, { stage, parent_work_id: rootWork.work_id, dispatch: "none", capacity_required: true, scope: stageGraphScope(context, project.id, run.id, stage) });
99
101
  return { ok: true, resumed: true, run_id: run.id, stage, stage_status: prepared.existingStage, prompt_path: promptPath, worker_prompt_markdown: fs.readFileSync(promptPath, "utf8"), ...(externalContext ? { external_context: externalContext } : {}), orchestration, graph: codeWorkGraph(context, project.id, run.id), next: { refresh_graph_command: runtimeCommand(context, ["work", "ls", "--run", run.id, "--ready", "--project-root", projectRoot, "--json"]), finish_command: finishCommand(context, input.runId, projectRoot, path.join(root, "code-verification.json")) } };
100
102
  }
103
+ if (!sourceRepairAttempt)
104
+ prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@1" }, true);
101
105
  fs.mkdirSync(root, { recursive: true });
102
106
  const readiness = await ensureCodeWorkspaceReady(context, project.id, run, root, [], input.progress);
103
107
  // Graph rendering may issue managed child commands; it belongs to execution.
104
108
  const graph = codeWorkGraph(context, project.id, run.id);
105
- const prompt = `${coordinatorPrompt(context, { projectRoot, run, root, rootWork, readiness, template: prepared.template, capacity: prepared.capacity, graph })}\n${stageLifecycleInstruction}\n`;
109
+ const prompt = `${coordinatorPrompt(context, { projectRoot, run, root, rootWork, readiness, template: prepared.template, capacity: prepared.capacity, graph })}\n${stageLifecycleInstruction}\n${verificationMatrixInstruction(prepared.matrix?.metadata ?? null, prepared.home, "implementation and code-verification.json")}\n`;
106
110
  fs.writeFileSync(promptPath, prompt);
107
111
  const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
108
112
  const binding = bindStageCoordinatorWork(context, {
@@ -114,15 +118,19 @@ export async function startVnextCode(context, input, prepared = prepareVnextCode
114
118
  ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {})
115
119
  });
116
120
  if (!sourceRepairAttempt) {
117
- attachFlowRunStage(context, {
121
+ const attachmentInput = {
118
122
  projectRoot,
119
123
  runId: input.runId,
120
124
  stage,
121
125
  dir: stageDir,
122
126
  status: "running",
123
127
  dataSchemaId: "dd-flow/stage-report@1"
124
- });
128
+ };
129
+ const attachment = prepareFlowRunStageAttachment(context, attachmentInput);
130
+ commitVerificationMatrixStart(context, prepared.matrix, () => attachFlowRunStage(context, attachmentInput, attachment));
125
131
  }
132
+ else
133
+ commitVerificationMatrixStart(context, prepared.matrix, () => undefined);
126
134
  const orchestration = writeFanoutDescriptor(root, { stage, parent_work_id: rootWork.work_id, dispatch: "none", capacity_required: true, scope: stageGraphScope(context, project.id, run.id, stage) });
127
135
  appendFlowRunTimelineEvent(context, project.id, input.runId, { type: "code_started", work_id: rootWork.work_id, graph });
128
136
  return {
@@ -180,8 +188,10 @@ export async function finishVnextCode(context, input) {
180
188
  // Reject a malformed semantic receipt before running the expensive gate.
181
189
  const verification = input.verification ?? verificationForFinish(context, { file: input.verificationFile, projectRoot: run.workspace_root, runId: run.id, runRoot: home });
182
190
  validateSchema({ schemaName: "code-verification", file: input.verificationFile, data: verification, projectRoot: run.workspace_root, ddFlowHome: context.ddFlowHome, runId: run.id, runRoot: home });
191
+ const retainedMatrix = latestRunVerificationMatrix(context, project.id, run.id, stage);
192
+ const retainedMatrixInstruction = verificationMatrixInstruction(retainedMatrix?.metadata ?? null, home, "the current semantic verification and repair");
183
193
  if (verification.verdict === "blocked") {
184
- return { ok: true, run_id: run.id, stage, outcome: "blocked", verification, instruction: "CODE remains running. Resolve the stated external or user-input blocker in this same coordinator session, update code-verification.json, then invoke this same stage finish command again." };
194
+ return { ok: true, run_id: run.id, stage, outcome: "blocked", verification, ...(retainedMatrix ? { verification_matrix: retainedMatrix.metadata } : {}), instruction: `CODE remains running. Resolve the stated external or user-input blocker in this same coordinator session, update code-verification.json, then invoke this same stage finish command again.\n${retainedMatrixInstruction}` };
185
195
  }
186
196
  if (verification.verdict === "passed" && verification.unresolved.length > 0) {
187
197
  throw new AppError("verification_contradictory", "A passed CODE verification cannot contain unresolved obligations", 2, { unresolved: verification.unresolved });
@@ -196,7 +206,7 @@ export async function finishVnextCode(context, input) {
196
206
  verification,
197
207
  objective: verification.summary
198
208
  });
199
- return { ok: true, run_id: run.id, stage, outcome: "repair_required", verification, repair, instruction: "The semantic verification is not accepted. The repair Work is already registered: do not invoke its start_command in this coordinator Session. End this Turn so the controller can launch it in a fresh child Session. After that Work completes, update code-verification.json and invoke this same stage finish command again." };
209
+ return { ok: true, run_id: run.id, stage, outcome: "repair_required", verification, repair, ...(retainedMatrix ? { verification_matrix: retainedMatrix.metadata } : {}), instruction: `The semantic verification is not accepted. The repair Work is already registered: do not invoke its start_command in this coordinator Session. End this Turn so the controller can launch it in a fresh child Session. After that Work completes, update code-verification.json and invoke this same stage finish command again.\n${retainedMatrixInstruction}` };
200
210
  }
201
211
  const checks = finalCodeCheckDeclarations(run.workspace_root, works.flatMap((work) => packet(work)?.checks ?? []));
202
212
  if (input.retryCheckId) {
@@ -209,7 +219,11 @@ export async function finishVnextCode(context, input) {
209
219
  ...(input.progress ? { progress: input.progress } : {})
210
220
  });
211
221
  const failed = receipts.filter((receipt) => receipt.status !== "passed");
222
+ const matrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "output", receipts, semanticFile: input.verificationFile });
223
+ const matrixInstruction = matrix ? verificationMatrixInstruction(matrix.metadata, home, "semantic verification in code-verification.json") : "";
212
224
  if (failed.length) {
225
+ context.db.writeTransaction(() => acceptVerificationMatrix(context, matrix));
226
+ publishVerificationMatrix(context, matrix);
213
227
  const continuation = registerAggregateCodeRepair(context, { projectRoot, runId: run.id, stage, failures: failed, checks, generation });
214
228
  throw new AppError("code_gate_failed", "CODE remains running because the aggregate project gate failed", 2, {
215
229
  failures: failed,
@@ -217,15 +231,19 @@ export async function finishVnextCode(context, input) {
217
231
  retry_after_workspace_change: true,
218
232
  workspace_fingerprint: failed[0].workspace_fingerprint,
219
233
  continuation,
220
- instruction: "Repair Work is already registered. End this coordinator Turn; the controller will launch a fresh child. After it completes, update semantic verification and finish the Stage."
234
+ ...(matrix ? { verification_matrix: matrix.metadata, verification_matrix_instruction: matrixInstruction } : {}),
235
+ instruction: `Repair Work is already registered. End this coordinator Turn; the controller will launch a fresh child. After it completes, update semantic verification and finish the Stage.\n${matrixInstruction}`
221
236
  });
222
237
  }
223
238
  const finalReceipts = receipts;
224
239
  if (verification.verdict === "needs_checks") {
240
+ context.db.writeTransaction(() => acceptVerificationMatrix(context, matrix));
241
+ publishVerificationMatrix(context, matrix);
225
242
  return { ok: true, run_id: run.id, stage, outcome: "verification_required", verification,
243
+ ...(matrix ? { verification_matrix: matrix.metadata, verification_matrix_instruction: matrixInstruction } : {}),
226
244
  receipts: finalReceipts.map(receipt => ({ id: receipt.id, receipt_path: receipt.receipt_path, input_hash: receipt.input_hash, status: receipt.status })),
227
245
  next: { kind: "verification_required", finish_command: finishCommand(context, run.id, projectRoot, input.verificationFile) },
228
- instruction: "Aggregate receipts are available. Keep CODE running. Inspect them, update code-verification.json to passed or needs_repair, then invoke only the issued successor finish_command. A material document change belongs to the registered fresh repair Work, not this coordinator Session." };
246
+ instruction: `Aggregate receipts are available. Keep CODE running. Inspect them, update code-verification.json to passed or needs_repair, then invoke only the issued successor finish_command. A material document change belongs to the registered fresh repair Work, not this coordinator Session.\n${matrixInstruction}` };
229
247
  }
230
248
  const projectedVerification = verificationProjection(works, finalReceipts, { historicalReceipts: finalReceipts, runId: run.id, runHome: home });
231
249
  const unresolvedAcceptance = (projectedVerification.acceptance ?? []).filter((item) => item.status === "unresolved");
@@ -278,6 +296,8 @@ export async function finishVnextCode(context, input) {
278
296
  let mergeRequest = null;
279
297
  assertRunMutationAllowed(context, project.id, run.id, generation);
280
298
  const prepareMerge = next === "start_merge" ? prepareVnextMergeRequest(context, { projectRoot, runId: run.id, acceptedPaths: changedPaths }) : null;
299
+ if (matrix)
300
+ Object.assign(report.semantic, { verification_matrix: matrix.metadata });
281
301
  const preparedReport = prepareStageReport(report);
282
302
  const completionInput = { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html", dataSha256: preparedReport.sha256, git: report.mechanical.git };
283
303
  withStageSettlement(context, () => {
@@ -288,6 +308,7 @@ export async function finishVnextCode(context, input) {
288
308
  }
289
309
  mergeRequest = prepareMerge?.() ?? null;
290
310
  const preparedCompletion = prepareFlowRunStageCompletion(context, completionInput);
311
+ acceptVerificationMatrix(context, matrix);
291
312
  acceptStageReport(context, { projectId: project.id, runId: run.id, stage, root, prepared: preparedReport });
292
313
  completeFlowRunStage(context, completionInput, preparedCompletion);
293
314
  const rootWork = requireRootWork(context, project.id, run.id);
@@ -23,6 +23,7 @@ import { ensureLaneWorkspace, prepareLaneWorkspace, heartbeatLaneLock, releaseLa
23
23
  import { assertRunMutationAllowed } from "./run-recovery.js";
24
24
  import { assertStageStartHookEvent } from "./hooks.js";
25
25
  import { prepareJsonSchema, prepareOutputFile } from "../cli/input-preparation.js";
26
+ import { prepareRunVerificationMatrix, acceptVerificationMatrix, commitVerificationMatrixStart, resumeVerificationMatrix, verificationMatrixInstruction, latestRunVerificationMatrix, publishVerificationMatrix } from "./verification-matrix.js";
26
27
  const stage = "merge";
27
28
  const stageDir = vnextStageDirectory(stage);
28
29
  export function isVnextMergeRun(context, input) {
@@ -133,8 +134,7 @@ export function prepareVnextMergeStart(context, input) {
133
134
  if (!work || work.project_id !== project.id || work.run_id !== run.id || !["created", "running"].includes(work.status))
134
135
  throw new AppError("runtime_artifact_invalid", "MERGE executor Work is missing or not startable", 1, { work_id: request.executor_work_id });
135
136
  const index = JSON.parse(run.index_json);
136
- if (!index.stage_runs?.some(entry => entry.stage === stage))
137
- prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@2" });
137
+ const attachment = ["active", "action_required"].includes(request.status) && index.stage_runs?.some(entry => entry.stage === stage) ? null : prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@2" });
138
138
  prepareLaneWorkspace(context, { projectRoot, lane: "merge", workspacePath: request.target_workspace, branch: request.target_branch });
139
139
  const checks = effectiveMergeChecks(run, request), settings = executionSettings(run);
140
140
  const existingPrompt = ["active", "action_required"].includes(request.status) && fs.existsSync(promptPath) ? fs.readFileSync(promptPath, "utf8") : null;
@@ -142,15 +142,21 @@ export function prepareVnextMergeStart(context, input) {
142
142
  prepareOutputFile(promptPath);
143
143
  if (input.externalContext)
144
144
  prepareExternalStageContext({ stageRoot: root, loaded: input.externalContext });
145
- return { ...common, terminal: false, root, workRoot, resultPath, promptPath, checks, settings, existingPrompt };
145
+ const matrix = existingPrompt !== null ? latestRunVerificationMatrix(context, project.id, run.id, stage, "input") : prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "input", ...(attachment ? { stageAttempt: attachment.attempt } : {}) });
146
+ return { ...common, terminal: false, root, workRoot, resultPath, promptPath, checks, settings, existingPrompt, matrix };
146
147
  }
147
148
  export async function startVnextMerge(context, input, prepared = prepareVnextMergeStart(context, input)) {
148
149
  const { projectRoot, project, run, request, generation } = prepared;
149
150
  if (prepared.terminal)
150
151
  return requestView(context, request);
151
152
  const { root, workRoot, resultPath, promptPath, checks, settings, existingPrompt } = prepared;
153
+ if (existingPrompt !== null)
154
+ resumeVerificationMatrix(context, project.id, run.id, stage);
152
155
  assertRunMutationAllowed(context, project.id, run.id, generation);
153
156
  assertStageStartHookEvent(context, { projectId: project.id, eventKey: input.hookEventId, runId: run.id, stage, projectRoot, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
157
+ const alreadyAttached = ["active", "action_required"].includes(request.status) && (JSON.parse(requireRun(context, project.id, run.id).index_json).stage_runs ?? []).some((item) => item.stage === stage);
158
+ if (!alreadyAttached)
159
+ prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@2" }, true);
154
160
  fs.mkdirSync(workRoot, { recursive: true });
155
161
  ensureLaneWorkspace(context, { projectRoot, lane: "merge", workspacePath: request.target_workspace, branch: request.target_branch });
156
162
  const lane = await waitAcquireLaneLock(context, { projectRoot, lane: "merge", workerId: request.executor_work_id, workspacePath: request.target_workspace, timeoutSeconds: 3600, pollIntervalSeconds: 15, ttlSeconds: 7200, reason: `MERGE ${request.merge_request_id}`, ...(input.progress ? { progress: input.progress } : {}) });
@@ -163,11 +169,13 @@ export async function startVnextMerge(context, input, prepared = prepareVnextMer
163
169
  const binding = pendingWork?.status === "created"
164
170
  ? startStageCoordinatorWork(context, { workId: request.executor_work_id, hookEventId: input.hookEventId, stage, projectRoot, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) })
165
171
  : bindStageCoordinatorWork(context, { workId: request.executor_work_id, hookEventId: input.hookEventId, stage, promptPath, resultPath, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
166
- const prompt = existingPrompt ?? `${mergePrompt(context, { run, request, resultPath, projectRoot }, checks, settings)}\n${stageLifecycleInstruction}\n`;
172
+ const prompt = existingPrompt ?? `${mergePrompt(context, { run, request, resultPath, projectRoot }, checks, settings)}\n${stageLifecycleInstruction}\n${verificationMatrixInstruction(prepared.matrix?.metadata ?? null, run.run_root, "MERGE source/target verification and residual-risk assessment")}\n`;
167
173
  if (existingPrompt === null)
168
174
  fs.writeFileSync(promptPath, prompt);
169
175
  const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
170
- ensureMergeStageAttached(context, run, projectRoot);
176
+ const attachmentInput = { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@2" };
177
+ const attachment = alreadyAttached ? null : prepareFlowRunStageAttachment(context, attachmentInput);
178
+ commitVerificationMatrixStart(context, prepared.matrix, () => attachment ? attachFlowRunStage(context, attachmentInput, attachment) : undefined);
171
179
  return startPacket(context, run, request, binding, promptPath, resultPath, prompt, projectRoot, checks, externalContext);
172
180
  }
173
181
  const current = requireRequest(context, request.merge_request_id);
@@ -182,11 +190,13 @@ export async function startVnextMerge(context, input, prepared = prepareVnextMer
182
190
  if (claimed.changes !== 1 && requireRequest(context, current.merge_request_id).status !== "active")
183
191
  throw new AppError("merge_claim_conflict", "MERGE request could not acquire the integration lane", 1, { merge_request_id: current.merge_request_id });
184
192
  const started = startStageCoordinatorWork(context, { workId: current.executor_work_id, hookEventId: input.hookEventId, stage, projectRoot, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
185
- const prompt = `${mergePrompt(context, { run, request: requireRequest(context, current.merge_request_id), resultPath, projectRoot }, checks, settings)}\n${stageLifecycleInstruction}\n`;
193
+ const prompt = `${mergePrompt(context, { run, request: requireRequest(context, current.merge_request_id), resultPath, projectRoot }, checks, settings)}\n${stageLifecycleInstruction}\n${verificationMatrixInstruction(prepared.matrix?.metadata ?? null, run.run_root, "MERGE source/target verification and residual-risk assessment")}\n`;
186
194
  fs.writeFileSync(promptPath, prompt);
187
195
  const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
188
196
  context.db.run("UPDATE work_sessions SET prompt_path = ?, result_path = ?, updated_at = ? WHERE work_id = ? AND status = 'running'", [promptPath, resultPath, context.now(), current.executor_work_id]);
189
- attachFlowRunStage(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@2" });
197
+ const attachmentInput = { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/stage-report@2" };
198
+ const attachment = alreadyAttached ? null : prepareFlowRunStageAttachment(context, attachmentInput);
199
+ commitVerificationMatrixStart(context, prepared.matrix, () => attachment ? attachFlowRunStage(context, attachmentInput, attachment) : undefined);
190
200
  appendFlowRunTimelineEvent(context, project.id, run.id, { type: "merge_started", merge_request_id: current.merge_request_id, work_id: current.executor_work_id, target_head: targetHead });
191
201
  return startPacket(context, run, current, started, promptPath, resultPath, prompt, projectRoot, checks, externalContext);
192
202
  }
@@ -323,6 +333,11 @@ export async function finishVnextMerge(context, input, prepared = prepareVnextMe
323
333
  verifyLocalDelivery(request);
324
334
  context.db.run("UPDATE merge_requests SET checkpoint = 'delivered', updated_at = ? WHERE merge_request_id = ?", [context.now(), request.merge_request_id]);
325
335
  request = requireRequest(context, request.merge_request_id);
336
+ const matrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "output", receipts, semanticFile: prepared.resultPath });
337
+ // Retain exact bounded sources before deleting the feature checkout. A
338
+ // crash here recovers presentation from packets, never repeats the checks.
339
+ context.db.writeTransaction(() => acceptVerificationMatrix(context, matrix));
340
+ publishVerificationMatrix(context, matrix);
326
341
  performConfiguredCleanup(run, request);
327
342
  const acceptedResult = prepared.result.text;
328
343
  const executor = context.db.get("SELECT status, result FROM works WHERE work_id = ?", [request.executor_work_id]);
@@ -338,6 +353,8 @@ export async function finishVnextMerge(context, input, prepared = prepareVnextMe
338
353
  const root = path.join(requireHome(run), stageDir);
339
354
  const retainedReport = completedMergeReport(context, run, root);
340
355
  const report = retainedReport ?? mergeReport(context, run, requireRequest(context, request.merge_request_id), semantic, receipts);
356
+ if (matrix)
357
+ Object.assign(report.semantic, { verification_matrix: matrix.metadata });
341
358
  if (!retainedReport)
342
359
  commitMergeStageReport(context, run, projectRoot, root, report);
343
360
  const rootWork = requireRootWork(context, project.id, run.id);
@@ -409,10 +426,16 @@ function completedMergeReport(context, run, root) {
409
426
  return JSON.parse(fs.readFileSync(file, "utf8"));
410
427
  }
411
428
  function commitMergeStageReport(context, run, projectRoot, root, report) {
429
+ const matrix = latestRunVerificationMatrix(context, run.project_id, run.id, stage, "output");
430
+ if (matrix)
431
+ Object.assign(report.semantic, { verification_matrix: matrix.metadata });
432
+ if (JSON.parse(run.index_json).verification_matrix_contract && !matrix)
433
+ throw new AppError("verification_matrix_packet_missing", "Completed marked MERGE lacks its retained final matrix", 1, { run_id: run.id });
412
434
  const preparedReport = prepareStageReport(report);
413
435
  const completion = { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@2", report: "stage-report.md", stageReport: "stage-report.html", dataSha256: preparedReport.sha256 };
414
436
  const preparedCompletion = prepareFlowRunStageCompletion(context, completion);
415
437
  withStageSettlement(context, () => {
438
+ acceptVerificationMatrix(context, matrix);
416
439
  acceptStageReport(context, { projectId: run.project_id, runId: run.id, stage, root, prepared: preparedReport });
417
440
  completeFlowRunStage(context, completion, preparedCompletion);
418
441
  });
@@ -22,6 +22,8 @@ import { prepareJsonSchema, prepareOutputFile } from "../cli/input-preparation.j
22
22
  import { getStageFanoutCapacity, readFanoutDescriptor, subagentCapacityKey, writeFanoutDescriptor } from "./vnext-fanout.js";
23
23
  import { stageGraphScope } from "./stage-work-graph.js";
24
24
  import { stageLifecycleInstruction } from "../harness-runtime/lib/delegation-instructions.mjs";
25
+ import { prepareRunVerificationMatrix, acceptVerificationMatrix, commitVerificationMatrixStart, resumeVerificationMatrix, verificationMatrixInstruction, latestRunVerificationMatrix } from "./verification-matrix.js";
26
+ import { reviewerSourceInputs, reviewerSourceInstruction } from "./review-copy.js";
25
27
  const stage = "plan-review";
26
28
  const stageDir = vnextStageDirectory(stage);
27
29
  const reviewTask = "Independently review the accepted PLAN and decide whether CODE may open.";
@@ -54,19 +56,24 @@ export function prepareVnextPlanReviewStart(context, input) {
54
56
  assertReviewWorkspace(run.workspace_root, protocolIds);
55
57
  const groups = reviewGroups(home, run.workspace_root);
56
58
  const effective = resolveMode(requested, groups, home, run.workspace_root);
59
+ const reviewerInputs = effective === "off" ? [] : reviewerSourceInputs(run.workspace_root, groups.flatMap(group => group.aspect_ids));
60
+ if (effective !== "off")
61
+ for (const group of groups)
62
+ group.task += `\n${reviewerSourceInstruction(run.workspace_root, group.aspect_ids)}`;
57
63
  const existing = context.db.get("SELECT work_id, status FROM works WHERE project_id = ? AND run_id = ? AND task = ? ORDER BY created_at DESC LIMIT 1", [project.id, run.id, reviewTask]);
58
64
  const batchChecksum = checksum(batch), planChecksum = planSetChecksum(home, run.workspace_root);
59
65
  if (input.externalContext)
60
66
  prepareExternalStageContext({ stageRoot: root, loaded: input.externalContext });
61
67
  prepareOutputFile(path.join(root, "stage-prompt.md"));
62
- const active = { ...common, requested, protocolIds, groups, effective, workspaceRoute, batchChecksum, planChecksum };
68
+ const attachment = existing?.status === "running" ? null : prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@3" });
69
+ const matrix = existing?.status === "running" ? latestRunVerificationMatrix(context, project.id, run.id, stage, "input") : prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "input", ...(attachment ? { stageAttempt: attachment.attempt } : {}) });
70
+ const active = { ...common, requested, protocolIds, groups, effective, workspaceRoute, batchChecksum, planChecksum, matrix, reviewerInputs };
63
71
  if (existing?.status === "running") {
64
72
  const orchestration = readFanoutDescriptor(root);
65
73
  if (!orchestration)
66
74
  prepareOutputFile(path.join(root, "fanout.json"));
67
75
  return { ...active, state: "resumed", workId: existing.work_id, prompt: fs.readFileSync(path.join(root, "stage-prompt.md"), "utf8"), orchestration };
68
76
  }
69
- prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@3" });
70
77
  const rootWork = rootWorkId(context, project.id, run.id);
71
78
  if (effective === "off") {
72
79
  const failures = validateVnextPlanArtifacts(context, { projectRoot, workspaceRoot: run.workspace_root, runId: run.id, home, protocols: protocolIds });
@@ -85,7 +92,8 @@ export function prepareVnextPlanReviewStart(context, input) {
85
92
  }
86
93
  for (const name of ["stage-report.json", "stage-report.md", "stage-report.html"])
87
94
  prepareOutputFile(path.join(root, name));
88
- return { ...active, state: "off", rootWork, codeItems };
95
+ const outputMatrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "output", ...(attachment ? { stageAttempt: attachment.attempt } : {}) });
96
+ return { ...active, state: "off", rootWork, codeItems, outputMatrix };
89
97
  }
90
98
  const template = read(path.join(projectRoot, ".memory-bank", "dd-flow", "vnext", "plan-review.md"));
91
99
  const revision = currentPlanRevision(home, run.workspace_root);
@@ -103,11 +111,13 @@ export function startVnextPlanReview(context, input, prepared = prepareVnextPlan
103
111
  }
104
112
  const { requested, effective, groups, batchChecksum, planChecksum, workspaceRoute } = prepared;
105
113
  if (prepared.state === "resumed") {
114
+ resumeVerificationMatrix(context, project.id, run.id, stage);
106
115
  const promptPath = path.join(root, "stage-prompt.md");
107
116
  const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
108
117
  return { ...preparedExisting(context, { projectRoot, projectId: project.id, run, root, workId: prepared.workId, hookEventId: input.hookEventId, ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}), requested, effective, groups, batchChecksum, planChecksum, orchestration: prepared.orchestration }), ...(externalContext ? { external_context: externalContext } : {}) };
109
118
  }
110
119
  ensureWorkRegistry(context);
120
+ prepareFlowRunStageAttachment(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@3" }, true);
111
121
  fs.mkdirSync(root, { recursive: true });
112
122
  const now = context.now(), rootWork = prepared.rootWork;
113
123
  if (prepared.state === "off") {
@@ -115,12 +125,17 @@ export function startVnextPlanReview(context, input, prepared = prepareVnextPlan
115
125
  fs.writeFileSync(promptPath, "PLAN-REVIEW is disabled by the frozen RUN configuration.\n");
116
126
  const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
117
127
  bindStageCoordinatorWork(context, { workId: rootWork, hookEventId: input.hookEventId, stage, promptPath, resultPath: path.join(root, "stage-report.json"), ...(input.contextSha256 ? { contextSha256: input.contextSha256 } : {}) });
118
- attachFlowRunStage(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@3" });
128
+ const attachmentInput = { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@3" };
129
+ const attachment = prepareFlowRunStageAttachment(context, attachmentInput);
130
+ commitVerificationMatrixStart(context, prepared.matrix, () => attachFlowRunStage(context, attachmentInput, attachment));
119
131
  const reportGit = gitFacts(run.workspace_root);
120
132
  const nextCommand = codeCommand(context, run.id, projectRoot);
121
133
  return withStageSettlement(context, () => {
122
134
  const registered = addWorkBatch(context, { parentWorkId: rootWork, prepared: prepared.codeItems, stage: "code" });
123
135
  const report = reportFor(context, { run, mode: effective, requested, outcome: "off", groups: [], batchChecksum, planChecksum, code: registered, now, projectRoot, flow: flowCommand(context), git: reportGit, nextCommand });
136
+ acceptVerificationMatrix(context, prepared.outputMatrix);
137
+ if (prepared.outputMatrix)
138
+ Object.assign(report.semantic, { verification_matrix: prepared.outputMatrix.metadata });
124
139
  const preparedReport = prepareStageReport(report);
125
140
  acceptStageReport(context, { projectId: project.id, runId: run.id, stage, root, prepared: preparedReport });
126
141
  const completionInput = { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html", dataSha256: preparedReport.sha256, git: reportGit };
@@ -133,7 +148,7 @@ export function startVnextPlanReview(context, input, prepared = prepareVnextPlan
133
148
  const workId = nextWorkId(context, project.id, "plan-review");
134
149
  context.db.run("INSERT INTO works (work_id, project_id, run_id, parent_work_id, task, launch_policy, result_schema, depends_on_json, status, result, started_at, created_at, updated_at, completed_at) VALUES (?, ?, ?, ?, ?, 'reuse_allowed', NULL, '[]', 'running', NULL, ?, ?, ?, NULL)", [workId, project.id, run.id, rootWork, reviewTask, now, now, now]);
135
150
  const pauseCommand = stagePauseCommand(context, { runId: run.id, stage, workId, projectRoot });
136
- const prompt = `${orchestratorPrompt(context, { projectRoot, run, home, root, workId, requested, effective, groups, batchChecksum, planChecksum, workspaceRoute, pauseCommand, pauseCommandTemplate: stagePauseCommandTemplate(pauseCommand), template: prepared.template, revision: prepared.revision, capacity: prepared.capacity })}\n${stageLifecycleInstruction}\n`;
151
+ const prompt = `${orchestratorPrompt(context, { projectRoot, run, home, root, workId, requested, effective, groups, batchChecksum, planChecksum, workspaceRoute, pauseCommand, pauseCommandTemplate: stagePauseCommandTemplate(pauseCommand), template: prepared.template, revision: prepared.revision, capacity: prepared.capacity })}\n${stageLifecycleInstruction}\n${verificationMatrixInstruction(prepared.matrix?.metadata ?? null, home, "reviewing PLAN coverage, fixtures and proof limits")}\n`;
137
152
  const promptPath = path.join(root, "stage-prompt.md");
138
153
  fs.writeFileSync(promptPath, prompt);
139
154
  const externalContext = applyExternalStageContext({ stageRoot: root, promptPath, ...(input.externalContext ? { loaded: input.externalContext } : {}) });
@@ -146,12 +161,14 @@ export function startVnextPlanReview(context, input, prepared = prepareVnextPlan
146
161
  fs.rmSync(promptPath, { force: true });
147
162
  throw error;
148
163
  }
149
- attachFlowRunStage(context, { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@3" });
164
+ const attachmentInput = { projectRoot, runId: run.id, stage, dir: stageDir, status: "running", dataSchemaId: "dd-flow/plan-review-decision@3" };
165
+ const attachment = prepareFlowRunStageAttachment(context, attachmentInput);
166
+ commitVerificationMatrixStart(context, prepared.matrix, () => attachFlowRunStage(context, attachmentInput, attachment));
150
167
  markReviewPending(home, groups);
151
168
  const workSessionId = String(binding.work_session_id);
152
169
  const sessionId = String(binding.session_id);
153
170
  const orchestration = writeFanoutDescriptor(root, { stage, parent_work_id: workId, dispatch: "plan-review", capacity_required: true, scope: stageGraphScope(context, project.id, run.id, stage) });
154
- writeJson(path.join(root, "work-context.json"), { schema_id: "dd-flow/plan-review-context@1", read_only_input: { parent_work_id: workId, version: 3 }, system: { run_id: run.id, work_id: workId, stage, requested_mode: requested, effective_mode: effective, plan_revision: prepared.revision, plan_checksum: planChecksum, batch_checksum: batchChecksum }, groups, workspace: { project_root: projectRoot, workspace_root: run.workspace_root, stage_root: root, plan_root: planRoot } });
171
+ writeJson(path.join(root, "work-context.json"), { schema_id: "dd-flow/plan-review-context@1", reviewer_inputs: prepared.reviewerInputs, read_only_input: { parent_work_id: workId, version: 3 }, system: { run_id: run.id, work_id: workId, stage, requested_mode: requested, effective_mode: effective, plan_revision: prepared.revision, plan_checksum: planChecksum, batch_checksum: batchChecksum }, groups, workspace: { project_root: projectRoot, workspace_root: run.workspace_root, stage_root: root, plan_root: planRoot } });
155
172
  refreshRunWorkProjection(context, project.id, run.id);
156
173
  appendFlowRunTimelineEvent(context, project.id, run.id, { type: "work_session_started", stage, work_id: workId, id: workSessionId, session_id: sessionId });
157
174
  return { ok: true, run_id: run.id, stage, outcome: "review_required", work_id: workId, id: workSessionId, prompt_path: promptPath, worker_prompt_markdown: fs.readFileSync(promptPath, "utf8"), ...(externalContext ? { external_context: externalContext } : {}), orchestration, plan_review: { requested_mode: requested, effective_mode: effective, policy_source: policySource(run), groups }, next: { dispatch_command: dispatchCommand(context, run.id, projectRoot), finish_command: finishCommand(context, run.id, projectRoot, path.join(root, "decision.json")) } };
@@ -307,13 +324,17 @@ function finishPlanReviewOwned(context, input) {
307
324
  const codeItems = prepareWorkBatch(context, { parentWorkId: codeParentId, file: batch, stage: "code" });
308
325
  const reportGit = gitFacts(run.workspace_root);
309
326
  const nextCommand = codeCommand(context, run.id, projectRoot);
327
+ const matrix = prepareRunVerificationMatrix(context, { projectId: project.id, runId: run.id, stage, role: "output", semanticFile: decisionPath });
310
328
  return withStageSettlement(context, () => {
311
329
  const registered = addWorkBatch(context, { parentWorkId: codeParentId, prepared: codeItems, stage: "code" });
312
330
  const now = context.now();
313
331
  settleReviewParent(context, { projectRoot, projectId: project.id, runId: run.id, parentWorkId: parent.work_id, status: "completed", result: decision.summary, reason: "plan_review_completed", resultPath: path.join(root, "stage-report.json") });
314
332
  const report = reportFor(context, { run, mode: contextFile.system?.effective_mode ?? "standard", requested: contextFile.system?.requested_mode ?? "auto", outcome: "accepted", groups, batchChecksum: contextFile.system?.batch_checksum ?? finalBatchChecksum, planChecksum: contextFile.system?.plan_checksum ?? finalPlanChecksum, finalBatchChecksum, finalPlanChecksum, initialPlanRevision: reviewedPlanRevision, finalPlanRevision: planRevision, code: registered, now, projectRoot, decision, children: latestChildren, flow: flowCommand(context), git: reportGit, nextCommand });
315
333
  applyReviewResults(home, latestChildren, decision);
334
+ if (matrix)
335
+ Object.assign(report.semantic, { verification_matrix: matrix.metadata });
316
336
  const preparedReport = prepareStageReport(report);
337
+ acceptVerificationMatrix(context, matrix);
317
338
  acceptStagePacket(context, { projectId: project.id, runId: run.id, stage, prepared: decisionReceipt });
318
339
  acceptStageReport(context, { projectId: project.id, runId: run.id, stage, root, prepared: preparedReport });
319
340
  const completionInput = { projectRoot, runId: run.id, stage, status: "done", data: "stage-report.json", dataSchemaId: "dd-flow/stage-report@1", report: "stage-report.md", stageReport: "stage-report.html", dataSha256: preparedReport.sha256, git: reportGit };