@deksden-com/dd-flow-cli 0.9.0-beta.136 → 0.9.0-beta.138

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 (230) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +9 -0
  3. package/dist/build-info.json +6 -6
  4. package/dist/cli/argument-values.js +5 -5
  5. package/dist/cli/command-inputs.js +89 -20
  6. package/dist/cli/command-metadata.js +56 -3
  7. package/dist/cli/command-option-descriptions.js +5 -0
  8. package/dist/cli/executor-onboarding.js +8 -3
  9. package/dist/cli/flows.js +29 -0
  10. package/dist/cli/help.js +240 -6
  11. package/dist/cli/input-preparation.js +54 -5
  12. package/dist/cli/memory-flow-admission.js +44 -0
  13. package/dist/cli/memory-init-invocation.js +78 -0
  14. package/dist/cli/memory-init.js +240 -0
  15. package/dist/cli/memory-storage-admission.js +53 -0
  16. package/dist/cli/native-hook-ingress.js +1 -1
  17. package/dist/cli/response-file.js +2 -0
  18. package/dist/cli/run-cli.js +283 -160
  19. package/dist/cli/setup.js +9 -7
  20. package/dist/cli.js +54 -2
  21. package/dist/domain/stage-handler-identities.js +20 -0
  22. package/dist/harness-runtime/DD-ZCODE.md +13 -0
  23. package/dist/harness-runtime/bin/dd-opencode.mjs +3 -3
  24. package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
  25. package/dist/harness-runtime/lib/daemon-operations.mjs +4 -3
  26. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +4 -2
  27. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +11 -5
  28. package/dist/harness-runtime/lib/dd-codex.mjs +22 -10
  29. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +2 -1
  30. package/dist/harness-runtime/lib/dd-droid.mjs +2 -1
  31. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +4 -2
  32. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +20 -5
  33. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +45 -10
  34. package/dist/harness-runtime/lib/dd-zcode.mjs +7 -3
  35. package/dist/harness-runtime/lib/delegation-instructions.mjs +5 -3
  36. package/dist/harness-runtime/lib/lifecycle-command.mjs +1 -0
  37. package/dist/harness-runtime/lib/managed-daemon.d.mts +1 -0
  38. package/dist/harness-runtime/lib/managed-daemon.mjs +50 -15
  39. package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
  40. package/dist/harness-runtime/lib/native-hook-command.mjs +1 -1
  41. package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
  42. package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
  43. package/dist/harness-runtime/lib/opencode-plugin.mjs +5 -1
  44. package/dist/harness-runtime/lib/operation-progress.mjs +3 -1
  45. package/dist/runtime/context.js +1 -1
  46. package/dist/schemas/discovery-result.schema.json +184 -0
  47. package/dist/schemas/execution-bundle.schema.json +205 -0
  48. package/dist/schemas/execution-settings.schema.json +162 -0
  49. package/dist/schemas/flow-descriptor.schema.json +54 -0
  50. package/dist/schemas/flow-run.schema.json +22 -0
  51. package/dist/schemas/harness-catalog.schema.json +80 -0
  52. package/dist/schemas/mb-init-compose-result.schema.json +457 -0
  53. package/dist/schemas/mb-init-discover-result.schema.json +500 -0
  54. package/dist/schemas/mb-init-finish-result.schema.json +422 -0
  55. package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
  56. package/dist/schemas/mb-init-review-result.schema.json +396 -0
  57. package/dist/schemas/mb-init-verify-result.schema.json +391 -0
  58. package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
  59. package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
  60. package/dist/schemas/memory-composition-work-result.schema.json +38 -0
  61. package/dist/schemas/memory-repair-work-result.schema.json +49 -0
  62. package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
  63. package/dist/schemas/memory-review-work-result.schema.json +43 -0
  64. package/dist/schemas/model-catalog.schema.json +153 -0
  65. package/dist/schemas/profile-settings.schema.json +122 -0
  66. package/dist/schemas/release-manifest.schema.json +291 -0
  67. package/dist/schemas/run-engine-binding.schema.json +1 -1
  68. package/dist/services/check-runner-evidence.js +51 -4
  69. package/dist/services/cli-operation-classifier.js +8 -47
  70. package/dist/services/code-checks.js +387 -34
  71. package/dist/services/controller-child-observer.js +99 -0
  72. package/dist/services/controller-execution.js +88 -0
  73. package/dist/services/controller-fanout.js +79 -13
  74. package/dist/services/dashboard.js +8 -2
  75. package/dist/services/descriptor-coordinator-session.js +35 -0
  76. package/dist/services/descriptor-root-work.js +53 -0
  77. package/dist/services/descriptor-run-settlement.js +105 -0
  78. package/dist/services/descriptor-run-start.js +449 -0
  79. package/dist/services/descriptor-stage-coordinator.js +197 -0
  80. package/dist/services/descriptor-stage-settlement.js +222 -0
  81. package/dist/services/descriptor-stage-start.js +139 -0
  82. package/dist/services/engines.js +39 -22
  83. package/dist/services/eval-snapshots.js +83 -25
  84. package/dist/services/execution-bundles.js +263 -0
  85. package/dist/services/execution-policy.js +20 -16
  86. package/dist/services/external-work-launch.js +91 -11
  87. package/dist/services/flow-catalog.js +21 -0
  88. package/dist/services/flow-descriptors.js +82 -0
  89. package/dist/services/frozen-engine.js +242 -0
  90. package/dist/services/generic-execution.js +164 -0
  91. package/dist/services/harness-adapter.js +28 -2
  92. package/dist/services/hooks.js +24 -6
  93. package/dist/services/hybrid-contracts.js +31 -3
  94. package/dist/services/ids.js +29 -1
  95. package/dist/services/installation-lock.js +7 -4
  96. package/dist/services/installed-flow-source.js +183 -0
  97. package/dist/services/lifecycle-invocations.js +257 -78
  98. package/dist/services/memory-accepted-composition.js +77 -0
  99. package/dist/services/memory-baseline-preservation.js +65 -0
  100. package/dist/services/memory-completed-correction-wave.js +320 -0
  101. package/dist/services/memory-compose-handler.js +258 -0
  102. package/dist/services/memory-composition-acceptance.js +199 -0
  103. package/dist/services/memory-composition-graph.js +134 -0
  104. package/dist/services/memory-composition-registration.js +146 -0
  105. package/dist/services/memory-composition-work-custody.js +151 -0
  106. package/dist/services/memory-composition-work-results.js +96 -0
  107. package/dist/services/memory-corrected-review-acceptance.js +129 -0
  108. package/dist/services/memory-corrected-review-batch.js +30 -0
  109. package/dist/services/memory-corrected-review-findings.js +106 -0
  110. package/dist/services/memory-corrected-review-inputs.js +210 -0
  111. package/dist/services/memory-corrected-review-projection.js +78 -0
  112. package/dist/services/memory-corrected-review-receipt.js +170 -0
  113. package/dist/services/memory-corrected-review-registration.js +191 -0
  114. package/dist/services/memory-correction-check-receipt.js +219 -0
  115. package/dist/services/memory-correction-input-owner.js +6 -0
  116. package/dist/services/memory-discover-handler.js +104 -0
  117. package/dist/services/memory-finish-evidence.js +435 -0
  118. package/dist/services/memory-finish-handler.js +215 -0
  119. package/dist/services/memory-finish-integration.js +280 -0
  120. package/dist/services/memory-finish-readiness.js +122 -0
  121. package/dist/services/memory-git-integration.js +151 -0
  122. package/dist/services/memory-historical-corrected-review.js +206 -0
  123. package/dist/services/memory-historical-original-review.js +130 -0
  124. package/dist/services/memory-historical-repair-registration.js +99 -0
  125. package/dist/services/memory-historical-second-review.js +128 -0
  126. package/dist/services/memory-init-bootstrap-readiness.js +210 -0
  127. package/dist/services/memory-init-contexts.js +89 -0
  128. package/dist/services/memory-init-controller-start.js +84 -0
  129. package/dist/services/memory-init-engine-release.js +223 -0
  130. package/dist/services/memory-init-git-policy.js +136 -0
  131. package/dist/services/memory-init-lint-capability.js +185 -0
  132. package/dist/services/memory-init-native-capacity.js +241 -0
  133. package/dist/services/memory-init-preflight.js +312 -0
  134. package/dist/services/memory-init-project-bootstrap.js +599 -0
  135. package/dist/services/memory-init-request-candidate.js +157 -0
  136. package/dist/services/memory-init-request-input.js +66 -0
  137. package/dist/services/memory-init-resource-storage.js +125 -0
  138. package/dist/services/memory-init-run-allocation.js +81 -0
  139. package/dist/services/memory-init-run-bundle.js +98 -0
  140. package/dist/services/memory-init-start.js +175 -0
  141. package/dist/services/memory-init-startup-custody.js +80 -0
  142. package/dist/services/memory-init-startup-input.js +240 -0
  143. package/dist/services/memory-init-storage.js +81 -0
  144. package/dist/services/memory-init-stores.js +359 -0
  145. package/dist/services/memory-init-worktrunk-capability.js +172 -0
  146. package/dist/services/memory-layer-publication.js +611 -0
  147. package/dist/services/memory-layer.js +156 -0
  148. package/dist/services/memory-lint-config.js +85 -0
  149. package/dist/services/memory-native-review-copy.js +165 -0
  150. package/dist/services/memory-normal-flow-entry.js +75 -0
  151. package/dist/services/memory-ordinary-defaults.js +112 -0
  152. package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
  153. package/dist/services/memory-preflight-handler.js +259 -0
  154. package/dist/services/memory-repair-current-view.js +299 -0
  155. package/dist/services/memory-repair-document-delta.js +451 -0
  156. package/dist/services/memory-repair-history.js +491 -0
  157. package/dist/services/memory-repair-physical-binding.js +312 -0
  158. package/dist/services/memory-repair-publication-files.js +152 -0
  159. package/dist/services/memory-repair-publication-intent.js +27 -0
  160. package/dist/services/memory-repair-publication-plan.js +160 -0
  161. package/dist/services/memory-repair-publication-source.js +225 -0
  162. package/dist/services/memory-repair-publication.js +794 -0
  163. package/dist/services/memory-repair-registration.js +239 -0
  164. package/dist/services/memory-repair-work-baseline.js +413 -0
  165. package/dist/services/memory-repair-work-completion.js +97 -0
  166. package/dist/services/memory-repair-work-custody.js +73 -0
  167. package/dist/services/memory-repair-work-results.js +89 -0
  168. package/dist/services/memory-repair-workspace.js +252 -0
  169. package/dist/services/memory-repair-writer-copy.js +735 -0
  170. package/dist/services/memory-result-binding.js +173 -0
  171. package/dist/services/memory-retained-review-authority.js +129 -0
  172. package/dist/services/memory-retained-review-results.js +70 -0
  173. package/dist/services/memory-retained-second-baseline.js +102 -0
  174. package/dist/services/memory-retained-second-repairs.js +156 -0
  175. package/dist/services/memory-review-acceptance.js +214 -0
  176. package/dist/services/memory-review-correction-plan.js +171 -0
  177. package/dist/services/memory-review-handler.js +585 -0
  178. package/dist/services/memory-review-inputs.js +200 -0
  179. package/dist/services/memory-review-receipt.js +180 -0
  180. package/dist/services/memory-review-registration.js +108 -0
  181. package/dist/services/memory-review-work-custody.js +124 -0
  182. package/dist/services/memory-review-work-input.js +72 -0
  183. package/dist/services/memory-review-work-results.js +73 -0
  184. package/dist/services/memory-second-corrected-review-inputs.js +165 -0
  185. package/dist/services/memory-second-correction-plan.js +76 -0
  186. package/dist/services/memory-second-repair-baseline.js +91 -0
  187. package/dist/services/memory-second-repair-launch.js +614 -0
  188. package/dist/services/memory-second-repair-registration.js +88 -0
  189. package/dist/services/memory-second-repair-source.js +187 -0
  190. package/dist/services/memory-second-review-acceptance.js +115 -0
  191. package/dist/services/memory-source-observations.js +117 -0
  192. package/dist/services/memory-stage-dispatch.js +28 -0
  193. package/dist/services/memory-stage-results.js +199 -0
  194. package/dist/services/memory-verification-acceptance.js +135 -0
  195. package/dist/services/memory-verification-checks.js +252 -0
  196. package/dist/services/memory-verification-receipt.js +159 -0
  197. package/dist/services/memory-verify-handler.js +135 -0
  198. package/dist/services/memory-work-result-preparation.js +63 -0
  199. package/dist/services/projects.js +17 -2
  200. package/dist/services/recovery-snapshot-database.js +416 -1
  201. package/dist/services/repair-intents.js +1 -1
  202. package/dist/services/review-copy.js +10 -2
  203. package/dist/services/run-control.js +80 -9
  204. package/dist/services/run-controller-capture.js +10 -3
  205. package/dist/services/run-controller-process.js +21 -0
  206. package/dist/services/run-controller-recovery.js +3 -1
  207. package/dist/services/run-controller-state.js +1 -1
  208. package/dist/services/run-controller.js +184 -48
  209. package/dist/services/run-recovery-guard.js +2 -0
  210. package/dist/services/run-recovery.js +145 -5
  211. package/dist/services/run-start-custody.js +177 -0
  212. package/dist/services/runs.js +319 -25
  213. package/dist/services/stage-context.js +9 -1
  214. package/dist/services/stage-handlers.js +74 -0
  215. package/dist/services/stage-lifecycle.js +2 -1
  216. package/dist/services/stage-report-renderer.js +76 -0
  217. package/dist/services/stage-work-graph.js +17 -0
  218. package/dist/services/vnext-code.js +3 -3
  219. package/dist/services/vnext-execution-profile.js +2 -2
  220. package/dist/services/vnext-fanout.js +136 -10
  221. package/dist/services/vnext-merge.js +8 -3
  222. package/dist/services/vnext-plan-review.js +6 -4
  223. package/dist/services/vnext-protocolize.js +60 -2
  224. package/dist/services/work-registry.js +616 -71
  225. package/dist/services/workspace-files.js +1 -1
  226. package/dist/storage/database.js +84 -6
  227. package/dist/storage/writer-contract.js +11 -7
  228. package/package.json +13 -3
  229. package/tools/create-canon-release.mjs +33 -0
  230. package/tools/produce-hybrid-release.mjs +41 -0
@@ -0,0 +1,72 @@
1
+ import { AppError } from "../shared/errors.js";
2
+ import { readFlowRunExecutionBundle } from "./runs.js";
3
+ import { PreparedMemoryInitialReviewInputs } from "./memory-review-inputs.js";
4
+ import { readRegisteredMemoryReview, memoryReviewBatch, memoryReviewWorkerSchema } from "./memory-review-registration.js";
5
+ import { hybridDigest } from "./hybrid-contracts.js";
6
+ import { PreparedCorrectedMemoryReviewInputs } from "./memory-corrected-review-inputs.js";
7
+ import { PreparedSecondCorrectedMemoryReviewInputs } from "./memory-second-corrected-review-inputs.js";
8
+ import { PreparedMemoryCorrectionCheckReceipt } from "./memory-correction-check-receipt.js";
9
+ import { PreparedRegisteredCorrectedMemoryReview } from "./memory-corrected-review-registration.js";
10
+ const preparedOwners = new WeakMap();
11
+ function workScope(work) { return hybridDigest([work.work_id, work.project_id, work.run_id, work.parent_work_id, work.payload_json, work.result_schema, work.launch_policy]); }
12
+ export function assertPreparedMemoryReviewWorkInput(context, owner, work, run, generation) {
13
+ const issued = preparedOwners.get(owner);
14
+ if (!issued || issued.store !== context.db || issued.work !== workScope(work) || issued.root !== run.project_root || issued.generation !== generation || issued.input !== hybridDigest(owner.input))
15
+ refuse("foreign_prepared_review_input");
16
+ owner.assertCustody(context);
17
+ }
18
+ function refuse(reason) {
19
+ throw new AppError("memory_review_input_invalid", "Memory reviewer Work lacks its independently registered input", 2, { reason, effect: "no_effect" });
20
+ }
21
+ /** Physical preparation outside a writer. The SQL-only closure must also be
22
+ * consumed by Work claim, not just by an external launch/copy precheck.
23
+ */
24
+ export function prepareMemoryReviewWorkInput(context, work, run, generation) {
25
+ const bundle = readFlowRunExecutionBundle(context, { projectRoot: run.project_root, runId: work.run_id });
26
+ if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
27
+ refuse("registered_memory_bundle_required");
28
+ let payload;
29
+ try {
30
+ payload = JSON.parse(work.payload_json ?? "null");
31
+ }
32
+ catch {
33
+ return refuse("invalid_review_payload");
34
+ }
35
+ const packet = payload?.memory_review;
36
+ if (!payload || payload.kind !== "memory-review" || payload.read_only !== true
37
+ || !((packet?.schema_id === "dd-flow/memory-review-work-packet@1" && packet.wave === 0)
38
+ || (packet?.schema_id === "dd-flow/memory-review-work-packet@2" && (packet.wave === 1 || packet.wave === 2))))
39
+ refuse("invalid_review_payload");
40
+ const scope = { projectId: work.project_id, runId: work.run_id };
41
+ const corrected = packet.wave === 2 ? PreparedSecondCorrectedMemoryReviewInputs.prepareFromBundle(context, { scope, bundle })
42
+ : packet.wave === 1 ? PreparedCorrectedMemoryReviewInputs.prepareFromBundle(context, { scope, bundle }) : null;
43
+ const registration = corrected ? PreparedRegisteredCorrectedMemoryReview.restore(context, corrected, PreparedMemoryCorrectionCheckReceipt.restore(context, corrected)) : null;
44
+ const owner = corrected ?? PreparedMemoryInitialReviewInputs.prepare(context, { scope, bundle });
45
+ const view = owner.value(), receipt = registration ? registration.receipt() : readRegisteredMemoryReview(context, view.scope, view.wave);
46
+ if (receipt.input_sha256 !== owner.sha256 || work.parent_work_id !== view.scope.parent_work_id
47
+ || work.result_schema !== memoryReviewWorkerSchema || work.launch_policy !== "fresh_agent_required")
48
+ refuse("foreign_review_assignment");
49
+ const batch = registration ? registration.batch() : memoryReviewBatch(owner.value());
50
+ const assigned = batch.works.find(item => receipt.work_ids[item.key] === work.work_id);
51
+ if (!assigned || !payload || payload.kind !== "memory-review" || payload.read_only !== true
52
+ || hybridDigest(payload.memory_review) !== hybridDigest(assigned.payload.memory_review))
53
+ refuse("changed_review_assignment");
54
+ const receiptSha256 = hybridDigest(receipt);
55
+ const assertCustody = (writer) => {
56
+ owner.assertCustody(writer);
57
+ if (registration)
58
+ registration.assertCustody(writer);
59
+ else if (hybridDigest(readRegisteredMemoryReview(writer, view.scope, view.wave)) !== receiptSha256)
60
+ refuse("review_registration_changed");
61
+ };
62
+ assertCustody(context);
63
+ const input = { schema_id: "dd-flow/review-input@3", parent_work_id: view.scope.parent_work_id,
64
+ kind: "memory-review", cycle: `memory-review-wave-${view.wave}`, generation,
65
+ workspace_fingerprint: view.source_sha256, source_policy: "review-inputs@1",
66
+ source_sha256: view.source_sha256, run_inputs: structuredClone(view.run_inputs) };
67
+ const prepared = Object.freeze({ input, assertCustody, bundle,
68
+ registration: () => structuredClone(receipt), packet: () => structuredClone(assigned.payload.memory_review),
69
+ revalidate: (reader) => { owner.revalidate(reader); registration?.revalidate(reader); assertCustody(reader); } });
70
+ preparedOwners.set(prepared, { store: context.db, work: workScope(work), root: run.project_root, generation, input: hybridDigest(input) });
71
+ return prepared;
72
+ }
@@ -0,0 +1,73 @@
1
+ import { Ajv2020 } from "ajv/dist/2020.js";
2
+ import { createHash } from "node:crypto";
3
+ import { AppError } from "../shared/errors.js";
4
+ import { AdmittedFrozenEngine } from "./frozen-engine.js";
5
+ import { memorySourceKey } from "./memory-stage-results.js";
6
+ import { memoryReviewWorkerSchema } from "./memory-review-registration.js";
7
+ function refuse(reason) {
8
+ throw new AppError("memory_review_work_result_invalid", "Reviewer result lacks assigned unit and retained input evidence", 2, { reason, effect: "no_effect" });
9
+ }
10
+ /** Frozen schema and claim admission only. The registered Work owner selects
11
+ * the packet/references and must still prove actual independent Session,
12
+ * unchanged copy, generation and atomic settlement. This executes no checks.
13
+ */
14
+ export function validateMemoryReviewWorkResult(input, engine, binding) {
15
+ try {
16
+ if (!(input instanceof Uint8Array) || !input.byteLength || input.byteLength > 1024 * 1024
17
+ || !(engine instanceof AdmittedFrozenEngine) || !binding.workId || binding.packet?.result_schema !== memoryReviewWorkerSchema)
18
+ refuse("invalid_preparation_binding");
19
+ const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input));
20
+ const schema = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(engine.schema("memory-review-work-result")));
21
+ if (schema.$id !== memoryReviewWorkerSchema)
22
+ refuse("invalid_result_schema");
23
+ 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
+ || value.unit_id !== packet.unit.id || value.wave !== packet.wave)
26
+ refuse("foreign_result_identity");
27
+ const validate = new Ajv2020({ strict: true, validateFormats: false }).compile(schema);
28
+ if (!validate(value))
29
+ throw new AppError("memory_review_work_result_invalid", "Reviewer result proposal does not match its schema; correct the reported fields using the assigned packet references", 2, { reason: "invalid_result_schema", phase: "prepare", effect: "no_effect", recoverable: true,
30
+ errors: structuredClone(validate.errors), result_sha256: createHash("sha256").update(input).digest("hex") });
31
+ const sources = new Set([...binding.references, ...packet.unit.evidence.map(ref => ({ root: ref.root === 'run' ? 'run' : 'workspace',
32
+ path: ref.root === 'canon' ? `.memory-bank/${ref.path}` : ref.path, sha256: ref.sha256 }))].map(ref => `${memorySourceKey(ref.root, ref.path)}\0${ref.sha256}`));
33
+ const evidence = (refs) => {
34
+ for (const ref of refs) {
35
+ if (!sources.has(`${memorySourceKey(ref.root, ref.path)}\0${ref.sha256}`))
36
+ refuse("unregistered_or_duplicate_evidence");
37
+ }
38
+ };
39
+ const nonblank = (text) => Boolean(text.trim()) && !text.includes("\0");
40
+ if (!nonblank(value.summary))
41
+ refuse("missing_assessment");
42
+ evidence(value.evidence);
43
+ const outputs = packet.unit.artifacts.map(ref => ({ root: "workspace", path: ref.path, sha256: ref.sha256 }));
44
+ if (!value.evidence.some(ref => outputs.some(output => ref.root === output.root && ref.path === output.path && ref.sha256 === output.sha256))) {
45
+ const corrected = "deleted_references" in packet;
46
+ if (!corrected || packet.schema_id !== "dd-flow/memory-review-work-packet@2" || ![1, 2].includes(packet.wave)
47
+ || outputs.length || value.outcome === "accepted" || !packet.deleted_references.some(deleted => deleted.role === "artifact"
48
+ && value.evidence.some(ref => ref.root === "run" && deleted.repair_result.root === "run"
49
+ && deleted.repair_result.path === ref.path && deleted.repair_result.sha256 === ref.sha256)))
50
+ refuse("assessment_without_unit_output");
51
+ }
52
+ const findingIds = new Set();
53
+ for (const finding of value.findings) {
54
+ if (finding.unit_id !== packet.unit.id || findingIds.has(finding.id))
55
+ refuse("foreign_or_duplicate_finding");
56
+ findingIds.add(finding.id);
57
+ if (!nonblank(finding.summary))
58
+ refuse("missing_finding_problem");
59
+ evidence(finding.evidence);
60
+ if (!finding.evidence.some(ref => packet.unit_inputs.some(source => ref.root === source.root && ref.path === source.path && ref.sha256 === source.sha256)))
61
+ refuse("finding_without_unit_source");
62
+ }
63
+ const material = value.findings.some(finding => finding.severity !== "minor");
64
+ if (value.outcome === "accepted" && material || value.outcome === "needs_repair" && !material)
65
+ refuse("contradictory_review_outcome");
66
+ return structuredClone(value);
67
+ }
68
+ catch (error) {
69
+ if (error instanceof AppError && ["memory_review_work_result_invalid", "frozen_engine_invalid"].includes(error.code))
70
+ throw error;
71
+ return refuse("invalid_result_or_binding");
72
+ }
73
+ }
@@ -0,0 +1,165 @@
1
+ import { AppError } from "../shared/errors.js";
2
+ import { hybridDigest } from "./hybrid-contracts.js";
3
+ import { PreparedCompletedMemoryCorrectionWave, memoryCorrectionCurrentUnits } from "./memory-completed-correction-wave.js";
4
+ import { PreparedRetainedSecondMemoryRepairs } from "./memory-retained-second-repairs.js";
5
+ import { correctedMemoryReviewAssignments } from "./memory-corrected-review-inputs.js";
6
+ import { ObservedMemorySources } from "./memory-source-observations.js";
7
+ import { memorySourceKey } from "./memory-stage-results.js";
8
+ import { assertPreparedRunBundleSqlAuthority } from "./runs.js";
9
+ import { reviewSourceHash } from "./review-copy.js";
10
+ import { RetainedExecutionBundle } from "./execution-bundles.js";
11
+ const token = Symbol("second-corrected-memory-review-inputs");
12
+ function refuse(reason) {
13
+ 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" });
14
+ }
15
+ /** Metadata projection only; caller must establish first/second custody. */
16
+ export function secondCorrectedMemoryReviewAssignments(input) {
17
+ if (!input.first || !Array.isArray(input.first.unit_inventory) || !input.first.unit_inventory.length || input.first.unit_inventory.length > 256
18
+ || !Array.isArray(input.first.assignments) || !input.first.assignments.length || input.first.assignments.length > 256
19
+ || input.first.assignments.some(assignment => !assignment?.unit?.id || !Array.isArray(assignment.deleted_references))
20
+ || new Set(input.first.assignments.map(assignment => assignment.unit.id)).size !== input.first.assignments.length
21
+ || input.first.assignments.some(assignment => !input.first.unit_inventory.some(unit => unit.id === assignment.unit.id)))
22
+ refuse("invalid_first_unit_projection");
23
+ const firstAssignments = new Map(input.first.assignments.map(assignment => [assignment.unit.id, assignment]));
24
+ 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 available = new Map(input.files.map(file => [file.path, file]));
27
+ for (const assignment of assignments) {
28
+ for (const deleted of firstAssignments.get(assignment.unit.id)?.deleted_references ?? []) {
29
+ const recreated = available.get(deleted.path);
30
+ if (recreated) {
31
+ const ref = { root: "workspace", path: recreated.path, sha256: recreated.sha256 };
32
+ const role = deleted.role === "artifact" ? assignment.unit.artifacts : assignment.unit.evidence;
33
+ if (!role.some(item => item.path === ref.path))
34
+ role.push({ ...ref });
35
+ if (!assignment.inputs.some(item => item.root === ref.root && item.path === ref.path))
36
+ assignment.inputs.push({ ...ref });
37
+ continue;
38
+ }
39
+ if (assignment.deleted_references.some(item => item.role === deleted.role && item.path === deleted.path))
40
+ continue;
41
+ assignment.deleted_references.push(structuredClone(deleted));
42
+ const proof = deleted.repair_result, prior = assignment.inputs.find(ref => ref.root === proof.root && ref.path === proof.path);
43
+ if (prior && prior.sha256 !== proof.sha256)
44
+ refuse("conflicting_first_deletion_proof");
45
+ if (!prior)
46
+ assignment.inputs.push(structuredClone(proof));
47
+ }
48
+ assignment.inputs.sort((a, b) => `${a.root}:${a.path}`.localeCompare(`${b.root}:${b.path}`, "en"));
49
+ }
50
+ return { units, assignments };
51
+ }
52
+ /** Pure reference projection shared by live admission and historical replay.
53
+ * The caller must independently establish all first/second settlement proofs. */
54
+ export function secondCorrectedMemoryReviewReferences(input) {
55
+ for (const items of [input.firstInputs, input.firstWorkers, input.repairs, input.files])
56
+ if (!Array.isArray(items) || items.length > 4096)
57
+ refuse("unbounded_second_inputs");
58
+ if (input.firstWorkers.some(worker => !worker || !worker.result) || input.repairs.some(repair => !repair || !repair.reference)
59
+ || input.files.some(file => !file))
60
+ refuse("invalid_second_input_reference");
61
+ const inputs = new Map();
62
+ const valid = (ref) => {
63
+ if (!ref || !["run", "workspace"].includes(ref.root) || typeof ref.path !== "string" || !ref.path
64
+ || ref.path.startsWith("/") || ref.path.includes("\\") || ref.path.includes("\0") || ref.path.length > 4096
65
+ || ref.path.split("/").some(part => !part || part === "." || part === "..")
66
+ || typeof ref.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(ref.sha256))
67
+ refuse("invalid_second_input_reference");
68
+ };
69
+ for (const ref of input.firstInputs)
70
+ valid(ref);
71
+ const references = [...input.firstInputs.filter(ref => ref.root === "run"),
72
+ ...input.firstWorkers.map(worker => ({ root: "run", ...worker.result })),
73
+ ...input.repairs.map(repair => ({ root: "run", ...repair.reference })),
74
+ ...input.files.map(file => ({ root: "workspace", ...file }))];
75
+ for (const ref of references) {
76
+ valid(ref);
77
+ const key = memorySourceKey(ref.root, ref.path), prior = inputs.get(key);
78
+ if (prior && prior.sha256 !== ref.sha256)
79
+ refuse("conflicting_second_input");
80
+ inputs.set(key, { root: ref.root, path: ref.path, sha256: ref.sha256 });
81
+ }
82
+ if (inputs.size > 4096)
83
+ refuse("unbounded_second_inputs");
84
+ return [...inputs.values()].sort((a, b) => `${a.root}:${a.path}`.localeCompare(`${b.root}:${b.path}`, "en"));
85
+ }
86
+ /** Second selective input custody only. Fresh checks and independently
87
+ * registered read-only reviewers must consume it before semantic closure. */
88
+ export class PreparedSecondCorrectedMemoryReviewInputs {
89
+ static isPrepared(value) {
90
+ return typeof value === "object" && value !== null && #wave in value;
91
+ }
92
+ sha256;
93
+ #wave;
94
+ #view;
95
+ #observed;
96
+ constructor(key, wave, view, observed) {
97
+ if (key !== token)
98
+ refuse("second_input_token_required");
99
+ this.#wave = wave;
100
+ this.#view = structuredClone(view);
101
+ this.#observed = observed;
102
+ this.sha256 = hybridDigest(view);
103
+ Object.freeze(this);
104
+ }
105
+ static prepare(context, wave) {
106
+ if (!PreparedCompletedMemoryCorrectionWave.isPrepared(wave))
107
+ refuse("completed_second_wave_required");
108
+ if (!PreparedRetainedSecondMemoryRepairs.isPrepared(wave.registration()))
109
+ refuse("second_registration_required");
110
+ if (context.db.isOwnedWriteTransaction?.())
111
+ refuse("preparation_outside_writer_required");
112
+ wave.revalidate(context);
113
+ const view = prepareView(context, wave), bundle = wave.registration().bundle(), workspace = bundle.execution.snapshot().scope.workspace;
114
+ const run = assertPreparedRunBundleSqlAuthority(context, { projectId: view.scope.project_id, runId: view.scope.run_id }, bundle);
115
+ const observed = ObservedMemorySources.capture({ workspace, run: run.run_root ?? run.run_dir }, view.run_inputs.map(ref => ({ root: ref.root, path: ref.path })));
116
+ const hashes = observed.hashes();
117
+ if (view.run_inputs.some(ref => hashes.get(memorySourceKey(ref.root, ref.path)) !== ref.sha256))
118
+ refuse("second_input_bytes_changed");
119
+ const prepared = new PreparedSecondCorrectedMemoryReviewInputs(token, wave, view, observed);
120
+ prepared.revalidate(context);
121
+ return prepared;
122
+ }
123
+ static prepareFromBundle(context, input) {
124
+ if (!(input.bundle instanceof RetainedExecutionBundle) || input.bundle.descriptor.descriptor.id !== "mb-init")
125
+ refuse("registered_memory_bundle_required");
126
+ const registration = PreparedRetainedSecondMemoryRepairs.prepare(context, input);
127
+ return this.prepare(context, PreparedCompletedMemoryCorrectionWave.prepare(context, registration));
128
+ }
129
+ value() { return structuredClone(this.#view); }
130
+ bundle() { return this.#wave.registration().bundle(); }
131
+ completedWave() { return this.#wave; }
132
+ verificationProfileHash(context) { this.assertCustody(context); return this.#wave.registration().verificationProfileHash(context); }
133
+ assertCustody(context) { this.#wave.assertCustody(context); }
134
+ assertAllocationAllowed(context) { this.#wave.assertAllocationAllowed(context); }
135
+ revalidate(context) {
136
+ this.#wave.revalidate(context);
137
+ this.#observed.revalidate();
138
+ const view = this.#view, workspace = this.bundle().execution.snapshot().scope.workspace;
139
+ if (reviewSourceHash(workspace, view.run_inputs.filter(ref => ref.root === "workspace").map(ref => ref.path), view.base) !== view.source_sha256)
140
+ refuse("second_copy_source_changed");
141
+ this.assertCustody(context);
142
+ }
143
+ }
144
+ function prepareView(context, wave) {
145
+ const registration = wave.registration(), receipt = registration.receipt(), first = registration.initialInputView(), batch = registration.batch();
146
+ const files = wave.workspaceManifest().entries().filter(entry => entry.kind === "file").map(entry => ({ path: entry.path, sha256: entry.sha256 }));
147
+ const repairs = wave.repairResults().map(repair => ({ ...repair,
148
+ unit_ids: batch.works.find(work => receipt.work_ids[work.key] === repair.work_id).payload.memory_repair.unit_ids }));
149
+ // The first re-review's current unit definitions, not obsolete compose-era
150
+ // byte identities, form the baseline for the second delta projection.
151
+ const { units, assignments } = secondCorrectedMemoryReviewAssignments({ first, affected: wave.affectedUnitIds(), files, repairs });
152
+ const proof = registration.firstReviewProof(context);
153
+ const runInputs = secondCorrectedMemoryReviewReferences({ firstInputs: first.run_inputs, firstWorkers: proof.workers, repairs, files });
154
+ const workspace = registration.bundle().execution.snapshot().scope.workspace;
155
+ const view = { schema_id: "dd-flow/memory-corrected-review-inputs@2", wave: 2, scope: first.scope,
156
+ completed_correction_sha256: wave.sha256, original_review_sha256: receipt.review_sha256,
157
+ first_review_input_sha256: receipt.first_review_input_sha256, first_check_receipt_sha256: receipt.first_check_receipt_sha256,
158
+ first_review_registration_sha256: receipt.first_review_registration_sha256,
159
+ source_policy: "review-inputs@1", source_sha256: reviewSourceHash(workspace, runInputs.filter(ref => ref.root === "workspace").map(ref => ref.path), first.base),
160
+ base: first.base, run_inputs: runInputs, unit_inventory: units, affected_unit_ids: wave.affectedUnitIds(), assignments,
161
+ 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)
163
+ refuse("unbounded_second_view");
164
+ return view;
165
+ }
@@ -0,0 +1,76 @@
1
+ import { createHash } from "node:crypto";
2
+ import { AppError } from "../shared/errors.js";
3
+ import { PreparedCorrectedMemoryReviewAcceptance } from "./memory-corrected-review-acceptance.js";
4
+ import { validateMemoryReviewCorrectionPlan } from "./memory-review-correction-plan.js";
5
+ import { PreparedHistoricalOriginalMemoryReview } from "./memory-historical-original-review.js";
6
+ import { PreparedHistoricalCorrectedMemoryReview } from "./memory-historical-corrected-review.js";
7
+ import { hybridDigest } from "./hybrid-contracts.js";
8
+ const token = Symbol("memory-second-correction-plan");
9
+ function refuse(reason) {
10
+ throw new AppError("memory_second_correction_invalid", "Second correction requires independent settled first-wave review", 2, { reason, effect: "no_effect" });
11
+ }
12
+ /** Admission only. Does not allocate repair Works, mutate documents or close
13
+ * review. Wave two binds the effective first-wave findings and source units,
14
+ * not obsolete initial findings or caller-supplied coverage. */
15
+ export class PreparedMemorySecondCorrectionPlan {
16
+ static isPrepared(value) {
17
+ return typeof value === "object" && value !== null && #review in value;
18
+ }
19
+ sha256;
20
+ #review;
21
+ #bytes;
22
+ #value;
23
+ constructor(key, review, bytes, value) {
24
+ if (key !== token)
25
+ refuse("prepared_plan_token_required");
26
+ this.#review = review;
27
+ this.#bytes = bytes;
28
+ this.#value = structuredClone(value);
29
+ this.sha256 = createHash("sha256").update(bytes).digest("hex");
30
+ Object.freeze(this);
31
+ }
32
+ static prepare(context, review, bytes) {
33
+ if (!PreparedCorrectedMemoryReviewAcceptance.isPrepared(review))
34
+ refuse("prepared_first_wave_review_required");
35
+ if (context.db.isOwnedWriteTransaction?.())
36
+ refuse("preparation_outside_writer_required");
37
+ review.revalidate(context);
38
+ const registration = review.registration(), view = registration.receipt().view;
39
+ if (view.wave !== 1)
40
+ refuse("correction_wave_limit");
41
+ const projection = review.resultProjection();
42
+ if (projection.outcome === "blocked")
43
+ refuse("blocked_review_requires_resolution");
44
+ const assignments = new Map(view.assignments.map(assignment => [assignment.unit.id, assignment.unit]));
45
+ const units = view.unit_inventory.map(unit => assignments.get(unit.id) ?? unit);
46
+ const bundle = registration.inputs().bundle();
47
+ const value = validateMemoryReviewCorrectionPlan(bytes, bundle.engine(), { runId: view.scope.run_id, wave: 2,
48
+ reviewSha256: review.sha256, units, findings: projection.findings });
49
+ for (const work of value.works)
50
+ bundle.execution.work("review", work.profile_id ?? undefined);
51
+ const prepared = new PreparedMemorySecondCorrectionPlan(token, review, Buffer.from(bytes), value);
52
+ prepared.historicalReview(context);
53
+ return prepared;
54
+ }
55
+ bytes() { return Buffer.from(this.#bytes); }
56
+ value() { return structuredClone(this.#value); }
57
+ review() { return this.#review; }
58
+ /** Prepare retained proof before second-wave mutation. Expected identities
59
+ * come only from this genuine live admission; no caller-selected receipt
60
+ * hash can substitute for the independently registered first review. */
61
+ historicalReview(context) {
62
+ this.revalidate(context);
63
+ const registration = this.#review.registration(), receipt = registration.receipt(), scope = receipt.scope;
64
+ const original = PreparedHistoricalOriginalMemoryReview.prepareWhileReviewOpen(context, {
65
+ scope: { projectId: scope.project_id, runId: scope.run_id }, bundle: registration.inputs().bundle()
66
+ });
67
+ const historical = PreparedHistoricalCorrectedMemoryReview.prepare(context, { original,
68
+ correctedInputSha256: receipt.input_sha256, checkReceiptSha256: receipt.check_receipt_sha256 });
69
+ if (historical.reviewSha256() !== this.#review.sha256
70
+ || hybridDigest(historical.projection()) !== hybridDigest(this.#review.resultProjection()))
71
+ refuse("historical_first_wave_changed");
72
+ return historical;
73
+ }
74
+ assertCustody(context) { this.#review.assertCustody(context); }
75
+ revalidate(context) { this.#review.revalidate(context); }
76
+ }
@@ -0,0 +1,91 @@
1
+ import { AppError } from "../shared/errors.js";
2
+ import { appendAudit } from "./audit.js";
3
+ import { hybridDigest } from "./hybrid-contracts.js";
4
+ import { PreparedSecondMemoryRepairSource } from "./memory-second-repair-source.js";
5
+ import { MemoryRepairDocumentBaseline } from "./memory-repair-document-delta.js";
6
+ const token = Symbol("second-memory-repair-baseline");
7
+ function refuse(reason) {
8
+ throw new AppError("memory_second_repair_baseline_invalid", "Second writer baseline lacks independently admitted current source", 2, { reason, effect: "no_effect" });
9
+ }
10
+ function selected(context, receipt) {
11
+ const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='memory.repair_work_baseline' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? LIMIT 2", [receipt.project_id, receipt.run_id, receipt.work_id]);
12
+ if (!rows.length)
13
+ return null;
14
+ if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 24 * 1024 * 1024)
15
+ refuse("ambiguous_or_unbounded_baseline");
16
+ try {
17
+ return JSON.parse(rows[0].payload_json);
18
+ }
19
+ catch {
20
+ return refuse("invalid_baseline");
21
+ }
22
+ }
23
+ /** Second-wave pre-launch receipt only. It grants no private copy,
24
+ * Session/start, publication or historical post-write re-admission rights. */
25
+ export class PreparedSecondMemoryRepairBaseline {
26
+ static isPrepared(value) {
27
+ return typeof value === "object" && value !== null && #source in value;
28
+ }
29
+ sha256;
30
+ #source;
31
+ #receipt;
32
+ constructor(key, source, receipt) {
33
+ if (key !== token)
34
+ refuse("baseline_token_required");
35
+ this.#source = source;
36
+ this.#receipt = structuredClone(receipt);
37
+ this.sha256 = hybridDigest(receipt);
38
+ Object.freeze(this);
39
+ }
40
+ static prepare(context, source) {
41
+ if (!PreparedSecondMemoryRepairSource.isPrepared(source))
42
+ refuse("prepared_second_source_required");
43
+ if (context.db.isOwnedWriteTransaction?.())
44
+ refuse("preparation_outside_writer_required");
45
+ source.revalidate(context);
46
+ const baseline = source.baseline(), exported = source.exportedBaseline();
47
+ const binding = source.baselineBinding(context);
48
+ const receipt = { schema_id: binding.current_view_sha256 ? "dd-flow/memory-repair-work-baseline@5" : "dd-flow/memory-repair-work-baseline@4", wave: 2, ...binding,
49
+ baseline_sha256: baseline.sha256, baseline_base64: baseline.bytes().toString("base64"), export_policy: "memory-repair-export@1",
50
+ exported_baseline_sha256: exported.sha256, exported_baseline_base64: exported.bytes().toString("base64") };
51
+ if (Buffer.byteLength(JSON.stringify(receipt)) > 24 * 1024 * 1024)
52
+ refuse("unbounded_baseline");
53
+ source.revalidate(context);
54
+ return new PreparedSecondMemoryRepairBaseline(token, source, receipt);
55
+ }
56
+ receipt() { return structuredClone(this.#receipt); }
57
+ baseline() {
58
+ return MemoryRepairDocumentBaseline.restore(Buffer.from(this.#receipt.baseline_base64, "base64"), this.#receipt.baseline_sha256);
59
+ }
60
+ exportedBaseline() {
61
+ return MemoryRepairDocumentBaseline.restore(Buffer.from(this.#receipt.exported_baseline_base64, "base64"), this.#receipt.exported_baseline_sha256);
62
+ }
63
+ copyInput() { return this.#source.copyInput(); }
64
+ workerRoute() { return this.#source.workerRoute(); }
65
+ assertPrelaunchCustody(context) { this.assertCustody(context); }
66
+ assertRegisteredCustody(context) {
67
+ this.assertCustody(context);
68
+ if (hybridDigest(selected(context, this.#receipt)) !== this.sha256)
69
+ refuse("registered_baseline_required");
70
+ }
71
+ revalidate(context) { this.#source.revalidate(context); this.assertCustody(context); }
72
+ assertCustody(context) {
73
+ this.#source.assertCustody(context);
74
+ if (hybridDigest({ ...this.#receipt, ...this.#source.baselineBinding(context) }) !== this.sha256)
75
+ refuse("source_binding_changed");
76
+ }
77
+ register(context) {
78
+ this.#source.revalidate(context);
79
+ return context.db.writeTransaction(() => {
80
+ this.assertCustody(context);
81
+ const prior = selected(context, this.#receipt);
82
+ if (prior !== null && hybridDigest(prior) !== this.sha256)
83
+ refuse("baseline_replay_conflict");
84
+ if (prior === null)
85
+ appendAudit(context, { projectId: this.#receipt.project_id, eventType: "memory.repair_work_baseline", payload: this.#receipt });
86
+ if (hybridDigest(selected(context, this.#receipt)) !== this.sha256)
87
+ refuse("baseline_receipt_missing");
88
+ return { work_id: this.#receipt.work_id, baseline_sha256: this.#receipt.baseline_sha256, receipt_sha256: this.sha256, replayed: prior !== null };
89
+ });
90
+ }
91
+ }