@deksden-com/dd-flow-cli 0.9.0-beta.143 → 0.9.0-beta.145

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 (197) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +13 -8
  3. package/dist/build-info.json +7 -7
  4. package/dist/cli/command-inputs.js +9 -3
  5. package/dist/cli/command-metadata.js +33 -14
  6. package/dist/cli/help.js +32 -1
  7. package/dist/cli/hook-ingress.js +27 -3
  8. package/dist/cli/memory-flow-admission.js +7 -3
  9. package/dist/cli/memory-init-invocation.js +10 -3
  10. package/dist/cli/memory-init.js +74 -68
  11. package/dist/cli/memory-storage-admission.js +3 -2
  12. package/dist/cli/memory-storage-qualification.js +105 -0
  13. package/dist/cli/memory-storage-transition.js +123 -0
  14. package/dist/cli/memory-upgrade.js +189 -0
  15. package/dist/cli/native-hook-ingress.js +14 -4
  16. package/dist/cli/native-hook-preparation.js +55 -0
  17. package/dist/cli/response-file.js +2 -1
  18. package/dist/cli/run-cli.js +70 -32
  19. package/dist/cli/setup.js +2 -1
  20. package/dist/cli.js +12 -0
  21. package/dist/domain/stage-handler-identities.js +8 -0
  22. package/dist/harness-runtime/bin/dd-agy.mjs +3 -3
  23. package/dist/harness-runtime/bin/dd-grok.mjs +3 -3
  24. package/dist/harness-runtime/bin/dd-zcode.mjs +5 -5
  25. package/dist/harness-runtime/lib/agy-error-boundary.mjs +58 -0
  26. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +62 -20
  27. package/dist/harness-runtime/lib/dd-agy.mjs +17 -3
  28. package/dist/harness-runtime/lib/dd-droid.mjs +1 -1
  29. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +70 -13
  30. package/dist/harness-runtime/lib/dd-grok.mjs +6 -3
  31. package/dist/harness-runtime/lib/dd-zcode.mjs +44 -14
  32. package/dist/harness-runtime/lib/delegation-instructions.mjs +1 -1
  33. package/dist/harness-runtime/lib/entity-references.d.mts +2 -0
  34. package/dist/harness-runtime/lib/entity-references.mjs +9 -0
  35. package/dist/harness-runtime/lib/managed-daemon.mjs +13 -6
  36. package/dist/harness-runtime/lib/native-hook-command.mjs +4 -4
  37. package/dist/harness-runtime/lib/opencode-plugin.mjs +3 -3
  38. package/dist/harness-runtime/lib/operation-errors.mjs +1 -1
  39. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +4 -2
  40. package/dist/harness-runtime/lib/provider-overload-policy.mjs +19 -6
  41. package/dist/harness-runtime/lib/runner-events.mjs +3 -1
  42. package/dist/harness-runtime/lib/setup-progress.mjs +8 -6
  43. package/dist/schemas/mb-upgrade-stage-result.schema.json +863 -0
  44. package/dist/schemas/memory-repair-work-result.schema.json +9 -2
  45. package/dist/schemas/release-impact.schema.json +3 -3
  46. package/dist/schemas/stage-start-response.schema.json +4 -0
  47. package/dist/services/accepted-intent.js +13 -1
  48. package/dist/services/canon.js +43 -7
  49. package/dist/services/cli-operation-classifier.js +1 -1
  50. package/dist/services/cli-upgrade-allowlist.js +1 -1
  51. package/dist/services/code-checks.js +11 -4
  52. package/dist/services/compatibility-preflight.js +5 -2
  53. package/dist/services/controller-fanout.js +21 -4
  54. package/dist/services/controller-memory-task.js +45 -0
  55. package/dist/services/controller-memory-worker.js +48 -0
  56. package/dist/services/dashboard.js +2 -2
  57. package/dist/services/descriptor-run-start.js +26 -19
  58. package/dist/services/descriptor-stage-coordinator.js +30 -10
  59. package/dist/services/descriptor-stage-start.js +40 -4
  60. package/dist/services/engines.js +64 -14
  61. package/dist/services/eval-snapshots.js +104 -93
  62. package/dist/services/external-work-launch.js +5 -3
  63. package/dist/services/flow-catalog.js +8 -4
  64. package/dist/services/frozen-engine.js +113 -11
  65. package/dist/services/hook-memory-routing.js +188 -0
  66. package/dist/services/hook-recovery-guard.js +14 -0
  67. package/dist/services/hook-session-observation.js +24 -0
  68. package/dist/services/hooks.js +27 -8
  69. package/dist/services/hybrid-contracts.js +2 -13
  70. package/dist/services/hybrid-digest.js +14 -0
  71. package/dist/services/installed-flow-source.js +25 -4
  72. package/dist/services/lifecycle-invocations.js +33 -5
  73. package/dist/services/memory-accepted-composition.js +5 -4
  74. package/dist/services/memory-completed-correction-wave.js +23 -15
  75. package/dist/services/memory-compose-handler.js +163 -21
  76. package/dist/services/memory-composition-acceptance.js +186 -10
  77. package/dist/services/memory-composition-graph.js +42 -11
  78. package/dist/services/memory-composition-registration.js +23 -8
  79. package/dist/services/memory-composition-work-custody.js +14 -9
  80. package/dist/services/memory-composition-work-results.js +15 -3
  81. package/dist/services/memory-corrected-review-batch.js +10 -3
  82. package/dist/services/memory-corrected-review-findings.js +4 -2
  83. package/dist/services/memory-corrected-review-inputs.js +10 -5
  84. package/dist/services/memory-corrected-review-receipt.js +17 -9
  85. package/dist/services/memory-corrected-review-registration.js +3 -2
  86. package/dist/services/memory-correction-check-receipt.js +20 -4
  87. package/dist/services/memory-custody-snapshot.js +36 -0
  88. package/dist/services/memory-finish-evidence.js +44 -22
  89. package/dist/services/memory-finish-handler.js +37 -23
  90. package/dist/services/memory-finish-integration.js +218 -48
  91. package/dist/services/memory-finish-readiness.js +66 -19
  92. package/dist/services/memory-git-integration.js +87 -18
  93. package/dist/services/memory-historical-corrected-review.js +21 -15
  94. package/dist/services/memory-historical-original-review.js +22 -9
  95. package/dist/services/memory-historical-repair-registration.js +14 -8
  96. package/dist/services/memory-historical-second-review.js +30 -11
  97. package/dist/services/memory-init-contexts.js +28 -2
  98. package/dist/services/memory-init-controller-start.js +19 -8
  99. package/dist/services/memory-init-git-policy.js +50 -21
  100. package/dist/services/memory-init-lint-capability.js +21 -3
  101. package/dist/services/memory-init-native-capacity.js +13 -0
  102. package/dist/services/memory-init-preflight.js +11 -1
  103. package/dist/services/memory-init-request-candidate.js +73 -17
  104. package/dist/services/memory-init-request-input.js +23 -7
  105. package/dist/services/memory-init-run-bundle.js +49 -12
  106. package/dist/services/memory-init-storage.js +17 -7
  107. package/dist/services/memory-layer.js +68 -4
  108. package/dist/services/memory-lint-config.js +111 -2
  109. package/dist/services/memory-native-review-copy.js +5 -67
  110. package/dist/services/memory-normal-flow-entry.js +12 -0
  111. package/dist/services/memory-ordinary-defaults.js +22 -5
  112. package/dist/services/memory-preflight-handler.js +15 -0
  113. package/dist/services/memory-repair-current-view.js +1 -1
  114. package/dist/services/memory-repair-document-delta.js +70 -33
  115. package/dist/services/memory-repair-history.js +12 -7
  116. package/dist/services/memory-repair-physical-binding.js +1 -119
  117. package/dist/services/memory-repair-publication-plan.js +5 -3
  118. package/dist/services/memory-repair-publication-source.js +1 -1
  119. package/dist/services/memory-repair-publication.js +3 -2
  120. package/dist/services/memory-repair-registration.js +2 -1
  121. package/dist/services/memory-repair-work-baseline.js +4 -2
  122. package/dist/services/memory-repair-work-completion.js +32 -12
  123. package/dist/services/memory-repair-work-results.js +21 -2
  124. package/dist/services/memory-repair-workspace.js +11 -2
  125. package/dist/services/memory-repair-writer-copy.js +2 -1
  126. package/dist/services/memory-result-binding.js +78 -12
  127. package/dist/services/memory-retained-review-authority.js +17 -8
  128. package/dist/services/memory-retained-second-baseline.js +1 -1
  129. package/dist/services/memory-retained-second-repairs.js +12 -6
  130. package/dist/services/memory-review-correction-plan.js +47 -12
  131. package/dist/services/memory-review-handler.js +17 -17
  132. package/dist/services/memory-review-inputs.js +55 -9
  133. package/dist/services/memory-review-limits.js +10 -0
  134. package/dist/services/memory-review-receipt.js +2 -2
  135. package/dist/services/memory-review-registration.js +13 -4
  136. package/dist/services/memory-review-work-custody.js +3 -1
  137. package/dist/services/memory-review-work-input.js +26 -7
  138. package/dist/services/memory-review-work-results.js +26 -4
  139. package/dist/services/memory-second-corrected-review-inputs.js +5 -3
  140. package/dist/services/memory-second-correction-plan.js +2 -1
  141. package/dist/services/memory-second-repair-launch.js +4 -4
  142. package/dist/services/memory-second-repair-source.js +1 -1
  143. package/dist/services/memory-stage-dispatch.js +38 -1
  144. package/dist/services/memory-stage-results.js +113 -22
  145. package/dist/services/memory-upgrade-analysis-handler.js +243 -0
  146. package/dist/services/memory-upgrade-bootstrap-readiness.js +251 -0
  147. package/dist/services/memory-upgrade-evidence.js +100 -0
  148. package/dist/services/memory-upgrade-inventory.js +442 -0
  149. package/dist/services/memory-upgrade-publication-custody.js +73 -0
  150. package/dist/services/memory-upgrade-publication.js +413 -0
  151. package/dist/services/memory-upgrade-selection.js +430 -0
  152. package/dist/services/memory-upgrade-start.js +60 -0
  153. package/dist/services/memory-upgrade-storage-readiness.js +340 -0
  154. package/dist/services/memory-verification-acceptance.js +38 -17
  155. package/dist/services/memory-verification-checks.js +77 -23
  156. package/dist/services/memory-verification-receipt.js +6 -5
  157. package/dist/services/memory-verify-handler.js +15 -13
  158. package/dist/services/memory-work-result-json.js +12 -0
  159. package/dist/services/memory-work-result-preparation.js +5 -4
  160. package/dist/services/merge-server.js +4 -2
  161. package/dist/services/migrations.js +127 -98
  162. package/dist/services/physical-startup.js +45 -0
  163. package/dist/services/project-registry.js +97 -0
  164. package/dist/services/project-summary.js +1 -1
  165. package/dist/services/projects.js +3 -93
  166. package/dist/services/prompt-delivery.js +71 -0
  167. package/dist/services/provider-capacity.js +74 -9
  168. package/dist/services/recovery-snapshot-database.js +32 -4
  169. package/dist/services/repair-intents.js +1 -12
  170. package/dist/services/repair-receipt.js +13 -0
  171. package/dist/services/run-control-worker.js +35 -18
  172. package/dist/services/run-controller-process.js +2 -6
  173. package/dist/services/run-controller-recovery.js +6 -1
  174. package/dist/services/run-controller.js +131 -80
  175. package/dist/services/run-recovery-guard.js +30 -0
  176. package/dist/services/run-recovery.js +11 -44
  177. package/dist/services/run-start-custody.js +17 -5
  178. package/dist/services/runtime-scope-worker.js +46 -19
  179. package/dist/services/sessions.js +3 -24
  180. package/dist/services/setup.js +63 -13
  181. package/dist/services/stage-handlers.js +9 -7
  182. package/dist/services/stage-pause.js +108 -44
  183. package/dist/services/version-status.js +1 -1
  184. package/dist/services/vnext-fanout.js +21 -9
  185. package/dist/services/vnext-plan-review.js +3 -5
  186. package/dist/services/work-registry.js +80 -33
  187. package/dist/services/workspace-files.js +1 -1
  188. package/dist/shared/entity-references.js +1 -8
  189. package/dist/storage/data-preservation.js +65 -0
  190. package/dist/storage/database.js +53 -19
  191. package/dist/storage/import-guard.js +84 -27
  192. package/dist/storage/memory-upgrade-schema-bootstrap.js +38 -0
  193. package/dist/storage/paired-writer-migration.js +424 -0
  194. package/dist/storage/writer-contract.js +11 -4
  195. package/dist/storage/writer-migration.js +313 -52
  196. package/dist/storage/writer-restore.js +217 -0
  197. package/package.json +8 -4
@@ -57,15 +57,15 @@ export function startMemoryReview(context, input) {
57
57
  if (!(input.bytes instanceof Uint8Array) || input.bytes.length > 1024 * 1024)
58
58
  refuse("invalid_input_bytes");
59
59
  const bundle = readFlowRunExecutionBundle(context, input);
60
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init" || bundle.descriptor.successor("verify", "accepted") !== "review"
60
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id) || bundle.descriptor.successor("verify", "accepted") !== "review"
61
61
  || bundle.descriptor.successor("review", "accepted") !== "finish")
62
62
  refuse("registered_memory_bundle_required");
63
63
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot)), scope = { projectId: project.id, runId: input.runId };
64
64
  const run = assertPreparedRunBundleSqlAuthority(context, scope, bundle), index = JSON.parse(run.index_json);
65
65
  if (index.stage_runs.find((stage) => stage.stage === "review")?.status === "done")
66
66
  refuse("use_accepted_finish_replay");
67
- const preservation = PreparedMemoryBaselinePreservation.prepare(context, bundle.execution);
68
67
  const composition = PreparedAcceptedMemoryComposition.prepare(context, { scope, bundle });
68
+ const preservation = bundle.descriptor.descriptor.id === "mb-upgrade" ? composition : PreparedMemoryBaselinePreservation.prepare(context, bundle.execution);
69
69
  const data = readAcceptedStageData(context, { ...scope, stage: "verify" });
70
70
  const verification = PreparedAcceptedMemoryVerification.prepare(context, { ...scope, bytes: data.bytes });
71
71
  const fence = (writer) => { preservation.assertCustody(writer); composition.assertCustody(writer); verification.assertCustody(writer); };
@@ -82,7 +82,7 @@ export function startMemoryReview(context, input) {
82
82
  */
83
83
  export function registerMemoryReviewFromStage(context, input) {
84
84
  const bundle = readFlowRunExecutionBundle(context, input);
85
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
85
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
86
86
  refuse("registered_memory_bundle_required");
87
87
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
88
88
  const inputs = PreparedMemoryInitialReviewInputs.prepare(context, { scope: { projectId: project.id, runId: input.runId }, bundle });
@@ -94,7 +94,7 @@ export function registerMemoryReviewFromStage(context, input) {
94
94
  */
95
95
  export function collectMemoryInitialReviewFromStage(context, input) {
96
96
  const bundle = readFlowRunExecutionBundle(context, input);
97
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
97
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
98
98
  refuse("registered_memory_bundle_required");
99
99
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
100
100
  const inputs = PreparedMemoryInitialReviewInputs.prepare(context, { scope: { projectId: project.id, runId: input.runId }, bundle });
@@ -107,7 +107,7 @@ export function finishMemoryReview(context, input) {
107
107
  if (!(input.bytes instanceof Uint8Array) || !input.bytes.length || input.bytes.length > 1024 * 1024)
108
108
  refuse("invalid_result_bytes");
109
109
  const bundle = readFlowRunExecutionBundle(context, input);
110
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init" || bundle.descriptor.successor("verify", "accepted") !== "review"
110
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id) || bundle.descriptor.successor("verify", "accepted") !== "review"
111
111
  || bundle.descriptor.successor("review", "accepted") !== "finish")
112
112
  refuse("registered_memory_bundle_required");
113
113
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot)), scope = { projectId: project.id, runId: input.runId };
@@ -170,7 +170,7 @@ export function registerMemoryCorrectionsFromStage(context, input) {
170
170
  if (!(input.bytes instanceof Uint8Array) || !input.bytes.length || input.bytes.length > 1024 * 1024)
171
171
  refuse("invalid_input_bytes");
172
172
  const bundle = readFlowRunExecutionBundle(context, input);
173
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
173
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
174
174
  refuse("registered_memory_bundle_required");
175
175
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
176
176
  const original = PreparedRetainedMemoryReviewAuthority.prepare(context, { projectId: project.id, runId: input.runId }, bundle);
@@ -222,7 +222,7 @@ function prepareMemoryRepairBaselineFromStage(context, input) {
222
222
  }
223
223
  function prepareMemoryRepairRegistrationFromStage(context, input) {
224
224
  const bundle = readFlowRunExecutionBundle(context, input);
225
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
225
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
226
226
  refuse("registered_memory_bundle_required");
227
227
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
228
228
  const original = PreparedRetainedMemoryReviewAuthority.prepare(context, { projectId: project.id, runId: input.runId }, bundle);
@@ -264,7 +264,7 @@ export function registerMemorySecondCorrectionsFromStage(context, input) {
264
264
  if (!(input.bytes instanceof Uint8Array) || !input.bytes.length || input.bytes.length > 1024 * 1024)
265
265
  refuse("invalid_input_bytes");
266
266
  const bundle = readFlowRunExecutionBundle(context, input);
267
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
267
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
268
268
  refuse("registered_memory_bundle_required");
269
269
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
270
270
  try {
@@ -281,7 +281,7 @@ export function registerMemorySecondCorrectionsFromStage(context, input) {
281
281
  }
282
282
  export function prepareMemorySecondRepairSourceFromStage(context, input) {
283
283
  const bundle = readFlowRunExecutionBundle(context, input);
284
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
284
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
285
285
  refuse("registered_memory_bundle_required");
286
286
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
287
287
  const registration = PreparedRetainedSecondMemoryRepairs.prepare(context, { scope: { projectId: project.id, runId: input.runId }, bundle });
@@ -295,7 +295,7 @@ export function prepareMemorySecondRepairSourceFromStage(context, input) {
295
295
  * only; consuming it as a dependent baseline/start requires separate owners. */
296
296
  export function prepareMemorySecondRepairCurrentViewFromStage(context, input) {
297
297
  const bundle = readFlowRunExecutionBundle(context, input);
298
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
298
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
299
299
  refuse("registered_memory_bundle_required");
300
300
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
301
301
  const registration = PreparedRetainedSecondMemoryRepairs.prepare(context, { scope: { projectId: project.id, runId: input.runId }, bundle });
@@ -306,7 +306,7 @@ export function registerMemorySecondRepairCurrentViewFromStage(context, input) {
306
306
  }
307
307
  export function restoreMemorySecondRepairCurrentViewFromStage(context, input) {
308
308
  const bundle = readFlowRunExecutionBundle(context, input);
309
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
309
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
310
310
  refuse("registered_memory_bundle_required");
311
311
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
312
312
  const registration = PreparedRetainedSecondMemoryRepairs.prepare(context, { scope: { projectId: project.id, runId: input.runId }, bundle });
@@ -314,7 +314,7 @@ export function restoreMemorySecondRepairCurrentViewFromStage(context, input) {
314
314
  }
315
315
  export function prepareMemorySecondRepairCurrentSourceFromStage(context, input) {
316
316
  const bundle = readFlowRunExecutionBundle(context, input);
317
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
317
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
318
318
  refuse("registered_memory_bundle_required");
319
319
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
320
320
  const registration = PreparedRetainedSecondMemoryRepairs.prepare(context, { scope: { projectId: project.id, runId: input.runId }, bundle });
@@ -342,7 +342,7 @@ export function prepareMemorySecondRepairDispatchFromStage(context, input) {
342
342
  }
343
343
  export function restoreMemorySecondRepairBaselineFromStage(context, input) {
344
344
  const bundle = readFlowRunExecutionBundle(context, input);
345
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
345
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
346
346
  refuse("registered_memory_bundle_required");
347
347
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
348
348
  const registration = PreparedRetainedSecondMemoryRepairs.prepare(context, { scope: { projectId: project.id, runId: input.runId }, bundle });
@@ -350,7 +350,7 @@ export function restoreMemorySecondRepairBaselineFromStage(context, input) {
350
350
  }
351
351
  export function prepareMemorySecondRepairLaunchFromStage(context, input) {
352
352
  const bundle = readFlowRunExecutionBundle(context, input);
353
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
353
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
354
354
  refuse("registered_memory_bundle_required");
355
355
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
356
356
  const registration = PreparedRetainedSecondMemoryRepairs.prepare(context, { scope: { projectId: project.id, runId: input.runId }, bundle });
@@ -359,7 +359,7 @@ export function prepareMemorySecondRepairLaunchFromStage(context, input) {
359
359
  }
360
360
  export function restoreStartedMemorySecondRepairLaunchFromStage(context, input) {
361
361
  const bundle = readFlowRunExecutionBundle(context, input);
362
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
362
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
363
363
  refuse("registered_memory_bundle_required");
364
364
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
365
365
  const registration = PreparedRetainedSecondMemoryRepairs.prepare(context, { scope: { projectId: project.id, runId: input.runId }, bundle });
@@ -386,7 +386,7 @@ export function finishMemorySecondRepairPublicationFromStage(context, input) {
386
386
  * The whole independent start/publication/SDK settlement chain is required. */
387
387
  export function prepareSettledMemorySecondRepairFromStage(context, input) {
388
388
  const bundle = readFlowRunExecutionBundle(context, input);
389
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
389
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
390
390
  refuse("registered_memory_bundle_required");
391
391
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
392
392
  const registration = PreparedRetainedSecondMemoryRepairs.prepare(context, { scope: { projectId: project.id, runId: input.runId }, bundle });
@@ -396,7 +396,7 @@ export function prepareSettledMemorySecondRepairFromStage(context, input) {
396
396
  * semantic re-review acceptance and does not close the review Stage. */
397
397
  export function prepareCompletedMemorySecondCorrectionsFromStage(context, input) {
398
398
  const bundle = readFlowRunExecutionBundle(context, input);
399
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
399
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
400
400
  refuse("registered_memory_bundle_required");
401
401
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
402
402
  const registration = PreparedRetainedSecondMemoryRepairs.prepare(context, { scope: { projectId: project.id, runId: input.runId }, bundle });
@@ -14,6 +14,8 @@ import { assertRunMutationAllowed } from "./run-recovery-guard.js";
14
14
  import { PreparedDescriptorStageCoordinator } from "./descriptor-stage-coordinator.js";
15
15
  import { readDescriptorCoordinatorSession } from "./descriptor-coordinator-session.js";
16
16
  import { readMemoryInitBootstrapReadiness } from "./memory-init-bootstrap-readiness.js";
17
+ import { readMemoryUpgradeEvidence } from "./memory-upgrade-evidence.js";
18
+ import { memoryReviewViewWithinLimit } from "./memory-review-limits.js";
17
19
  /** Exact inherited evidence only; current artifact bytes remain independently observed. */
18
20
  export function memoryReviewEvidenceTriples(units) {
19
21
  return new Set(units.flatMap(unit => unit.evidence.map(ref => `${memorySourceKey(ref.root, ref.path)}\0${ref.sha256}`)));
@@ -25,7 +27,7 @@ function refuse(reason) {
25
27
  * are read through the assigned workspace, whose baseline is independently
26
28
  * preserved. This function alone supplies neither acceptance nor SQL authority.
27
29
  */
28
- export function memoryReviewUnitAssignments(units, historicalEvidence = new Set()) {
30
+ export function memoryReviewUnitAssignments(units, historicalEvidence = new Set(), retainedEvidence = new Map()) {
29
31
  if (!Array.isArray(units) || !units.length || units.length > 256
30
32
  || units.some(unit => !unit || typeof unit.id !== "string" || !unit.id || typeof unit.aspect !== "string" || !unit.aspect)
31
33
  || new Set(units.map(unit => unit.id)).size !== units.length)
@@ -46,7 +48,8 @@ export function memoryReviewUnitAssignments(units, historicalEvidence = new Set(
46
48
  || ref.path.split("/").some((part) => !part || part === "." || part === "..")
47
49
  || typeof ref.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(ref.sha256))
48
50
  refuse("invalid_unit_source");
49
- const input = { root: ref.root === "run" ? "run" : "workspace",
51
+ const historical = role === "evidence" ? retainedEvidence.get(`${memorySourceKey(ref.root, ref.path)}\0${ref.sha256}`) : undefined;
52
+ const input = historical ?? { root: ref.root === "run" ? "run" : "workspace",
50
53
  path: ref.root === "canon" ? `.memory-bank/${ref.path}` : ref.path, sha256: ref.sha256 };
51
54
  if (role === "evidence" && historicalEvidence.has(`${memorySourceKey(ref.root, ref.path)}\0${ref.sha256}`)
52
55
  && units.flatMap(item => item.artifacts).some(artifact => (artifact.root === "run" ? "run" : "workspace") === input.root
@@ -57,6 +60,12 @@ export function memoryReviewUnitAssignments(units, historicalEvidence = new Set(
57
60
  refuse("conflicting_unit_sources");
58
61
  references.set(key, input);
59
62
  }
63
+ // The author cannot hide a semantic loss by omitting the previous version
64
+ // from unit.evidence. Every rewritten output brings its old bytes to review.
65
+ const outputPaths = new Set(unit.artifacts.map((ref) => memorySourceKey(ref.root === "run" ? "run" : "workspace", ref.root === "canon" ? `.memory-bank/${ref.path}` : ref.path)));
66
+ for (const [source, retained] of retainedEvidence)
67
+ if (outputPaths.has(source.slice(0, source.lastIndexOf("\0"))))
68
+ references.set(memorySourceKey(retained.root, retained.path), retained);
60
69
  if (references.size > 4096)
61
70
  refuse("unbounded_unit_sources");
62
71
  return { unit: structuredClone(unit), inputs: [...references.values()].sort((a, b) => a.path.localeCompare(b.path, "en")) };
@@ -109,7 +118,7 @@ export class PreparedMemoryInitialReviewInputs {
109
118
  }
110
119
  static prepare(context, input) {
111
120
  const { scope, bundle } = input;
112
- if (!(bundle instanceof RetainedExecutionBundle) || bundle.descriptor.descriptor.id !== "mb-init")
121
+ if (!(bundle instanceof RetainedExecutionBundle) || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
113
122
  refuse("registered_memory_bundle_required");
114
123
  const generation = assertRunMutationAllowed(context, scope.projectId, scope.runId);
115
124
  const workerSchema = JSON.parse(bundle.engine().schema("memory-review-work-result").toString("utf8"));
@@ -119,13 +128,50 @@ export class PreparedMemoryInitialReviewInputs {
119
128
  const coordinator = PreparedDescriptorStageCoordinator.prepare(context, { projectRoot: bundle.execution.snapshot().scope.projectRoot, runId: scope.runId, stage: "review" });
120
129
  coordinator.assertAllocatedCustody(context);
121
130
  const coordinatorSession = readDescriptorCoordinatorSession(context, { scope: { ...scope, stage: "review" }, bundle, workId: selected.parent_work_id }, refuse);
122
- const preservation = PreparedMemoryBaselinePreservation.prepare(context, bundle.execution);
123
131
  const composition = PreparedAcceptedMemoryComposition.prepare(context, { scope, bundle });
124
- const compose = readAcceptedStageData(context, { ...scope, stage: "compose" });
132
+ const upgrade = bundle.descriptor.descriptor.id === "mb-upgrade", frozenScope = bundle.execution.snapshot().scope;
133
+ const preservation = upgrade ? composition : PreparedMemoryBaselinePreservation.prepare(context, bundle.execution);
134
+ const old = upgrade ? readMemoryUpgradeEvidence(context, { requestId: frozenScope.requestId, projectRoot: frozenScope.projectRoot, workspaceRoot: frozenScope.workspace }) : null;
135
+ const retainedEvidence = new Map();
136
+ for (const entry of old?.index.entries ?? []) {
137
+ retainedEvidence.set(`${memorySourceKey(entry.source.root, entry.source.path)}\0${entry.source.sha256}`, entry.retained);
138
+ if (frozenScope.workspace === frozenScope.projectRoot)
139
+ retainedEvidence.set(`${memorySourceKey("project", entry.source.path)}\0${entry.source.sha256}`, entry.retained);
140
+ }
141
+ const compose = readAcceptedStageData(context, { ...scope, stage: upgrade ? "upgrade" : "compose" });
125
142
  const verify = readAcceptedStageData(context, { ...scope, stage: "verify" });
126
143
  const verification = PreparedAcceptedMemoryVerification.prepare(context, { ...scope, bytes: verify.bytes });
127
144
  const unitInventory = composition.result().value().units;
128
- const assignments = memoryReviewUnitAssignments(unitInventory, composition.historicalEvidence());
145
+ const assignments = memoryReviewUnitAssignments(unitInventory, composition.historicalEvidence(), retainedEvidence);
146
+ if (upgrade && typeof verify.input_sha256 === "string") {
147
+ const reference = { root: "run", path: `${bundle.descriptor.stage("verify").directory}/stage-input.json`, sha256: verify.input_sha256 };
148
+ for (const assignment of assignments)
149
+ if (!assignment.inputs.some(ref => ref.root === reference.root && ref.path === reference.path))
150
+ assignment.inputs.push(reference);
151
+ }
152
+ if (old) {
153
+ const diff = JSON.parse(readAcceptedStageData(context, { ...scope, stage: "diff-analysis" }).bytes.toString("utf8"));
154
+ for (const change of diff.details.path_map) {
155
+ const assigned = assignments.find(item => item.unit.id === change.unit_id);
156
+ const before = old.index.entries.find(entry => entry.source.path === change.from)?.retained;
157
+ if (assigned && before && !assigned.inputs.some(ref => ref.root === before.root && ref.path === before.path))
158
+ assigned.inputs.push(before);
159
+ }
160
+ }
161
+ // Every named preservation criterion is supplied independently of which
162
+ // historical references the author chose to repeat in its final unit.
163
+ const invariants = composition.result().preservationInvariants();
164
+ for (const invariant of invariants) {
165
+ const assigned = assignments.find(item => item.unit.id === invariant.unit_id);
166
+ if (!assigned)
167
+ refuse("preservation_invariant_without_reviewer");
168
+ for (const ref of invariant.evidence) {
169
+ const retained = retainedEvidence.get(`${memorySourceKey(ref.root, ref.path)}\0${ref.sha256}`)
170
+ ?? { root: ref.root === "run" ? "run" : "workspace", path: ref.root === "canon" ? `.memory-bank/${ref.path}` : ref.path, sha256: ref.sha256 };
171
+ if (!assigned.inputs.some(input => input.root === retained.root && input.path === retained.path && input.sha256 === retained.sha256))
172
+ assigned.inputs.push(retained);
173
+ }
174
+ }
129
175
  const diagnosticFindings = verification.qualityPassed(context) ? [] : verification.diagnosticFindings(context).filter(finding => finding.status === "open" && finding.severity !== "minor");
130
176
  for (const finding of diagnosticFindings) {
131
177
  const assignment = assignments.find(item => item.unit.id === finding.unit_id);
@@ -136,8 +182,7 @@ export class PreparedMemoryInitialReviewInputs {
136
182
  assignment.inputs.push({ ...ref, root: "run" });
137
183
  }
138
184
  const inputs = new Map();
139
- const frozenScope = bundle.execution.snapshot().scope;
140
- const bootstrap = readMemoryInitBootstrapReadiness(context, { requestId: frozenScope.requestId,
185
+ const bootstrap = upgrade ? null : readMemoryInitBootstrapReadiness(context, { requestId: frozenScope.requestId,
141
186
  projectRoot: frozenScope.projectRoot, workspaceRoot: frozenScope.workspace });
142
187
  const lintSha256 = bootstrap?.authority.binding.target.generated_lint_config_sha256;
143
188
  if (lintSha256)
@@ -168,8 +213,9 @@ export class PreparedMemoryInitialReviewInputs {
168
213
  source_policy: "review-inputs@1", source_sha256: reviewSourceHash(roots.workspace, paths, base), base,
169
214
  run_inputs: runInputs, unit_inventory: structuredClone(unitInventory), assignments,
170
215
  ...(diagnosticFindings.length ? { diagnostic_findings: diagnosticFindings } : {}),
216
+ ...(old ? { upgrade_baseline: { ...old.index, entries: old.index.entries.filter(entry => runInputs.some(ref => ref.root === entry.retained.root && ref.path === entry.retained.path)) }, preservation_invariants: invariants } : {}),
171
217
  instruction: { sha256: bundle.descriptor.stage("review").instruction.sha256, base64: instruction.toString("base64") } };
172
- if (Buffer.byteLength(JSON.stringify(view)) > 1024 * 1024)
218
+ if (!memoryReviewViewWithinLimit(view))
173
219
  refuse("unbounded_review_input");
174
220
  const prepared = new PreparedMemoryInitialReviewInputs(scope, bundle, selected, composition, verification, preservation, observed, view, generation, coordinator, coordinatorSession);
175
221
  prepared.assertCustody(context);
@@ -0,0 +1,10 @@
1
+ /** A prepared wave aggregates independently bounded units and retained before
2
+ * references. It is not one untrusted agent response (still capped at 1 MiB).
3
+ * Use the existing complete reviewer-batch budget for all three wave views;
4
+ * leave room for allocation identities in their retained SQL receipts.
5
+ */
6
+ export const memoryReviewWaveByteLimit = 64 * 1024 * 1024;
7
+ export const memoryReviewRegistrationByteLimit = memoryReviewWaveByteLimit + 1024 * 1024;
8
+ export function memoryReviewViewWithinLimit(view) {
9
+ return Buffer.byteLength(JSON.stringify(view)) <= memoryReviewWaveByteLimit;
10
+ }
@@ -100,7 +100,7 @@ function accepted(context, scope, bundle) {
100
100
  }
101
101
  if (!receipt || typeof receipt !== "object" || Array.isArray(receipt) || !receipt.registration_scope
102
102
  || receipt.registration_scope.bundle_sha256 !== bundle.sha256 || receipt.registration_scope.execution_sha256 !== bundle.execution.sha256
103
- || receipt.wave !== 0 || data.data_schema_id !== "dd-flow/mb-init-review-result@1" || data.input_sha256 !== selected.input_sha256)
103
+ || receipt.wave !== 0 || data.data_schema_id !== (bundle.descriptor.descriptor.id === "mb-upgrade" ? "dd-flow/mb-upgrade-stage-result@1" : "dd-flow/mb-init-review-result@1") || data.input_sha256 !== selected.input_sha256)
104
104
  refuse("accepted_review_changed");
105
105
  const registration = readRegisteredMemoryReview(context, receipt.registration_scope, 0);
106
106
  const batch = memoryReviewBatch(registration.view), harness = runtimeHarness(bundle.execution.work("review").worker);
@@ -136,7 +136,7 @@ export class PreparedAcceptedMemoryInitialReview {
136
136
  Object.freeze(this);
137
137
  }
138
138
  static prepare(context, input) {
139
- if (!(input.bundle instanceof RetainedExecutionBundle) || input.bundle.descriptor.descriptor.id !== "mb-init"
139
+ if (!(input.bundle instanceof RetainedExecutionBundle) || !["mb-init", "mb-upgrade"].includes(input.bundle.descriptor.descriptor.id)
140
140
  || !(input.bytes instanceof Uint8Array) || !input.bytes.length || input.bytes.length > 1024 * 1024)
141
141
  refuse("registered_memory_result_required");
142
142
  if (context.db.isOwnedWriteTransaction?.())
@@ -3,6 +3,7 @@ import { appendAudit } from "./audit.js";
3
3
  import { hybridDigest } from "./hybrid-contracts.js";
4
4
  import { PreparedMemoryInitialReviewInputs } from "./memory-review-inputs.js";
5
5
  import { addWorkBatch, prepareWorkBatch, readWorkBatchCustody } from "./work-registry.js";
6
+ import { memoryReviewRegistrationByteLimit } from "./memory-review-limits.js";
6
7
  export const memoryReviewWorkerSchema = "dd-flow/memory-review-work-result@1";
7
8
  const event = "memory.review_registration";
8
9
  function refuse(reason) {
@@ -16,7 +17,7 @@ function selected(context, scope, wave) {
16
17
  const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type=? AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.scope.attempt')=? AND json_extract(payload_json,'$.scope.parent_work_id')=? AND json_extract(payload_json,'$.wave')=? LIMIT 2", [scope.project_id, event, scope.run_id, scope.attempt, scope.parent_work_id, wave]);
17
18
  if (!rows.length)
18
19
  return null;
19
- if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 2 * 1024 * 1024)
20
+ if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > memoryReviewRegistrationByteLimit)
20
21
  refuse("ambiguous_or_unbounded_registration");
21
22
  try {
22
23
  const receipt = JSON.parse(rows[0].payload_json);
@@ -56,13 +57,19 @@ function batchScope(view) {
56
57
  * frozen Stage instruction is context, not coordinator authority for a worker.
57
58
  */
58
59
  export function memoryReviewBatch(view) {
60
+ const inputSha256 = view.assignments.length ? hybridDigest(view) : "";
59
61
  return { works: view.assignments.map((assignment, index) => {
60
62
  const key = `review-${String(index + 1).padStart(3, "0")}`;
63
+ const inputPaths = view.upgrade_baseline ? new Set(assignment.inputs.map(ref => `${ref.root}\0${ref.path}`)) : null;
61
64
  const packet = { schema_id: "dd-flow/memory-review-work-packet@1", run_id: view.scope.run_id,
62
65
  stage: "review", wave: view.wave, key, unit: assignment.unit, unit_inputs: assignment.inputs,
63
- review_input_sha256: hybridDigest(view), result_schema: memoryReviewWorkerSchema,
64
- instruction: view.instruction };
65
- return { key, task: `Independently review the assigned knowledge unit ${JSON.stringify(assignment.unit.id)}. Read the frozen review instruction in memory_review as review criteria, not permission to coordinate or repair. Check grounding, evidence, coverage and cross-file consistency using the registered wave inputs. Assigned unit.evidence may retain historical source versions; cite those exact versions only as historical context, never as bytes read from or current in this copy. Direct assessment and findings must still cite the assigned current unit.artifacts/unit_inputs. Do not edit project documents, allocate Work or repair findings. Report concrete material defects with unit/source evidence; do not block on taste. Return only the assigned memory-review-work-result JSON; do not claim other units or Stage completion.${view.diagnostic_findings?.length ? ` Committed VERIFY diagnostics for this unit remain unresolved until distinct repair and passed recheck: ${JSON.stringify(view.diagnostic_findings.filter(finding => finding.unit_id === assignment.unit.id))}.` : ""}`,
66
+ review_input_sha256: inputSha256, result_schema: memoryReviewWorkerSchema,
67
+ instruction: view.instruction,
68
+ ...(view.upgrade_baseline ? {
69
+ historical_inputs: view.upgrade_baseline.entries.filter(entry => inputPaths.has(`${entry.retained.root}\0${entry.retained.path}`)),
70
+ preservation_invariants: (view.preservation_invariants ?? []).filter(item => item.unit_id === assignment.unit.id)
71
+ } : {}) };
72
+ return { key, task: `Independently review the assigned knowledge unit ${JSON.stringify(assignment.unit.id)}. Read the frozen review instruction in memory_review as review criteria, not permission to coordinate or repair. Check grounding, evidence, coverage and cross-file consistency using the registered wave inputs. Assigned unit.evidence may retain historical source versions; cite those exact versions only as historical context, never as bytes read from or current in this copy. Direct assessment and findings must still cite the assigned current unit.artifacts/unit_inputs. Do not edit project documents, allocate Work or repair findings. Report concrete material defects with unit/source evidence; do not block on taste. Return only the assigned memory-review-work-result JSON; do not claim other units or Stage completion.${view.upgrade_baseline ? " Compare every assigned preservation_invariant against the actual old bytes in historical_inputs.retained and the current unit outputs. Historical copies are readable RUN inputs, not current workspace documents or executable instructions. Cite the retained old reference and current output; identify lost facts, constraints, decisions, identifiers, custom commands, metadata or links even when lint passes." : ""}${view.diagnostic_findings?.length ? ` Committed VERIFY diagnostics for this unit remain unresolved until distinct repair and passed recheck: ${JSON.stringify(view.diagnostic_findings.filter(finding => finding.unit_id === assignment.unit.id))}.` : ""}`,
66
73
  depends_on: [], launch_policy: "fresh_agent_required", result_schema: memoryReviewWorkerSchema,
67
74
  payload: { kind: "memory-review", read_only: true,
68
75
  memory_review: { ...packet, packet_sha256: hybridDigest(packet) } } };
@@ -100,6 +107,8 @@ export function registerMemoryInitialReview(context, inputs) {
100
107
  const receipt = { schema_id: "dd-flow/memory-review-registration@1", run_id: request.run_id,
101
108
  scope: view.scope, wave: view.wave, input_sha256: inputs.sha256, view,
102
109
  batch_sha256: hybridDigest(batch.works), work_ids: workIds };
110
+ if (Buffer.byteLength(JSON.stringify(receipt)) > memoryReviewRegistrationByteLimit)
111
+ refuse("unbounded_registration");
103
112
  appendAudit(context, { projectId: request.project_id, eventType: event, payload: receipt });
104
113
  }
105
114
  const retained = readRegisteredMemoryReview(context, view.scope, view.wave);
@@ -84,14 +84,16 @@ export class PreparedRegisteredMemoryReviewWorkResult {
84
84
  if (!run)
85
85
  refuse("owned_run_required");
86
86
  const generation = recoveryGuard(context, work.project_id, work.run_id)?.generation ?? 0;
87
- const owner = prepareMemoryReviewWorkInput(context, work, run, generation);
87
+ const owner = prepareMemoryReviewWorkInput(context, work, run, generation, input.progress);
88
88
  const bytes = Buffer.from(input.bytes), value = validateMemoryReviewWorkResult(bytes, owner.bundle.engine(), {
89
89
  workId: work.work_id, packet: owner.packet(), references: owner.input.run_inputs
90
90
  });
91
+ input.progress?.(`Memory review ${work.work_id}: result validated`);
91
92
  const harness = runtimeHarness(owner.bundle.execution.work("review").worker);
92
93
  const allocation = readWorkBatchCustody(context, work.work_id), selected = physical(context, work, owner, harness);
93
94
  const prepared = new PreparedRegisteredMemoryReviewWorkResult(bytes, value, work, owner, allocation, selected, harness);
94
95
  prepared.assertCustody(context);
96
+ input.progress?.(`Memory review ${work.work_id}: result custody verified`);
95
97
  return prepared;
96
98
  }
97
99
  bytes() { return Buffer.from(this.#bytes); }
@@ -21,10 +21,11 @@ function refuse(reason) {
21
21
  /** Physical preparation outside a writer. The SQL-only closure must also be
22
22
  * consumed by Work claim, not just by an external launch/copy precheck.
23
23
  */
24
- export function prepareMemoryReviewWorkInput(context, work, run, generation) {
24
+ export function prepareMemoryReviewWorkInput(context, work, run, generation, progress) {
25
25
  const bundle = readFlowRunExecutionBundle(context, { projectRoot: run.project_root, runId: work.run_id });
26
- if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
26
+ if (!bundle || !["mb-init", "mb-upgrade"].includes(bundle.descriptor.descriptor.id))
27
27
  refuse("registered_memory_bundle_required");
28
+ progress?.(`Memory review ${work.work_id}: bundle verified`);
28
29
  let payload;
29
30
  try {
30
31
  payload = JSON.parse(work.payload_json ?? "null");
@@ -40,8 +41,14 @@ export function prepareMemoryReviewWorkInput(context, work, run, generation) {
40
41
  const scope = { projectId: work.project_id, runId: work.run_id };
41
42
  const corrected = packet.wave === 2 ? PreparedSecondCorrectedMemoryReviewInputs.prepareFromBundle(context, { scope, bundle })
42
43
  : packet.wave === 1 ? PreparedCorrectedMemoryReviewInputs.prepareFromBundle(context, { scope, bundle }) : null;
43
- const registration = corrected ? PreparedRegisteredCorrectedMemoryReview.restore(context, corrected, PreparedMemoryCorrectionCheckReceipt.restore(context, corrected)) : null;
44
44
  const owner = corrected ?? PreparedMemoryInitialReviewInputs.prepare(context, { scope, bundle });
45
+ progress?.(`Memory review ${work.work_id}: inputs prepared`);
46
+ const checks = corrected ? PreparedMemoryCorrectionCheckReceipt.restore(context, corrected) : null;
47
+ if (checks)
48
+ progress?.(`Memory review ${work.work_id}: checks restored`);
49
+ const registration = corrected && checks ? PreparedRegisteredCorrectedMemoryReview.restore(context, corrected, checks) : null;
50
+ if (registration)
51
+ progress?.(`Memory review ${work.work_id}: registration restored`);
45
52
  const view = owner.value(), receipt = registration ? registration.receipt() : readRegisteredMemoryReview(context, view.scope, view.wave);
46
53
  if (receipt.input_sha256 !== owner.sha256 || work.parent_work_id !== view.scope.parent_work_id
47
54
  || work.result_schema !== memoryReviewWorkerSchema || work.launch_policy !== "fresh_agent_required")
@@ -53,20 +60,32 @@ export function prepareMemoryReviewWorkInput(context, work, run, generation) {
53
60
  refuse("changed_review_assignment");
54
61
  const receiptSha256 = hybridDigest(receipt);
55
62
  const assertCustody = (writer) => {
56
- owner.assertCustody(writer);
57
63
  if (registration)
58
64
  registration.assertCustody(writer);
59
- else if (hybridDigest(readRegisteredMemoryReview(writer, view.scope, view.wave)) !== receiptSha256)
60
- refuse("review_registration_changed");
65
+ else {
66
+ owner.assertCustody(writer);
67
+ if (hybridDigest(readRegisteredMemoryReview(writer, view.scope, view.wave)) !== receiptSha256)
68
+ refuse("review_registration_changed");
69
+ }
61
70
  };
62
71
  assertCustody(context);
72
+ progress?.(`Memory review ${work.work_id}: assignment verified`);
63
73
  const input = { schema_id: "dd-flow/review-input@3", parent_work_id: view.scope.parent_work_id,
64
74
  kind: "memory-review", cycle: `memory-review-wave-${view.wave}`, generation,
65
75
  workspace_fingerprint: view.source_sha256, source_policy: "review-inputs@1",
66
76
  source_sha256: view.source_sha256, run_inputs: structuredClone(view.run_inputs) };
67
77
  const prepared = Object.freeze({ input, assertCustody, bundle,
68
78
  registration: () => structuredClone(receipt), packet: () => structuredClone(assigned.payload.memory_review),
69
- revalidate: (reader) => { owner.revalidate(reader); registration?.revalidate(reader); assertCustody(reader); } });
79
+ // The registered owner already revalidates corrected inputs, checks and
80
+ // its allocation. Repeating those traversals here adds no new boundary.
81
+ revalidate: (reader) => {
82
+ if (registration)
83
+ registration.revalidate(reader);
84
+ else {
85
+ owner.revalidate(reader);
86
+ assertCustody(reader);
87
+ }
88
+ } });
70
89
  preparedOwners.set(prepared, { store: context.db, work: workScope(work), root: run.project_root, generation, input: hybridDigest(input) });
71
90
  return prepared;
72
91
  }
@@ -3,6 +3,7 @@ import { createHash } from "node:crypto";
3
3
  import { AppError } from "../shared/errors.js";
4
4
  import { AdmittedFrozenEngine } from "./frozen-engine.js";
5
5
  import { memorySourceKey } from "./memory-stage-results.js";
6
+ import { parseMemoryWorkResultProposal } from "./memory-work-result-json.js";
6
7
  import { memoryReviewWorkerSchema } from "./memory-review-registration.js";
7
8
  function refuse(reason) {
8
9
  throw new AppError("memory_review_work_result_invalid", "Reviewer result lacks assigned unit and retained input evidence", 2, { reason, effect: "no_effect" });
@@ -16,12 +17,16 @@ export function validateMemoryReviewWorkResult(input, engine, binding) {
16
17
  if (!(input instanceof Uint8Array) || !input.byteLength || input.byteLength > 1024 * 1024
17
18
  || !(engine instanceof AdmittedFrozenEngine) || !binding.workId || binding.packet?.result_schema !== memoryReviewWorkerSchema)
18
19
  refuse("invalid_preparation_binding");
19
- const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input));
20
+ const value = parseMemoryWorkResultProposal(input, "memory_review_work_result_invalid");
20
21
  const schema = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(engine.schema("memory-review-work-result")));
21
22
  if (schema.$id !== memoryReviewWorkerSchema)
22
23
  refuse("invalid_result_schema");
23
24
  const packet = binding.packet;
24
- if (value.schema_id !== memoryReviewWorkerSchema || value.run_id !== packet.run_id || value.work_id !== binding.workId || value.packet_sha256 !== packet.packet_sha256
25
+ // A malformed checksum is a correctable proposal shape, not proof that a
26
+ // valid foreign packet was selected. Never accept either mismatch.
27
+ const foreignPacket = typeof value.packet_sha256 === "string" && /^[a-f0-9]{64}$/.test(value.packet_sha256)
28
+ && value.packet_sha256 !== packet.packet_sha256;
29
+ if (value.schema_id !== memoryReviewWorkerSchema || value.run_id !== packet.run_id || value.work_id !== binding.workId || foreignPacket
25
30
  || value.unit_id !== packet.unit.id || value.wave !== packet.wave)
26
31
  refuse("foreign_result_identity");
27
32
  const validate = new Ajv2020({ strict: true, validateFormats: false }).compile(schema);
@@ -33,14 +38,31 @@ export function validateMemoryReviewWorkResult(input, engine, binding) {
33
38
  const evidence = (refs) => {
34
39
  for (const ref of refs) {
35
40
  if (!sources.has(`${memorySourceKey(ref.root, ref.path)}\0${ref.sha256}`))
36
- refuse("unregistered_or_duplicate_evidence");
41
+ throw new AppError("memory_review_work_result_invalid", "Cite only the retained references assigned to this reviewer", 2, { reason: "unregistered_or_duplicate_evidence", phase: "prepare", effect: "no_effect", recoverable: true,
42
+ unit_id: packet.unit.id, reference: structuredClone(ref),
43
+ hint: "Correct this citation using assigned packet references, or remove the extra citation. Do not change the packet, accepted inputs or assigned scope.",
44
+ result_sha256: createHash("sha256").update(input).digest("hex") });
37
45
  }
38
46
  };
39
47
  const nonblank = (text) => Boolean(text.trim()) && !text.includes("\0");
40
48
  if (!nonblank(value.summary))
41
49
  refuse("missing_assessment");
42
50
  evidence(value.evidence);
43
- const outputs = packet.unit.artifacts.map(ref => ({ root: "workspace", path: ref.path, sha256: ref.sha256 }));
51
+ if ("preservation_invariants" in packet && packet.preservation_invariants) {
52
+ for (const invariant of packet.preservation_invariants) {
53
+ const old = invariant.evidence;
54
+ const refs = old.flatMap(source => {
55
+ const path = source.root === "canon" ? `.memory-bank/${source.path}` : source.path;
56
+ const retained = packet.historical_inputs?.filter(entry => entry.source.path === path && entry.source.sha256 === source.sha256).map(entry => entry.retained) ?? [];
57
+ return retained.length ? retained : packet.unit_inputs.filter(ref => ref.path === path && ref.sha256 === source.sha256);
58
+ });
59
+ if (!refs.length || !value.evidence.some(ref => refs.some(old => old.root === ref.root && old.path === ref.path && old.sha256 === ref.sha256)))
60
+ throw new AppError("memory_review_work_result_invalid", "Examine and cite the assigned old document evidence as well as current outputs", 2, { reason: "preservation_review_without_old_evidence", phase: "prepare", effect: "no_effect", recoverable: true,
61
+ invariant_id: invariant.id, old_references: structuredClone(refs), result_sha256: createHash("sha256").update(input).digest("hex") });
62
+ }
63
+ }
64
+ const outputs = packet.unit.artifacts.map(ref => ({ root: ref.root === "run" ? "run" : "workspace",
65
+ path: ref.root === "canon" ? `.memory-bank/${ref.path}` : ref.path, sha256: ref.sha256 }));
44
66
  if (!value.evidence.some(ref => outputs.some(output => ref.root === output.root && ref.path === output.path && ref.sha256 === output.sha256))) {
45
67
  const corrected = "deleted_references" in packet;
46
68
  if (!corrected || packet.schema_id !== "dd-flow/memory-review-work-packet@2" || ![1, 2].includes(packet.wave)
@@ -8,6 +8,7 @@ import { memorySourceKey } from "./memory-stage-results.js";
8
8
  import { assertPreparedRunBundleSqlAuthority } from "./runs.js";
9
9
  import { reviewSourceHash } from "./review-copy.js";
10
10
  import { RetainedExecutionBundle } from "./execution-bundles.js";
11
+ import { memoryReviewViewWithinLimit } from "./memory-review-limits.js";
11
12
  const token = Symbol("second-corrected-memory-review-inputs");
12
13
  function refuse(reason) {
13
14
  throw new AppError("memory_second_corrected_review_input_invalid", "Final re-review lacks settled second repairs and independent first-review custody", 2, { reason, effect: "no_effect" });
@@ -22,7 +23,7 @@ export function secondCorrectedMemoryReviewAssignments(input) {
22
23
  refuse("invalid_first_unit_projection");
23
24
  const firstAssignments = new Map(input.first.assignments.map(assignment => [assignment.unit.id, assignment]));
24
25
  const units = memoryCorrectionCurrentUnits(input.first.unit_inventory, input.first.assignments);
25
- const assignments = correctedMemoryReviewAssignments({ units, affected: input.affected, files: input.files, repairs: input.repairs });
26
+ const assignments = correctedMemoryReviewAssignments({ units, initialAssignments: input.first.assignments, affected: input.affected, files: input.files, repairs: input.repairs });
26
27
  const available = new Map(input.files.map(file => [file.path, file]));
27
28
  for (const assignment of assignments) {
28
29
  for (const deleted of firstAssignments.get(assignment.unit.id)?.deleted_references ?? []) {
@@ -121,7 +122,7 @@ export class PreparedSecondCorrectedMemoryReviewInputs {
121
122
  return prepared;
122
123
  }
123
124
  static prepareFromBundle(context, input) {
124
- if (!(input.bundle instanceof RetainedExecutionBundle) || input.bundle.descriptor.descriptor.id !== "mb-init")
125
+ if (!(input.bundle instanceof RetainedExecutionBundle) || !["mb-init", "mb-upgrade"].includes(input.bundle.descriptor.descriptor.id))
125
126
  refuse("registered_memory_bundle_required");
126
127
  const registration = PreparedRetainedSecondMemoryRepairs.prepare(context, input);
127
128
  return this.prepare(context, PreparedCompletedMemoryCorrectionWave.prepare(context, registration));
@@ -158,8 +159,9 @@ function prepareView(context, wave) {
158
159
  first_review_registration_sha256: receipt.first_review_registration_sha256,
159
160
  source_policy: "review-inputs@1", source_sha256: reviewSourceHash(workspace, runInputs.filter(ref => ref.root === "workspace").map(ref => ref.path), first.base),
160
161
  base: first.base, run_inputs: runInputs, unit_inventory: units, affected_unit_ids: wave.affectedUnitIds(), assignments,
162
+ ...(first.upgrade_baseline ? { upgrade_baseline: structuredClone(first.upgrade_baseline), preservation_invariants: structuredClone(first.preservation_invariants ?? []) } : {}),
161
163
  instruction: structuredClone(first.instruction), repairs: repairs.map(repair => ({ work_id: repair.work_id, authority_sha256: repair.authority_sha256, result: repair.reference })) };
162
- if (Buffer.byteLength(JSON.stringify(view)) > 1024 * 1024)
164
+ if (!memoryReviewViewWithinLimit(view))
163
165
  refuse("unbounded_second_view");
164
166
  return view;
165
167
  }
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { AppError } from "../shared/errors.js";
3
3
  import { PreparedCorrectedMemoryReviewAcceptance } from "./memory-corrected-review-acceptance.js";
4
- import { validateMemoryReviewCorrectionPlan } from "./memory-review-correction-plan.js";
4
+ import { validateMemoryReviewCorrectionPlan, memoryRepairCanonicalPaths } from "./memory-review-correction-plan.js";
5
5
  import { PreparedHistoricalOriginalMemoryReview } from "./memory-historical-original-review.js";
6
6
  import { PreparedHistoricalCorrectedMemoryReview } from "./memory-historical-corrected-review.js";
7
7
  import { hybridDigest } from "./hybrid-contracts.js";
@@ -45,6 +45,7 @@ export class PreparedMemorySecondCorrectionPlan {
45
45
  const units = view.unit_inventory.map(unit => assignments.get(unit.id) ?? unit);
46
46
  const bundle = registration.inputs().bundle();
47
47
  const value = validateMemoryReviewCorrectionPlan(bytes, bundle.engine(), { runId: view.scope.run_id, wave: 2,
48
+ canonicalPaths: memoryRepairCanonicalPaths(context, bundle),
48
49
  reviewSha256: review.sha256, units, findings: projection.findings });
49
50
  for (const work of value.works)
50
51
  bundle.execution.work("review", work.profile_id ?? undefined);
@@ -11,7 +11,7 @@ import { readDescriptorCoordinatorSession } from "./descriptor-coordinator-sessi
11
11
  import { reviewSourceHash } from "./review-copy.js";
12
12
  import { runtimeHarness } from "./execution-policy.js";
13
13
  import { appendAudit } from "./audit.js";
14
- import { memoryRepairCurrentReferences, validateMemoryRepairWorkResult } from "./memory-repair-work-results.js";
14
+ import { isCompletedMemoryRepairClaim, memoryRepairCurrentReferences, validateMemoryRepairWorkResult } from "./memory-repair-work-results.js";
15
15
  import { RetainedMemoryRepairPublicationPlan } from "./memory-repair-publication-plan.js";
16
16
  import { retainedMemoryRepairPublicationPlans } from "./memory-repair-publication-intent.js";
17
17
  import { deriveRetainedMemoryRepairViews } from "./memory-repair-publication.js";
@@ -97,7 +97,7 @@ export class PreparedSecondMemoryRepairLaunch {
97
97
  static #prepare(context, input, workSessionId) {
98
98
  if (!PreparedRetainedSecondMemoryRepairs.isPrepared(input.registration) || !PreparedRetainedSecondMemoryBaseline.isPrepared(input.baseline))
99
99
  refuse("retained_second_owners_required");
100
- if (!(input.bundle instanceof RetainedExecutionBundle) || input.bundle.descriptor.descriptor.id !== "mb-init")
100
+ if (!(input.bundle instanceof RetainedExecutionBundle) || !["mb-init", "mb-upgrade"].includes(input.bundle.descriptor.descriptor.id))
101
101
  refuse("registered_memory_bundle_required");
102
102
  if (context.db.isOwnedWriteTransaction?.())
103
103
  refuse("preparation_outside_writer_required");
@@ -204,7 +204,7 @@ export class PreparedSecondMemoryRepairLaunch {
204
204
  * settled peer views and partial publication recovery require other owners. */
205
205
  reservePublication(context, bytes) {
206
206
  const observed = this.observeOutput(context, bytes), receipt = this.#baseline.receipt();
207
- if (observed.result.outcome !== "accepted")
207
+ if (!isCompletedMemoryRepairClaim(observed.result))
208
208
  refuse("accepted_private_output_required");
209
209
  const before = this.#baseline.baseline(), exported = this.#baseline.exportedBaseline();
210
210
  this.#workspace.verifySelectedSourceInputs(receipt.workspace_root, this.#baseline.copyInput(), before);
@@ -294,7 +294,7 @@ export class PreparedSecondMemoryRepairLaunch {
294
294
  const packet = assigned.payload.memory_repair;
295
295
  const result = validateMemoryRepairWorkResult(bytes, this.#bundle.engine(), { workId: receipt.work_id, packet,
296
296
  effectiveInputs: memoryRepairCurrentReferences(packet, this.#workspace) });
297
- if (result.outcome !== "accepted")
297
+ if (!isCompletedMemoryRepairClaim(result))
298
298
  refuse("accepted_reserved_result_required");
299
299
  const views = deriveRetainedMemoryRepairViews(this.#baseline.baseline(), this.#baseline.exportedBaseline(), plan, result, privatePlan);
300
300
  const outputSha256 = hybridDigest({ input: this.input(), start_receipt_sha256: value.start_receipt_sha256,
@@ -56,7 +56,7 @@ export class PreparedSecondMemoryRepairSource {
56
56
  static #prepare(context, input, current) {
57
57
  if (!PreparedRetainedSecondMemoryRepairs.isPrepared(input.registration))
58
58
  refuse("retained_second_registration_required");
59
- if (!(input.bundle instanceof RetainedExecutionBundle) || input.bundle.descriptor.descriptor.id !== "mb-init")
59
+ if (!(input.bundle instanceof RetainedExecutionBundle) || !["mb-init", "mb-upgrade"].includes(input.bundle.descriptor.descriptor.id))
60
60
  refuse("registered_memory_bundle_required");
61
61
  if (context.db.isOwnedWriteTransaction?.())
62
62
  refuse("preparation_outside_writer_required");