@deksden-com/dd-flow-cli 0.9.0-beta.137 → 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 (260) hide show
  1. package/CHANGELOG.md +4 -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 +197 -0
  7. package/dist/cli/command-option-descriptions.js +191 -0
  8. package/dist/cli/completion-cli.js +70 -0
  9. package/dist/cli/completion.js +57 -0
  10. package/dist/cli/error-output.js +52 -0
  11. package/dist/cli/executor-onboarding.js +76 -0
  12. package/dist/cli/flows.js +29 -0
  13. package/dist/cli/harnesses.js +84 -0
  14. package/dist/cli/help.js +240 -6
  15. package/dist/cli/input-preparation.js +54 -5
  16. package/dist/cli/memory-flow-admission.js +44 -0
  17. package/dist/cli/memory-init-invocation.js +78 -0
  18. package/dist/cli/memory-init.js +240 -0
  19. package/dist/cli/memory-storage-admission.js +53 -0
  20. package/dist/cli/models.js +39 -0
  21. package/dist/cli/native-hook-ingress.js +1 -1
  22. package/dist/cli/operator-profiles.js +61 -0
  23. package/dist/cli/output-options.js +35 -0
  24. package/dist/cli/response-file.js +71 -0
  25. package/dist/cli/run-cli.js +283 -160
  26. package/dist/cli/setup.js +149 -0
  27. package/dist/cli.js +54 -2
  28. package/dist/domain/stage-handler-identities.js +20 -0
  29. package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
  30. package/dist/harness-runtime/lib/daemon-operations.mjs +4 -3
  31. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +4 -2
  32. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +11 -5
  33. package/dist/harness-runtime/lib/dd-codex.mjs +22 -10
  34. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +2 -1
  35. package/dist/harness-runtime/lib/dd-droid.mjs +2 -1
  36. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +4 -2
  37. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +6 -4
  38. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +9 -9
  39. package/dist/harness-runtime/lib/delegation-instructions.mjs +5 -3
  40. package/dist/harness-runtime/lib/lifecycle-command.mjs +1 -0
  41. package/dist/harness-runtime/lib/managed-daemon.d.mts +1 -0
  42. package/dist/harness-runtime/lib/managed-daemon.mjs +50 -15
  43. package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
  44. package/dist/harness-runtime/lib/native-hook-command.mjs +1 -1
  45. package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
  46. package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
  47. package/dist/harness-runtime/lib/opencode-plugin.mjs +2 -0
  48. package/dist/harness-runtime/lib/operation-progress.mjs +3 -1
  49. package/dist/runtime/context.js +1 -1
  50. package/dist/schemas/discovery-result.schema.json +184 -0
  51. package/dist/schemas/execution-bundle.schema.json +205 -0
  52. package/dist/schemas/execution-settings.schema.json +162 -0
  53. package/dist/schemas/flow-descriptor.schema.json +54 -0
  54. package/dist/schemas/flow-run.schema.json +22 -0
  55. package/dist/schemas/harness-catalog.schema.json +80 -0
  56. package/dist/schemas/mb-init-compose-result.schema.json +457 -0
  57. package/dist/schemas/mb-init-discover-result.schema.json +500 -0
  58. package/dist/schemas/mb-init-finish-result.schema.json +422 -0
  59. package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
  60. package/dist/schemas/mb-init-review-result.schema.json +396 -0
  61. package/dist/schemas/mb-init-verify-result.schema.json +391 -0
  62. package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
  63. package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
  64. package/dist/schemas/memory-composition-work-result.schema.json +38 -0
  65. package/dist/schemas/memory-repair-work-result.schema.json +49 -0
  66. package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
  67. package/dist/schemas/memory-review-work-result.schema.json +43 -0
  68. package/dist/schemas/model-catalog.schema.json +153 -0
  69. package/dist/schemas/profile-settings.schema.json +122 -0
  70. package/dist/schemas/release-manifest.schema.json +291 -0
  71. package/dist/schemas/run-engine-binding.schema.json +1 -1
  72. package/dist/services/check-runner-evidence.js +51 -4
  73. package/dist/services/cli-operation-classifier.js +8 -47
  74. package/dist/services/cli-upgrade-allowlist.js +27 -0
  75. package/dist/services/code-checks.js +387 -34
  76. package/dist/services/controller-execution.js +88 -0
  77. package/dist/services/controller-fanout.js +71 -12
  78. package/dist/services/dashboard.js +8 -2
  79. package/dist/services/descriptor-coordinator-session.js +35 -0
  80. package/dist/services/descriptor-root-work.js +53 -0
  81. package/dist/services/descriptor-run-settlement.js +105 -0
  82. package/dist/services/descriptor-run-start.js +449 -0
  83. package/dist/services/descriptor-stage-coordinator.js +197 -0
  84. package/dist/services/descriptor-stage-settlement.js +222 -0
  85. package/dist/services/descriptor-stage-start.js +139 -0
  86. package/dist/services/discovery/agy-discovery.js +2 -0
  87. package/dist/services/discovery/codex-discovery.js +2 -0
  88. package/dist/services/discovery/droid-discovery.js +2 -0
  89. package/dist/services/discovery/grok-discovery.js +2 -0
  90. package/dist/services/discovery/opencode-discovery.js +2 -0
  91. package/dist/services/discovery/static.js +31 -0
  92. package/dist/services/discovery/zcode-discovery.js +2 -0
  93. package/dist/services/engines.js +39 -22
  94. package/dist/services/eval-snapshots.js +83 -25
  95. package/dist/services/execution-bundles.js +263 -0
  96. package/dist/services/execution-policy.js +20 -16
  97. package/dist/services/executor-onboarding.js +56 -0
  98. package/dist/services/external-work-launch.js +91 -11
  99. package/dist/services/flow-catalog.js +21 -0
  100. package/dist/services/flow-descriptors.js +82 -0
  101. package/dist/services/frozen-engine.js +242 -0
  102. package/dist/services/generic-execution.js +164 -0
  103. package/dist/services/harness-adapter.js +28 -2
  104. package/dist/services/harness-discovery.js +29 -0
  105. package/dist/services/harness-doctor.js +75 -0
  106. package/dist/services/harness-probe-effects.js +51 -0
  107. package/dist/services/harness-version-probe.js +100 -0
  108. package/dist/services/hooks.js +24 -6
  109. package/dist/services/hybrid-contracts.js +483 -0
  110. package/dist/services/ids.js +29 -1
  111. package/dist/services/installation-lock.js +95 -0
  112. package/dist/services/installed-flow-source.js +183 -0
  113. package/dist/services/lifecycle-invocations.js +257 -78
  114. package/dist/services/memory-accepted-composition.js +77 -0
  115. package/dist/services/memory-baseline-preservation.js +65 -0
  116. package/dist/services/memory-completed-correction-wave.js +320 -0
  117. package/dist/services/memory-compose-handler.js +258 -0
  118. package/dist/services/memory-composition-acceptance.js +199 -0
  119. package/dist/services/memory-composition-graph.js +134 -0
  120. package/dist/services/memory-composition-registration.js +146 -0
  121. package/dist/services/memory-composition-work-custody.js +151 -0
  122. package/dist/services/memory-composition-work-results.js +96 -0
  123. package/dist/services/memory-corrected-review-acceptance.js +129 -0
  124. package/dist/services/memory-corrected-review-batch.js +30 -0
  125. package/dist/services/memory-corrected-review-findings.js +106 -0
  126. package/dist/services/memory-corrected-review-inputs.js +210 -0
  127. package/dist/services/memory-corrected-review-projection.js +78 -0
  128. package/dist/services/memory-corrected-review-receipt.js +170 -0
  129. package/dist/services/memory-corrected-review-registration.js +191 -0
  130. package/dist/services/memory-correction-check-receipt.js +219 -0
  131. package/dist/services/memory-correction-input-owner.js +6 -0
  132. package/dist/services/memory-discover-handler.js +104 -0
  133. package/dist/services/memory-finish-evidence.js +435 -0
  134. package/dist/services/memory-finish-handler.js +215 -0
  135. package/dist/services/memory-finish-integration.js +280 -0
  136. package/dist/services/memory-finish-readiness.js +122 -0
  137. package/dist/services/memory-git-integration.js +151 -0
  138. package/dist/services/memory-historical-corrected-review.js +206 -0
  139. package/dist/services/memory-historical-original-review.js +130 -0
  140. package/dist/services/memory-historical-repair-registration.js +99 -0
  141. package/dist/services/memory-historical-second-review.js +128 -0
  142. package/dist/services/memory-init-bootstrap-readiness.js +210 -0
  143. package/dist/services/memory-init-contexts.js +89 -0
  144. package/dist/services/memory-init-controller-start.js +84 -0
  145. package/dist/services/memory-init-engine-release.js +223 -0
  146. package/dist/services/memory-init-git-policy.js +136 -0
  147. package/dist/services/memory-init-lint-capability.js +185 -0
  148. package/dist/services/memory-init-native-capacity.js +241 -0
  149. package/dist/services/memory-init-preflight.js +312 -0
  150. package/dist/services/memory-init-project-bootstrap.js +599 -0
  151. package/dist/services/memory-init-request-candidate.js +157 -0
  152. package/dist/services/memory-init-request-input.js +66 -0
  153. package/dist/services/memory-init-resource-storage.js +125 -0
  154. package/dist/services/memory-init-run-allocation.js +81 -0
  155. package/dist/services/memory-init-run-bundle.js +98 -0
  156. package/dist/services/memory-init-start.js +175 -0
  157. package/dist/services/memory-init-startup-custody.js +80 -0
  158. package/dist/services/memory-init-startup-input.js +240 -0
  159. package/dist/services/memory-init-storage.js +81 -0
  160. package/dist/services/memory-init-stores.js +359 -0
  161. package/dist/services/memory-init-worktrunk-capability.js +172 -0
  162. package/dist/services/memory-layer-publication.js +611 -0
  163. package/dist/services/memory-layer.js +156 -0
  164. package/dist/services/memory-lint-config.js +85 -0
  165. package/dist/services/memory-native-review-copy.js +165 -0
  166. package/dist/services/memory-normal-flow-entry.js +75 -0
  167. package/dist/services/memory-ordinary-defaults.js +112 -0
  168. package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
  169. package/dist/services/memory-preflight-handler.js +259 -0
  170. package/dist/services/memory-repair-current-view.js +299 -0
  171. package/dist/services/memory-repair-document-delta.js +451 -0
  172. package/dist/services/memory-repair-history.js +491 -0
  173. package/dist/services/memory-repair-physical-binding.js +312 -0
  174. package/dist/services/memory-repair-publication-files.js +152 -0
  175. package/dist/services/memory-repair-publication-intent.js +27 -0
  176. package/dist/services/memory-repair-publication-plan.js +160 -0
  177. package/dist/services/memory-repair-publication-source.js +225 -0
  178. package/dist/services/memory-repair-publication.js +794 -0
  179. package/dist/services/memory-repair-registration.js +239 -0
  180. package/dist/services/memory-repair-work-baseline.js +413 -0
  181. package/dist/services/memory-repair-work-completion.js +97 -0
  182. package/dist/services/memory-repair-work-custody.js +73 -0
  183. package/dist/services/memory-repair-work-results.js +89 -0
  184. package/dist/services/memory-repair-workspace.js +252 -0
  185. package/dist/services/memory-repair-writer-copy.js +735 -0
  186. package/dist/services/memory-result-binding.js +173 -0
  187. package/dist/services/memory-retained-review-authority.js +129 -0
  188. package/dist/services/memory-retained-review-results.js +70 -0
  189. package/dist/services/memory-retained-second-baseline.js +102 -0
  190. package/dist/services/memory-retained-second-repairs.js +156 -0
  191. package/dist/services/memory-review-acceptance.js +214 -0
  192. package/dist/services/memory-review-correction-plan.js +171 -0
  193. package/dist/services/memory-review-handler.js +585 -0
  194. package/dist/services/memory-review-inputs.js +200 -0
  195. package/dist/services/memory-review-receipt.js +180 -0
  196. package/dist/services/memory-review-registration.js +108 -0
  197. package/dist/services/memory-review-work-custody.js +124 -0
  198. package/dist/services/memory-review-work-input.js +72 -0
  199. package/dist/services/memory-review-work-results.js +73 -0
  200. package/dist/services/memory-second-corrected-review-inputs.js +165 -0
  201. package/dist/services/memory-second-correction-plan.js +76 -0
  202. package/dist/services/memory-second-repair-baseline.js +91 -0
  203. package/dist/services/memory-second-repair-launch.js +614 -0
  204. package/dist/services/memory-second-repair-registration.js +88 -0
  205. package/dist/services/memory-second-repair-source.js +187 -0
  206. package/dist/services/memory-second-review-acceptance.js +115 -0
  207. package/dist/services/memory-source-observations.js +117 -0
  208. package/dist/services/memory-stage-dispatch.js +28 -0
  209. package/dist/services/memory-stage-results.js +199 -0
  210. package/dist/services/memory-verification-acceptance.js +135 -0
  211. package/dist/services/memory-verification-checks.js +252 -0
  212. package/dist/services/memory-verification-receipt.js +159 -0
  213. package/dist/services/memory-verify-handler.js +135 -0
  214. package/dist/services/memory-work-result-preparation.js +63 -0
  215. package/dist/services/model-catalog.js +109 -0
  216. package/dist/services/model-discovery.js +46 -0
  217. package/dist/services/operator-profiles.js +236 -0
  218. package/dist/services/projects.js +17 -2
  219. package/dist/services/recovery-snapshot-database.js +416 -1
  220. package/dist/services/release-archive-producer.js +52 -0
  221. package/dist/services/release-archive.js +143 -0
  222. package/dist/services/release-cache.js +142 -0
  223. package/dist/services/release-compatibility.js +18 -0
  224. package/dist/services/release-download.js +67 -0
  225. package/dist/services/release-envelope.js +104 -0
  226. package/dist/services/release-manifest.js +45 -0
  227. package/dist/services/release-payload.js +114 -0
  228. package/dist/services/release-producer.js +63 -0
  229. package/dist/services/release-provenance.js +160 -0
  230. package/dist/services/release-registry.js +66 -0
  231. package/dist/services/release-source.js +194 -0
  232. package/dist/services/repair-intents.js +1 -1
  233. package/dist/services/review-copy.js +10 -2
  234. package/dist/services/run-control.js +80 -9
  235. package/dist/services/run-controller-capture.js +10 -3
  236. package/dist/services/run-controller-process.js +21 -0
  237. package/dist/services/run-controller-recovery.js +3 -1
  238. package/dist/services/run-controller-state.js +1 -1
  239. package/dist/services/run-controller.js +101 -40
  240. package/dist/services/run-recovery-guard.js +2 -0
  241. package/dist/services/run-recovery.js +145 -5
  242. package/dist/services/run-start-custody.js +177 -0
  243. package/dist/services/runs.js +319 -25
  244. package/dist/services/setup.js +377 -0
  245. package/dist/services/stage-context.js +9 -1
  246. package/dist/services/stage-handlers.js +74 -0
  247. package/dist/services/stage-report-renderer.js +63 -0
  248. package/dist/services/stage-work-graph.js +17 -0
  249. package/dist/services/vnext-code.js +3 -3
  250. package/dist/services/vnext-execution-profile.js +2 -2
  251. package/dist/services/vnext-fanout.js +70 -9
  252. package/dist/services/vnext-merge.js +8 -3
  253. package/dist/services/vnext-plan-review.js +6 -4
  254. package/dist/services/work-registry.js +616 -71
  255. package/dist/services/workspace-files.js +1 -1
  256. package/dist/storage/database.js +84 -6
  257. package/dist/storage/writer-contract.js +11 -7
  258. package/package.json +13 -3
  259. package/tools/create-canon-release.mjs +33 -0
  260. package/tools/produce-hybrid-release.mjs +41 -0
@@ -0,0 +1,78 @@
1
+ import { AppError } from "../shared/errors.js";
2
+ import { hybridDigest } from "./hybrid-contracts.js";
3
+ import { memorySourceKey } from "./memory-stage-results.js";
4
+ function refuse(reason) {
5
+ throw new AppError("memory_corrected_review_projection_invalid", "Review result differs from retained inventory and independent selective coverage", 2, { reason, effect: "no_effect" });
6
+ }
7
+ /** Pure full-coverage projection, not Stage acceptance. Untouched units retain
8
+ * their original reviewer and unchanged source identities. Affected units use
9
+ * the separately admitted current assignment, including explicit deletions. */
10
+ export function correctedMemoryReviewUnits(input) {
11
+ const { view } = input, inventory = view.unit_inventory;
12
+ if (!((view.wave === 1 && view.schema_id === "dd-flow/memory-corrected-review-inputs@1")
13
+ || (view.wave === 2 && view.schema_id === "dd-flow/memory-corrected-review-inputs@2"))
14
+ || !Array.isArray(inventory) || !inventory.length || inventory.length > 256
15
+ || new Set(inventory.map(unit => unit.id)).size !== inventory.length
16
+ || !Array.isArray(view.assignments) || !Array.isArray(view.affected_unit_ids)
17
+ || new Set(view.affected_unit_ids).size !== view.affected_unit_ids.length
18
+ || view.assignments.length !== view.affected_unit_ids.length || input.current.length !== view.assignments.length
19
+ || input.repairWorkIds.length !== view.repairs.length
20
+ || new Set(input.repairWorkIds).size !== input.repairWorkIds.length
21
+ || hybridDigest([...input.repairWorkIds].sort()) !== hybridDigest(view.repairs.map(repair => repair.work_id).sort()))
22
+ refuse("invalid_projection_inventory");
23
+ const applicable = inventory.filter(unit => unit.applicability === "applicable");
24
+ const reviews = (results, refs, ids) => {
25
+ if (results.length !== ids.length || refs.length !== ids.length
26
+ || new Set(results.map(result => result.unit_id)).size !== ids.length || new Set(results.map(result => result.work_id)).size !== ids.length
27
+ || new Set(refs.map(reference => reference.reviewer_work_id)).size !== refs.length
28
+ || results.some(result => !ids.includes(result.unit_id)))
29
+ refuse("changed_review_coverage");
30
+ return new Map(results.map(result => {
31
+ const reference = refs.find(reference => reference.reviewer_work_id === result.work_id);
32
+ if (!reference || hybridDigest(reference.unit_ids) !== hybridDigest([result.unit_id]))
33
+ refuse("changed_review_reference");
34
+ return [result.unit_id, { result, reference: structuredClone(reference) }];
35
+ }));
36
+ };
37
+ const original = reviews(input.original, input.originalReferences, applicable.map(unit => unit.id));
38
+ const current = reviews(input.current, input.currentReferences, view.affected_unit_ids);
39
+ if (input.current.some(result => input.original.some(old => old.work_id === result.work_id)))
40
+ refuse("reused_review_work");
41
+ const sources = new Map(view.run_inputs.map(ref => [memorySourceKey(ref.root, ref.path), ref.sha256]));
42
+ const references = [];
43
+ const units = inventory.map(old => {
44
+ if (!["applicable", "not-applicable"].includes(old.applicability))
45
+ refuse("unresolved_unit_applicability");
46
+ if (old.applicability === "not-applicable") {
47
+ if (view.affected_unit_ids.includes(old.id))
48
+ refuse("excluded_unit_reviewed");
49
+ return { ...structuredClone(old), outcome: "not-applicable" };
50
+ }
51
+ const changed = view.affected_unit_ids.includes(old.id), review = changed ? current.get(old.id) : original.get(old.id);
52
+ if (!review)
53
+ refuse("missing_unit_review");
54
+ references.push(review.reference);
55
+ const assignments = view.assignments.filter(assignment => assignment.unit.id === old.id);
56
+ if (changed && (assignments.length !== 1 || assignments[0].unit.applicability !== "applicable"
57
+ || assignments[0].unit.aspect !== old.aspect || assignments[0].original_unit_sha256 !== hybridDigest(old)))
58
+ refuse("changed_unit_assignment");
59
+ const effective = changed ? assignments[0].unit : old;
60
+ if (!["accepted", "needs_repair", "blocked"].includes(review.result.outcome)
61
+ || (review.result.outcome === "accepted" && !effective.artifacts.length))
62
+ refuse("invalid_unit_assessment");
63
+ const normalize = (refs) => refs.map(ref => {
64
+ const root = ref.root === "run" ? "run" : "workspace", path = ref.root === "canon" ? `.memory-bank/${ref.path}` : ref.path;
65
+ if (!["workspace", "project", "canon", "run"].includes(ref.root) || sources.get(memorySourceKey(root, path)) !== ref.sha256)
66
+ refuse("stale_unit_reference");
67
+ return { root: ref.root === "project" ? "workspace" : ref.root, path: ref.path, sha256: ref.sha256 };
68
+ });
69
+ const evidence = effective.evidence.filter(ref => changed || !["project", "workspace", "canon", "run"].includes(ref.root) || !inventory.flatMap(unit => unit.artifacts).some(artifact => (artifact.root === "run" ? "run" : "workspace") === (ref.root === "run" ? "run" : "workspace")
70
+ && (artifact.root === "canon" ? `.memory-bank/${artifact.path}` : artifact.path) === (ref.root === "canon" ? `.memory-bank/${ref.path}` : ref.path)
71
+ && artifact.sha256 !== ref.sha256 && sources.get(memorySourceKey(artifact.root === "run" ? "run" : "workspace", artifact.root === "canon" ? `.memory-bank/${artifact.path}` : artifact.path)) === artifact.sha256));
72
+ return { ...structuredClone(effective), evidence: normalize(evidence), artifacts: normalize(effective.artifacts), outcome: review.result.outcome };
73
+ });
74
+ if (view.assignments.some(assignment => !view.affected_unit_ids.includes(assignment.unit.id))
75
+ || view.affected_unit_ids.some(id => !applicable.some(unit => unit.id === id)))
76
+ refuse("foreign_assignment");
77
+ return { units, details: { reviews: references, repair_wave: view.wave, repair_work_ids: [...input.repairWorkIds] } };
78
+ }
@@ -0,0 +1,170 @@
1
+ import { AppError } from "../shared/errors.js";
2
+ import { appendAudit } from "./audit.js";
3
+ import { hybridDigest } from "./hybrid-contracts.js";
4
+ import { PreparedCorrectedMemoryReviewAcceptance } from "./memory-corrected-review-acceptance.js";
5
+ import { PreparedSecondMemoryReviewAcceptance } from "./memory-second-review-acceptance.js";
6
+ import { PreparedMemoryStageResult } from "./memory-stage-results.js";
7
+ import { Ajv2020 } from "ajv/dist/2020.js";
8
+ import { RetainedExecutionBundle } from "./execution-bundles.js";
9
+ import { readAcceptedStageData } from "./stage-report-renderer.js";
10
+ import { PreparedHistoricalOriginalMemoryReview } from "./memory-historical-original-review.js";
11
+ import { PreparedHistoricalCorrectedMemoryReview } from "./memory-historical-corrected-review.js";
12
+ import { PreparedHistoricalSecondMemoryReview } from "./memory-historical-second-review.js";
13
+ const token = Symbol("memory-corrected-review-receipt"), event = "memory.corrected_review_acceptance";
14
+ function refuse(reason) {
15
+ throw new AppError("memory_corrected_review_receipt_invalid", "Corrected review lacks independently accepted selective-wave custody", 2, { reason, effect: "no_effect" });
16
+ }
17
+ function selected(context, scope) {
18
+ const run = context.db.get("SELECT index_json FROM runs WHERE project_id=? AND id=?", [scope.projectId, scope.runId]);
19
+ if (!run)
20
+ refuse("run_missing");
21
+ const index = JSON.parse(run.index_json), stage = index.stage_runs?.find((item) => item.stage === "review");
22
+ if (index.current_stage !== "review" || stage?.status !== "running" || typeof stage.attempt !== "string"
23
+ || !/^try-[0-9]{3,}$/.test(stage.attempt) || typeof stage.input_sha256 !== "string"
24
+ || !/^[a-f0-9]{64}$/.test(stage.input_sha256))
25
+ refuse("active_review_required");
26
+ return { attempt: stage.attempt, input_sha256: stage.input_sha256 };
27
+ }
28
+ function records(context, scope) {
29
+ return context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type=? AND json_extract(payload_json,'$.run_id')=? LIMIT 2", [scope.projectId, event, scope.runId]);
30
+ }
31
+ function receipt(context, scope, wave, result) {
32
+ wave.assertResult(result);
33
+ const proof = wave.settlementProof(context), stage = selected(context, scope), value = result.value();
34
+ const expectedWave = PreparedSecondMemoryReviewAcceptance.isPrepared(wave) ? 2 : 1;
35
+ if (proof.wave !== expectedWave || proof.registration_scope.project_id !== scope.projectId || proof.registration_scope.run_id !== scope.runId
36
+ || proof.registration_scope.stage !== "review" || proof.registration_scope.attempt !== stage.attempt
37
+ || proof.registration_scope.input_sha256 !== stage.input_sha256 || value.outcome !== "accepted")
38
+ refuse("accepted_owned_wave_required");
39
+ const retained = { schema_id: "dd-flow/memory-corrected-review-acceptance@1", project_id: scope.projectId, run_id: scope.runId,
40
+ stage: "review", ...stage, result_sha256: result.sha256, projection_sha256: hybridDigest(wave.resultProjection()),
41
+ corrected_input_sha256: wave.registration().receipt().input_sha256, ...proof };
42
+ if (Buffer.byteLength(JSON.stringify(retained)) > 2 * 1024 * 1024)
43
+ refuse("unbounded_receipt");
44
+ return retained;
45
+ }
46
+ /** Fresh receipt only: the generic Stage acceptance writer must record this
47
+ * atomically. It does not close a Stage or authorize historical replay. */
48
+ export class PreparedMemoryCorrectedReviewReceipt {
49
+ #scope;
50
+ #wave;
51
+ #result;
52
+ #receipt;
53
+ constructor(key, scope, wave, result, value) {
54
+ if (key !== token)
55
+ refuse("receipt_token_required");
56
+ this.#scope = { ...scope };
57
+ this.#wave = wave;
58
+ this.#result = result;
59
+ this.#receipt = structuredClone(value);
60
+ Object.freeze(this);
61
+ }
62
+ static prepare(context, input) {
63
+ if ((!PreparedCorrectedMemoryReviewAcceptance.isPrepared(input.wave) && !PreparedSecondMemoryReviewAcceptance.isPrepared(input.wave))
64
+ || !(input.result instanceof PreparedMemoryStageResult))
65
+ refuse("prepared_wave_required");
66
+ if (context.db.isOwnedWriteTransaction?.())
67
+ refuse("preparation_outside_writer_required");
68
+ input.wave.revalidate(context);
69
+ if (records(context, input.scope).length)
70
+ refuse("fresh_receipt_already_exists");
71
+ return new PreparedMemoryCorrectedReviewReceipt(token, input.scope, input.wave, input.result, receipt(context, input.scope, input.wave, input.result));
72
+ }
73
+ record(context) {
74
+ if (!context.db.isOwnedWriteTransaction?.())
75
+ refuse("owned_acceptance_writer_required");
76
+ if (records(context, this.#scope).length || hybridDigest(receipt(context, this.#scope, this.#wave, this.#result)) !== hybridDigest(this.#receipt))
77
+ refuse("fresh_review_custody_changed");
78
+ appendAudit(context, { projectId: this.#scope.projectId, eventType: event, payload: this.#receipt });
79
+ const rows = records(context, this.#scope);
80
+ if (rows.length !== 1 || hybridDigest(JSON.parse(rows[0].payload_json)) !== hybridDigest(this.#receipt))
81
+ refuse("receipt_readback_failed");
82
+ }
83
+ }
84
+ function acceptedReceipt(context, scope) {
85
+ const data = readAcceptedStageData(context, { ...scope, stage: "review" }), rows = records(context, scope);
86
+ if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 2 * 1024 * 1024)
87
+ refuse("accepted_receipt_missing");
88
+ let retained;
89
+ try {
90
+ retained = JSON.parse(rows[0].payload_json);
91
+ }
92
+ catch {
93
+ return refuse("invalid_accepted_receipt");
94
+ }
95
+ if (!retained || typeof retained !== "object" || Array.isArray(retained)
96
+ || retained.schema_id !== "dd-flow/memory-corrected-review-acceptance@1" || retained.project_id !== scope.projectId
97
+ || retained.run_id !== scope.runId || retained.stage !== "review" || ![1, 2].includes(retained.wave)
98
+ || (retained.wave === 2 && (typeof retained.first_review_proof_sha256 !== "string"
99
+ || !/^[a-f0-9]{64}$/.test(retained.first_review_proof_sha256)))
100
+ || typeof retained.corrected_input_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(retained.corrected_input_sha256)
101
+ || typeof retained.check_receipt_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(retained.check_receipt_sha256)
102
+ || data.data_schema_id !== "dd-flow/mb-init-review-result@1" || retained.result_sha256 !== data.result.sha256
103
+ || retained.input_sha256 !== data.input_sha256)
104
+ refuse("accepted_receipt_changed");
105
+ return { data, retained, authority: hybridDigest({ data: data.authority_sha256, receipt: retained }) };
106
+ }
107
+ /** Closed Stage replay proves exact independently accepted bytes and rebuilt
108
+ * historical coverage/findings. Own Stage projection may be missing after
109
+ * commit: generic settlement restores it; reviewers/checks/repairs may not be
110
+ * substituted or re-admitted from today's project policy or documents. */
111
+ export class PreparedAcceptedMemoryCorrectedReview {
112
+ #scope;
113
+ #authority;
114
+ #history;
115
+ constructor(key, scope, authority, history) {
116
+ if (key !== token)
117
+ refuse("receipt_token_required");
118
+ this.#scope = { ...scope };
119
+ this.#authority = authority;
120
+ this.#history = history;
121
+ Object.freeze(this);
122
+ }
123
+ static prepare(context, input) {
124
+ if (!(input.bundle instanceof RetainedExecutionBundle) || input.bundle.descriptor.descriptor.id !== "mb-init"
125
+ || !(input.bytes instanceof Uint8Array) || !input.bytes.length || input.bytes.length > 1024 * 1024)
126
+ refuse("registered_memory_result_required");
127
+ if (context.db.isOwnedWriteTransaction?.())
128
+ refuse("preparation_outside_writer_required");
129
+ const accepted = context.db.readSnapshot(() => acceptedReceipt(context, input.scope));
130
+ if (!accepted.data.bytes.equals(Buffer.from(input.bytes)))
131
+ refuse("accepted_result_bytes_changed");
132
+ const hashes = { correctedInputSha256: accepted.retained.corrected_input_sha256, checkReceiptSha256: accepted.retained.check_receipt_sha256 };
133
+ const history = accepted.retained.wave === 2
134
+ ? PreparedHistoricalSecondMemoryReview.prepare(context, { scope: input.scope, bundle: input.bundle, ...hashes })
135
+ : PreparedHistoricalCorrectedMemoryReview.prepare(context, {
136
+ original: PreparedHistoricalOriginalMemoryReview.prepare(context, { scope: input.scope, bundle: input.bundle }), ...hashes
137
+ });
138
+ const projection = history.projection(), proof = history.settlementProof(context), scope = proof.registration_scope;
139
+ const expected = { schema_id: "dd-flow/memory-corrected-review-acceptance@1", project_id: input.scope.projectId, run_id: input.scope.runId,
140
+ stage: "review", attempt: scope.attempt, input_sha256: scope.input_sha256, result_sha256: accepted.data.result.sha256,
141
+ projection_sha256: hybridDigest(projection), corrected_input_sha256: accepted.retained.corrected_input_sha256, ...proof };
142
+ if (hybridDigest(expected) !== hybridDigest(accepted.retained))
143
+ refuse("accepted_review_custody_changed");
144
+ const value = JSON.parse(accepted.data.bytes.toString("utf8"));
145
+ const verify = readAcceptedStageData(context, { ...input.scope, stage: "verify" });
146
+ if (value.run_id !== input.scope.runId || value.stage !== "review" || value.schema_id !== accepted.data.data_schema_id
147
+ || value.input_sha256 !== scope.input_sha256 || projection.outcome !== "accepted"
148
+ || hybridDigest(value.predecessors) !== hybridDigest([{ stage: "verify", result: verify.result, input_sha256: verify.input_sha256 }])
149
+ || hybridDigest({ units: value.units, findings: value.findings, outcome: value.outcome, details: value.details }) !== hybridDigest(projection))
150
+ refuse("accepted_result_projection_changed");
151
+ const schema = JSON.parse(input.bundle.descriptor.stageBlob("review", "result_schema").toString("utf8"));
152
+ if (!new Ajv2020({ strict: true, strictRequired: false, validateFormats: false }).compile(schema)(value))
153
+ refuse("accepted_result_schema_changed");
154
+ const prepared = new PreparedAcceptedMemoryCorrectedReview(token, input.scope, accepted.authority, history);
155
+ prepared.revalidate(context);
156
+ return prepared;
157
+ }
158
+ assertCustody(context) {
159
+ this.#history.assertCustody(context);
160
+ if (acceptedReceipt(context, this.#scope).authority !== this.#authority)
161
+ refuse("accepted_review_custody_changed");
162
+ }
163
+ /** Final wave inputs selected by the independently accepted receipt. */
164
+ reviewedSources(context) {
165
+ this.assertCustody(context);
166
+ return this.#history.retainedInputView().run_inputs;
167
+ }
168
+ finalChecks(context) { this.assertCustody(context); return this.#history.finalChecks(context); }
169
+ revalidate(context) { this.#history.revalidate(context); this.assertCustody(context); }
170
+ }
@@ -0,0 +1,191 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { AppError } from "../shared/errors.js";
3
+ import { appendAudit } from "./audit.js";
4
+ import { hybridDigest } from "./hybrid-contracts.js";
5
+ import { isMemoryCorrectionInputOwner } from "./memory-correction-input-owner.js";
6
+ import { PreparedMemoryCorrectionCheckReceipt } from "./memory-correction-check-receipt.js";
7
+ import { memoryCorrectedReviewBatch } from "./memory-corrected-review-batch.js";
8
+ import { addWorkBatch, prepareWorkBatch, readWorkBatchCustody } from "./work-registry.js";
9
+ const token = Symbol("registered-corrected-memory-review");
10
+ const event = "memory.corrected_review_registration";
11
+ function refuse(reason) {
12
+ throw new AppError("memory_corrected_review_registration_invalid", "Corrected reviewers lack independent current-view, checks and allocation custody", 2, { reason, effect: "no_effect" });
13
+ }
14
+ function batchScope(view) {
15
+ return { project_id: view.scope.project_id, run_id: view.scope.run_id, stage: "review", attempt: view.scope.attempt,
16
+ cycle: "", parent_work_id: view.scope.parent_work_id, request_key: `memory-review-wave-${view.wave}` };
17
+ }
18
+ function selected(context, inputs, checks) {
19
+ inputs.assertCustody(context);
20
+ checks.assertOwner(inputs);
21
+ checks.assertRegisteredCustody(context);
22
+ const view = inputs.value(), scope = view.scope;
23
+ const receipt = readRetainedRegistration(context, scope, inputs.sha256, checks.sha256, view.wave);
24
+ if (receipt && hybridDigest(receipt.view) !== hybridDigest(view))
25
+ refuse("changed_registration");
26
+ return receipt;
27
+ }
28
+ /** SQL-only retained allocation proof. Expected identities must be pinned by
29
+ * an independent accepted receipt. This reader never grants current-view or
30
+ * new allocation rights and does not inspect an obsolete private copy. */
31
+ export function readRetainedCorrectedMemoryReviewRegistration(context, scope, inputSha256, checkReceiptSha256) {
32
+ return readRetainedRegistration(context, scope, inputSha256, checkReceiptSha256, 1);
33
+ }
34
+ /** Second historical metadata only, pinned by independent accepted hashes.
35
+ * This is not a current input owner or allocation admission. */
36
+ export function readRetainedSecondCorrectedMemoryReviewRegistration(context, scope, inputSha256, checkReceiptSha256) {
37
+ return readRetainedRegistration(context, scope, inputSha256, checkReceiptSha256, 2);
38
+ }
39
+ function readRetainedRegistration(context, scope, inputSha256, checkReceiptSha256, wave) {
40
+ 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]);
41
+ if (!rows.length)
42
+ return null;
43
+ if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 2 * 1024 * 1024)
44
+ refuse("ambiguous_or_unbounded_registration");
45
+ let receipt;
46
+ try {
47
+ receipt = JSON.parse(rows[0].payload_json);
48
+ }
49
+ catch {
50
+ return refuse("invalid_registration");
51
+ }
52
+ const view = receipt?.view;
53
+ if (!view || hybridDigest(view.scope) !== hybridDigest(scope) || view.wave !== wave
54
+ || hybridDigest(view) !== inputSha256 || !/^[a-f0-9]{64}$/.test(inputSha256)
55
+ || !/^[a-f0-9]{64}$/.test(checkReceiptSha256))
56
+ refuse("changed_retained_view");
57
+ const batch = memoryCorrectedReviewBatch(view), ids = receipt?.work_ids;
58
+ if (!ids || typeof ids !== "object" || Array.isArray(ids)
59
+ || Object.keys(ids).length !== batch.works.length || Object.values(ids).some(id => typeof id !== "string" || !id)
60
+ || new Set(Object.values(ids)).size !== batch.works.length)
61
+ refuse("invalid_work_ids");
62
+ const expected = { schema_id: "dd-flow/memory-corrected-review-registration@1", run_id: scope.run_id, scope, wave: view.wave,
63
+ input_sha256: inputSha256, view, check_receipt_sha256: checkReceiptSha256, batch_sha256: hybridDigest(batch.works), work_ids: ids };
64
+ if (hybridDigest(receipt) !== hybridDigest(expected))
65
+ refuse("changed_registration");
66
+ for (const item of batch.works) {
67
+ const id = ids[item.key];
68
+ if (!id)
69
+ refuse("missing_review_work");
70
+ const custody = readWorkBatchCustody(context, id);
71
+ if (custody.item_key !== item.key || custody.batch_sha256 !== expected.batch_sha256
72
+ || hybridDigest(custody.scope) !== hybridDigest(batchScope(view))
73
+ || hybridDigest(custody.work_ids) !== hybridDigest(ids))
74
+ refuse("changed_work_custody");
75
+ const work = context.db.get("SELECT parent_work_id,task,launch_policy,result_schema,payload_json,depends_on_json FROM works WHERE project_id=? AND run_id=? AND work_id=?", [scope.project_id, scope.run_id, id]);
76
+ let payload, dependencies;
77
+ try {
78
+ payload = JSON.parse(work?.payload_json ?? "null");
79
+ dependencies = JSON.parse(work?.depends_on_json ?? "null");
80
+ }
81
+ catch {
82
+ return refuse("invalid_review_work");
83
+ }
84
+ if (!work || work.parent_work_id !== scope.parent_work_id || work.task !== item.task
85
+ || work.launch_policy !== item.launch_policy || work.result_schema !== item.result_schema
86
+ || hybridDigest(dependencies) !== hybridDigest([])
87
+ || hybridDigest(payload) !== hybridDigest({ ...item.payload, _dd_flow_batch_request: {
88
+ request_sha256: custody.request_sha256, batch_sha256: custody.batch_sha256, item_key: item.key
89
+ } }))
90
+ refuse("changed_review_work");
91
+ }
92
+ return structuredClone(receipt);
93
+ }
94
+ /** A selective wave is allocated only after independent current-view checks.
95
+ * The generic batch and this receipt commit together. Replay never allocates
96
+ * replacements after receipt loss and never grants provider or Session scope. */
97
+ export class PreparedRegisteredCorrectedMemoryReview {
98
+ static isPrepared(value) {
99
+ return typeof value === "object" && value !== null && #inputs in value;
100
+ }
101
+ sha256;
102
+ #inputs;
103
+ #checks;
104
+ #receipt;
105
+ constructor(key, inputs, checks, receipt) {
106
+ if (key !== token)
107
+ refuse("registration_token_required");
108
+ this.#inputs = inputs;
109
+ this.#checks = checks;
110
+ this.#receipt = structuredClone(receipt);
111
+ this.sha256 = hybridDigest(receipt);
112
+ Object.freeze(this);
113
+ }
114
+ static restore(context, inputs, checks) {
115
+ if (!isMemoryCorrectionInputOwner(inputs) || !PreparedMemoryCorrectionCheckReceipt.isPrepared(checks))
116
+ refuse("prepared_registration_owners_required");
117
+ if (context.db.isOwnedWriteTransaction?.())
118
+ refuse("preparation_outside_writer_required");
119
+ checks.assertOwner(inputs);
120
+ checks.revalidate(context);
121
+ checks.assertRegisteredCustody(context);
122
+ const receipt = context.db.readSnapshot(() => selected(context, inputs, checks));
123
+ if (!receipt)
124
+ refuse("registered_corrected_review_required");
125
+ const prepared = new PreparedRegisteredCorrectedMemoryReview(token, inputs, checks, receipt);
126
+ prepared.assertCustody(context);
127
+ return prepared;
128
+ }
129
+ receipt() { return structuredClone(this.#receipt); }
130
+ inputs() { return this.#inputs; }
131
+ batch() { return memoryCorrectedReviewBatch(this.#inputs.value()); }
132
+ assertCustody(context) {
133
+ if (hybridDigest(selected(context, this.#inputs, this.#checks)) !== this.sha256)
134
+ refuse("registered_review_custody_changed");
135
+ }
136
+ revalidate(context) {
137
+ this.#checks.revalidate(context);
138
+ this.assertCustody(context);
139
+ }
140
+ }
141
+ export function registerCorrectedMemoryReview(context, inputs, checks) {
142
+ if (!isMemoryCorrectionInputOwner(inputs) || !PreparedMemoryCorrectionCheckReceipt.isPrepared(checks))
143
+ refuse("prepared_registration_owners_required");
144
+ if (context.db.isOwnedWriteTransaction?.())
145
+ refuse("preparation_outside_writer_required");
146
+ checks.assertOwner(inputs);
147
+ checks.assertRegisteredCustody(context);
148
+ const view = inputs.value(), batch = memoryCorrectedReviewBatch(view), scope = batchScope(view);
149
+ const batchInput = { parentWorkId: scope.parent_work_id, requestKey: scope.request_key, stage: "review", value: batch };
150
+ const prepared = prepareWorkBatch(context, batchInput);
151
+ inputs.revalidate(context);
152
+ checks.revalidate(context);
153
+ const result = context.db.writeTransaction(() => {
154
+ if (!context.db.isOwnedWriteTransaction?.())
155
+ refuse("owned_registration_writer_required");
156
+ const prior = selected(context, inputs, checks);
157
+ if (!prior) {
158
+ // If allocation committed without this receipt, do not manufacture a
159
+ // new wave or adopt an orphaned Work graph from its status alone.
160
+ if (context.db.get("SELECT 1 FROM works w JOIN stage_work_memberships m ON m.work_id=w.work_id AND m.project_id=w.project_id AND m.run_id=w.run_id WHERE w.project_id=? AND w.run_id=? AND w.parent_work_id=? AND m.stage='review' AND m.attempt=? AND json_extract(w.payload_json,'$.kind')='memory-review' AND json_extract(w.payload_json,'$.memory_review.wave')=? LIMIT 1", [scope.project_id, scope.run_id, scope.parent_work_id, scope.attempt, view.wave]))
161
+ refuse("review_registration_missing");
162
+ inputs.assertAllocationAllowed(context);
163
+ }
164
+ const allocated = addWorkBatch(context, { ...batchInput, prepared }), ids = allocated.work_ids;
165
+ if (prior) {
166
+ if (!allocated.replayed || hybridDigest(ids) !== hybridDigest(prior.work_ids))
167
+ refuse("changed_allocation");
168
+ }
169
+ else {
170
+ if (allocated.replayed)
171
+ refuse("review_registration_missing");
172
+ const receipt = { schema_id: "dd-flow/memory-corrected-review-registration@1", run_id: scope.run_id, scope: view.scope, wave: view.wave,
173
+ input_sha256: inputs.sha256, view, check_receipt_sha256: checks.sha256, batch_sha256: hybridDigest(batch.works), work_ids: ids };
174
+ if (Buffer.byteLength(JSON.stringify(receipt)) > 2 * 1024 * 1024)
175
+ refuse("unbounded_registration");
176
+ appendAudit(context, { projectId: scope.project_id, eventType: event, payload: receipt });
177
+ }
178
+ const retained = selected(context, inputs, checks);
179
+ if (!retained)
180
+ refuse("registration_readback_missing");
181
+ return { input_sha256: retained.input_sha256, wave: view.wave, work_ids: { ...retained.work_ids }, replayed: Boolean(prior) };
182
+ }, "memory.registerCorrectedReview");
183
+ try {
184
+ inputs.revalidate(context);
185
+ checks.revalidate(context);
186
+ }
187
+ catch (error) {
188
+ throw new AppError("memory_corrected_review_registration_pending", "Reviewer allocation committed but current-view revalidation needs recovery", 1, { effect: "committed", input_sha256: inputs.sha256, work_ids: result.work_ids, cause: error instanceof Error ? error.message : String(error) });
189
+ }
190
+ return result;
191
+ }