@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,280 @@
1
+ import { AppError } from "../shared/errors.js";
2
+ import { RetainedExecutionBundle } from "./execution-bundles.js";
3
+ import { assertPreparedRunBundleSqlAuthority } from "./runs.js";
4
+ import { PreparedMemoryFinishReview } from "./memory-finish-handler.js";
5
+ import { readMemoryInitBootstrapReadiness } from "./memory-init-bootstrap-readiness.js";
6
+ import { readMemoryInitGitPolicy } from "./memory-init-git-policy.js";
7
+ import { hybridDigest } from "./hybrid-contracts.js";
8
+ import { observeReviewedMemoryGitIntegration, reviewedMemoryGitFiles } from "./memory-git-integration.js";
9
+ import { createHash } from "node:crypto";
10
+ import { readBoundedReleaseFile } from "./release-source.js";
11
+ import { spawnSync } from "node:child_process";
12
+ import { ObservedMemorySources } from "./memory-source-observations.js";
13
+ import { memorySourceKey } from "./memory-stage-results.js";
14
+ import { readMemoryInitProjectBootstrap } from "./memory-init-project-bootstrap.js";
15
+ import { readPublishedMemoryReferenceInventory } from "./memory-layer-publication.js";
16
+ import { readDescriptorStageInput } from "./descriptor-stage-coordinator.js";
17
+ import { appendAudit } from "./audit.js";
18
+ import fs from "node:fs";
19
+ import path from "node:path";
20
+ const token = Symbol("memory-finish-integration");
21
+ function refuse(reason) {
22
+ throw new AppError("memory_finish_integration_invalid", "Finish lacks policy-bound reviewed Git integration", 2, { reason, effect: "no_effect" });
23
+ }
24
+ function retained(context, scope, bundle) {
25
+ assertPreparedRunBundleSqlAuthority(context, scope, bundle);
26
+ const frozen = bundle.execution.snapshot().scope;
27
+ const request = { requestId: frozen.requestId, projectRoot: frozen.projectRoot, workspaceRoot: frozen.workspace };
28
+ const readiness = readMemoryInitBootstrapReadiness(context, request), policy = readMemoryInitGitPolicy(context, request);
29
+ if (!readiness || !policy || readiness.projectId !== scope.projectId
30
+ || readiness.authority.binding.execution_sha256 !== bundle.execution.sha256
31
+ || readiness.authority.binding.git_policy_sha256 !== hybridDigest(policy)
32
+ || policy.target_sha256 !== hybridDigest(readiness.authority.binding.target))
33
+ refuse("policy_bootstrap_binding_changed");
34
+ const selection = { projectRoot: request.projectRoot, workspaceRoot: request.workspaceRoot,
35
+ integrationBranch: policy.integration_branch, gitExecutable: readiness.authority.binding.target.tools.git };
36
+ const gitSha256 = readiness.authority.binding.target.tool_probes?.git.executable_sha256;
37
+ if (typeof gitSha256 !== "string" || !/^[a-f0-9]{64}$/.test(gitSha256))
38
+ refuse("pinned_git_executable_required");
39
+ return { readiness, policy, selection, gitSha256 };
40
+ }
41
+ function assertGitExecutable(selection, sha256) {
42
+ if (createHash("sha256").update(readBoundedReleaseFile(selection.gitExecutable, 64 * 1024 * 1024)).digest("hex") !== sha256)
43
+ refuse("pinned_git_executable_changed");
44
+ }
45
+ /** Physical Git operation only; callers must independently select accepted files
46
+ * and retain each mutation's intent. No ambient Git redirection or whole-tree add. */
47
+ export function integrateReviewedMemoryFiles(selection, environment, references, runId, mutation, policy) {
48
+ const env = Object.fromEntries(Object.entries(environment).filter(([key]) => !key.startsWith("GIT_")));
49
+ const git = (directory, args, write = false, absent = false) => {
50
+ const result = spawnSync(selection.gitExecutable, ["-c", "core.fsmonitor=false", "-C", directory, ...args], { env, encoding: "utf8", timeout: 30000, maxBuffer: 16 * 1024 * 1024, shell: false, windowsHide: true });
51
+ if (absent && !result.error && !result.signal && result.status !== 0)
52
+ return "";
53
+ if (result.error || result.signal || result.status !== 0)
54
+ throw new AppError("memory_finish_integration_invalid", write ? "Git integration did not return success; inspect the original operation before retrying" : "Git integration preparation failed", 2, { reason: "git_operation_failed", effect: write ? "unknown" : "no_effect", operation: args[0], stderr: result.stderr,
55
+ next_action: write ? "reconcile_original_integration" : "resolve_git_configuration_or_workspace" });
56
+ return result.stdout.trim();
57
+ };
58
+ const paths = references.map(ref => ref.path);
59
+ if (!paths.length || paths.length > 4096 || !paths.includes(".memory-bank/index.md") || new Set(paths).size !== paths.length
60
+ || references.some(ref => ref.root !== "workspace" || !(ref.path.startsWith(".memory-bank/") || ref.path === ".mb-lint.json")
61
+ || ref.path.split("/").some(part => !part || part === "." || part === "..") || /[\\\0]/.test(ref.path)
62
+ || !/^[a-f0-9]{64}$/.test(ref.sha256)))
63
+ refuse("invalid_owned_inventory");
64
+ const source = fs.realpathSync(selection.workspaceRoot), target = fs.realpathSync(selection.projectRoot);
65
+ const branch = git(source, ["symbolic-ref", "--short", "HEAD"]);
66
+ if (target === source ? branch !== selection.integrationBranch
67
+ : git(target, ["symbolic-ref", "--short", "HEAD"]) !== selection.integrationBranch)
68
+ refuse("integration_branch_changed");
69
+ const common = (directory) => fs.realpathSync(path.resolve(directory, git(directory, ["rev-parse", "--git-common-dir"])));
70
+ if (common(source) !== common(target))
71
+ refuse("foreign_repository");
72
+ if (policy && (common(source) !== policy.repository || branch !== policy.workspace_branch))
73
+ refuse("policy_repository_or_branch_changed");
74
+ const observed = ObservedMemorySources.capture({ workspace: source }, references.map(({ root, path }) => ({ root, path })));
75
+ const hashes = observed.hashes();
76
+ for (const ref of references)
77
+ if (hashes.get(memorySourceKey(ref.root, ref.path)) !== ref.sha256)
78
+ refuse("reviewed_workspace_bytes_changed");
79
+ try {
80
+ return observeReviewedMemoryGitIntegration(selection, environment, references);
81
+ }
82
+ catch (error) {
83
+ if (!(error instanceof AppError) || error.code !== "memory_git_integration_invalid")
84
+ throw error;
85
+ }
86
+ const visibility = git(source, ["ls-files", "-v", "--", ...paths]);
87
+ if (visibility.split("\n").filter(Boolean).some(line => !line.startsWith("H ")))
88
+ refuse("owned_index_visibility_override");
89
+ if (git(source, ["diff", "--name-only", "--diff-filter=U"]))
90
+ refuse("unmerged_workspace");
91
+ const sourceHead = git(source, ["rev-parse", "--verify", "HEAD"], false, true);
92
+ if (source !== target) {
93
+ const targetHead = git(target, ["rev-parse", "HEAD"]), base = git(source, ["merge-base", targetHead, sourceHead]);
94
+ const changes = git(source, ["diff", "--name-only", "-z", base, sourceHead]).split("\0").filter(Boolean);
95
+ if (changes.some(logical => !paths.includes(logical)))
96
+ refuse("feature_contains_unowned_changes");
97
+ if (git(target, ["diff", "--cached", "--name-only"]) || git(target, ["diff", "--name-only", "--diff-filter=U"]))
98
+ refuse("integration_target_index_not_clean");
99
+ }
100
+ const needsCommit = references.some(ref => {
101
+ const bytes = spawnSync(selection.gitExecutable, ["-C", source, "cat-file", "blob", `HEAD:${ref.path}`], { env, timeout: 10000, maxBuffer: 16 * 1024 * 1024, shell: false, windowsHide: true });
102
+ return bytes.status !== 0 || createHash("sha256").update(bytes.stdout).digest("hex") !== ref.sha256;
103
+ });
104
+ if (needsCommit) {
105
+ git(source, ["var", "GIT_AUTHOR_IDENT"]);
106
+ git(source, ["var", "GIT_COMMITTER_IDENT"]);
107
+ mutation("commit", sourceHead, () => {
108
+ observed.revalidate();
109
+ if (git(source, ["rev-parse", "--verify", "HEAD"], false, true) !== sourceHead
110
+ || git(source, ["symbolic-ref", "--short", "HEAD"]) !== branch)
111
+ refuse("source_git_baseline_changed");
112
+ git(source, ["add", "-f", "--", ...paths], true);
113
+ git(source, ["commit", "--only", "-m", `chore(${runId.toLowerCase()}): integrate reviewed Memory Bank`, "--", ...paths], true);
114
+ });
115
+ }
116
+ observed.revalidate();
117
+ if (source !== target) {
118
+ const head = git(source, ["rev-parse", "HEAD"]), targetHead = git(target, ["rev-parse", "HEAD"]);
119
+ if (head !== targetHead) {
120
+ mutation("merge", targetHead, () => {
121
+ if (git(target, ["rev-parse", "HEAD"]) !== targetHead
122
+ || git(target, ["symbolic-ref", "--short", "HEAD"]) !== selection.integrationBranch
123
+ || git(target, ["diff", "--cached", "--name-only"]))
124
+ refuse("target_git_baseline_changed");
125
+ git(target, ["merge", "--no-edit", "--no-autostash", head], true);
126
+ });
127
+ }
128
+ }
129
+ return observeReviewedMemoryGitIntegration(selection, environment, references);
130
+ }
131
+ /** The admitted Memory finish operation. A retained intent never licenses a
132
+ * second dispatch: replay first observes completed Git effects, or reports unknown. */
133
+ export function integrateMemoryFinish(context, input) {
134
+ if (!(input.bundle instanceof RetainedExecutionBundle) || input.bundle.descriptor.descriptor.id !== "mb-init")
135
+ refuse("registered_memory_bundle_required");
136
+ if (context.db.isOwnedWriteTransaction?.())
137
+ refuse("preparation_outside_writer_required");
138
+ const stageScope = { ...input.scope, stage: "finish" };
139
+ readDescriptorStageInput(context, stageScope, input.bundle);
140
+ const review = PreparedMemoryFinishReview.prepare(context, input);
141
+ const selected = context.db.readSnapshot(() => retained(context, input.scope, input.bundle));
142
+ const request = { requestId: selected.policy.scope.requestId, projectRoot: selected.selection.projectRoot,
143
+ workspaceRoot: selected.selection.workspaceRoot };
144
+ const bootstrap = readMemoryInitProjectBootstrap(context, request);
145
+ if (!bootstrap)
146
+ refuse("accepted_bootstrap_required");
147
+ const published = readPublishedMemoryReferenceInventory(context.ddFlowHome, { ...request, targetSha256: bootstrap.receipt.target_sha256,
148
+ layerSha256: bootstrap.receipt.layer_sha256, installationSha256: selected.readiness.authority.binding.installation_manifest_sha256,
149
+ packSha256: selected.readiness.authority.binding.pack_sha256 }, selected.readiness.authority.binding.target, bootstrap.receipt.layer_publication_sha256, bootstrap.receipt.draft_identity_sha256);
150
+ const files = new Map(reviewedMemoryGitFiles(review.reviewedSources(context)).map(ref => [ref.path, ref]));
151
+ for (const [logical, sha256] of published.artifacts) {
152
+ const ref = { root: "workspace", path: logical, sha256 };
153
+ if (files.has(ref.path)) {
154
+ if (published.canon.has(logical.slice(".memory-bank/".length)) && files.get(ref.path).sha256 !== ref.sha256)
155
+ refuse("selected_canon_changed");
156
+ }
157
+ else
158
+ files.set(ref.path, ref);
159
+ }
160
+ const references = [...files.values()].sort((a, b) => a.path.localeCompare(b.path, "en"));
161
+ const identity = hybridDigest({ scope: input.scope, bundle: input.bundle.sha256, policy: selected.policy,
162
+ review: review.bytes().toString("base64"), references });
163
+ const events = context.db.all(`SELECT payload_json FROM audit_events WHERE project_id=?
164
+ AND event_type='memory.finish_integration_intent' AND json_extract(payload_json,'$.run_id')=?`, [input.scope.projectId, input.scope.runId]);
165
+ const intents = events.map(row => JSON.parse(row.payload_json));
166
+ if (intents.length > 2 || intents.some(value => value.identity !== identity))
167
+ refuse("integration_intent_changed");
168
+ assertGitExecutable(selected.selection, selected.gitSha256);
169
+ if (intents.length) {
170
+ try {
171
+ observeReviewedMemoryGitIntegration(selected.selection, context.env, references);
172
+ const accepted = PreparedMemoryFinishIntegration.prepare(context, input);
173
+ return { integrated: true, replayed: true, integration: accepted.value() };
174
+ }
175
+ catch (error) {
176
+ const commit = intents.length === 1 && intents[0].phase === "commit" ? intents[0] : null;
177
+ const result = spawnSync(selected.selection.gitExecutable, ["-C", selected.selection.workspaceRoot, "log", "-1", "--format=%P%n%s"], { env: Object.fromEntries(Object.entries(context.env).filter(([key]) => !key.startsWith("GIT_"))), encoding: "utf8", timeout: 10000 });
178
+ // Only a physically completed original scoped commit can continue to the
179
+ // not-yet-issued merge. Never rerun a commit or unresolved merge intent.
180
+ if (!commit || result.status !== 0 || result.stdout.trim() !== `${commit.baseline}\nchore(${input.scope.runId.toLowerCase()}): integrate reviewed Memory Bank`.trim()
181
+ || selected.selection.workspaceRoot === selected.selection.projectRoot)
182
+ throw new AppError("memory_finish_integration_pending", "Original integration has not been physically proven; do not redispatch Git mutations", 1, { effect: "unknown", next_action: "reconcile_original_integration", cause: error instanceof AppError ? error.details.reason : "observation_failed" });
183
+ }
184
+ }
185
+ let dispatched = false;
186
+ try {
187
+ const integration = integrateReviewedMemoryFiles(selected.selection, context.env, references, input.scope.runId, (phase, baseline, action) => {
188
+ if (intents.some(value => value.phase === phase))
189
+ throw new AppError("memory_finish_integration_pending", "Original Git mutation remains unresolved", 1, { effect: "unknown", next_action: "reconcile_original_integration" });
190
+ review.revalidate(context);
191
+ assertGitExecutable(selected.selection, selected.gitSha256);
192
+ context.db.writeTransaction(() => {
193
+ readDescriptorStageInput(context, stageScope, input.bundle);
194
+ review.assertCustody(context);
195
+ if (hybridDigest(retained(context, input.scope, input.bundle)) !== hybridDigest(selected))
196
+ refuse("integration_custody_changed");
197
+ const current = context.db.all(`SELECT payload_json FROM audit_events WHERE project_id=?
198
+ AND event_type='memory.finish_integration_intent' AND json_extract(payload_json,'$.run_id')=?`, [input.scope.projectId, input.scope.runId])
199
+ .map(row => JSON.parse(row.payload_json));
200
+ if (current.some(value => value.identity !== identity || value.phase === phase) || current.length > 1)
201
+ throw new AppError("memory_finish_integration_pending", "The original Git mutation already has an owner; observe its effect without redispatch", 1, { effect: "unknown", next_action: "reconcile_original_integration" });
202
+ appendAudit(context, { projectId: input.scope.projectId, eventType: "memory.finish_integration_intent",
203
+ payload: { run_id: input.scope.runId, identity, phase, baseline } });
204
+ }, "memoryFinish.integrate");
205
+ dispatched = true;
206
+ action();
207
+ }, selected.policy);
208
+ return { integrated: true, replayed: false, integration };
209
+ }
210
+ catch (error) {
211
+ if (!dispatched)
212
+ throw error;
213
+ throw new AppError("memory_finish_integration_pending", "The retained original Git operation requires physical reconciliation; do not redispatch", 1, { effect: "unknown", next_action: "reconcile_original_integration", cause: error instanceof AppError ? error.details.reason : "git_operation_failed" });
214
+ }
215
+ }
216
+ /** Fresh terminal integration proof: independent accepted review + explicit
217
+ * retained policy/bootstrap + exact committed reviewed MB bytes. Read-only:
218
+ * no Git integration is performed and no normal-flow readiness, Stage/RUN
219
+ * completion, permission or historical replay authority is granted here. */
220
+ export class PreparedMemoryFinishIntegration {
221
+ static isPrepared(value) {
222
+ return typeof value === "object" && value !== null && #review in value;
223
+ }
224
+ #scope;
225
+ #bundle;
226
+ #review;
227
+ #retainedSha256;
228
+ #selection;
229
+ #observation;
230
+ #gitSha256;
231
+ constructor(key, scope, bundle, review, selected, observation) {
232
+ if (key !== token)
233
+ refuse("finish_integration_token_required");
234
+ this.#scope = { ...scope };
235
+ this.#bundle = bundle;
236
+ this.#review = review;
237
+ this.#retainedSha256 = hybridDigest(selected);
238
+ this.#selection = structuredClone(selected.selection);
239
+ this.#observation = structuredClone(observation);
240
+ this.#gitSha256 = selected.gitSha256;
241
+ Object.freeze(this);
242
+ }
243
+ static prepare(context, input) {
244
+ if (!(input.bundle instanceof RetainedExecutionBundle) || input.bundle.descriptor.descriptor.id !== "mb-init")
245
+ refuse("registered_memory_bundle_required");
246
+ if (context.db.isOwnedWriteTransaction?.())
247
+ refuse("preparation_outside_writer_required");
248
+ const review = PreparedMemoryFinishReview.prepare(context, input);
249
+ const selected = context.db.readSnapshot(() => retained(context, input.scope, input.bundle));
250
+ const references = reviewedMemoryGitFiles(review.reviewedSources(context));
251
+ assertGitExecutable(selected.selection, selected.gitSha256);
252
+ const observation = observeReviewedMemoryGitIntegration(selected.selection, context.env, references);
253
+ assertGitExecutable(selected.selection, selected.gitSha256);
254
+ if (observation.repository !== selected.policy.repository || observation.workspace_branch !== selected.policy.workspace_branch)
255
+ refuse("policy_repository_or_branch_changed");
256
+ const prepared = new PreparedMemoryFinishIntegration(token, input.scope, input.bundle, review, selected, observation);
257
+ prepared.revalidate(context);
258
+ return prepared;
259
+ }
260
+ value() { return structuredClone(this.#observation); }
261
+ /** SQL-only writer fence; physical observation must precede the writer. */
262
+ assertCustody(context) {
263
+ this.#review.assertCustody(context);
264
+ if (hybridDigest(retained(context, this.#scope, this.#bundle)) !== this.#retainedSha256)
265
+ refuse("finish_integration_custody_changed");
266
+ }
267
+ revalidate(context) {
268
+ const review = this.#review;
269
+ if (context.db.isOwnedWriteTransaction?.())
270
+ refuse("physical_revalidation_outside_writer_required");
271
+ review.revalidate(context);
272
+ this.assertCustody(context);
273
+ const references = reviewedMemoryGitFiles(this.#review.reviewedSources(context));
274
+ assertGitExecutable(this.#selection, this.#gitSha256);
275
+ if (hybridDigest(observeReviewedMemoryGitIntegration(this.#selection, context.env, references)) !== hybridDigest(this.#observation))
276
+ refuse("finish_integration_observation_changed");
277
+ assertGitExecutable(this.#selection, this.#gitSha256);
278
+ this.assertCustody(context);
279
+ }
280
+ }
@@ -0,0 +1,122 @@
1
+ var _a;
2
+ import { createHash } from "node:crypto";
3
+ import { AppError } from "../shared/errors.js";
4
+ import { RetainedExecutionBundle } from "./execution-bundles.js";
5
+ import { PreparedMemoryFinishIntegration } from "./memory-finish-integration.js";
6
+ import { PreparedMemoryFinishReview } from "./memory-finish-handler.js";
7
+ import { observeMemoryNormalFlowEntry } from "./memory-normal-flow-entry.js";
8
+ import { readMemoryInitBootstrapReadiness } from "./memory-init-bootstrap-readiness.js";
9
+ import { readMemoryInitProjectBootstrap } from "./memory-init-project-bootstrap.js";
10
+ import { readRetainedMemoryLayer } from "./memory-layer-publication.js";
11
+ import { ObservedMemorySources } from "./memory-source-observations.js";
12
+ import { readBoundedReleaseFile } from "./release-source.js";
13
+ import { memorySourceKey } from "./memory-stage-results.js";
14
+ import { hybridDigest } from "./hybrid-contracts.js";
15
+ const token = Symbol("memory-finish-readiness");
16
+ function refuse(reason) {
17
+ throw new AppError("memory_finish_readiness_invalid", "Finish lacks reviewed ordinary-flow readiness", 2, { reason, effect: "no_effect" });
18
+ }
19
+ function accepted(context, scope, bundle) {
20
+ const frozen = bundle.execution.snapshot().scope;
21
+ const request = { requestId: frozen.requestId, projectRoot: frozen.projectRoot, workspaceRoot: frozen.workspace };
22
+ const bootstrap = readMemoryInitBootstrapReadiness(context, request), registration = readMemoryInitProjectBootstrap(context, request);
23
+ if (!bootstrap || !registration || bootstrap.projectId !== scope.projectId
24
+ || bootstrap.authority.binding.execution_sha256 !== bundle.execution.sha256)
25
+ refuse("accepted_bootstrap_required");
26
+ return { request, bootstrap, registration };
27
+ }
28
+ function tools(selected) {
29
+ const target = selected.bootstrap.authority.binding.target;
30
+ for (const name of ["git", "wt", "mb-lint"]) {
31
+ const expected = target.tool_probes?.[name].executable_sha256;
32
+ if (!expected || createHash("sha256").update(readBoundedReleaseFile(target.tools[name], 64 * 1024 * 1024)).digest("hex") !== expected)
33
+ refuse("selected_tool_changed");
34
+ }
35
+ }
36
+ /** Fresh readiness requires accepted final review, integrated reviewed bytes,
37
+ * unchanged selected canonical pack/tools and real ordinary SPECIFY preparation
38
+ * from the integration checkout. It does not execute the future task or seal a
39
+ * Stage/RUN; historical replay must use independently committed evidence. */
40
+ export class PreparedMemoryFinishReadiness {
41
+ static isPrepared(value) {
42
+ return typeof value === "object" && value !== null && #integration in value;
43
+ }
44
+ #scope;
45
+ #bundle;
46
+ #integration;
47
+ #review;
48
+ #acceptedSha256;
49
+ #value;
50
+ constructor(key, scope, bundle, integration, review, acceptedSha256, value) {
51
+ if (key !== token)
52
+ refuse("readiness_token_required");
53
+ this.#scope = { ...scope };
54
+ this.#bundle = bundle;
55
+ this.#integration = integration;
56
+ this.#review = review;
57
+ this.#acceptedSha256 = acceptedSha256;
58
+ this.#value = structuredClone(value);
59
+ Object.freeze(this);
60
+ }
61
+ static observe(context, scope, bundle, review, integration) {
62
+ const selected = context.db.readSnapshot(() => accepted(context, scope, bundle));
63
+ tools(selected);
64
+ const layer = readRetainedMemoryLayer(context.ddFlowHome, { ...selected.request,
65
+ targetSha256: selected.registration.receipt.target_sha256, layerSha256: selected.registration.receipt.layer_sha256,
66
+ installationSha256: selected.bootstrap.authority.binding.installation_manifest_sha256 });
67
+ if (layer.packSha256 !== selected.bootstrap.authority.binding.pack_sha256)
68
+ refuse("selected_pack_changed");
69
+ const files = layer.files();
70
+ for (const root of new Set([selected.request.projectRoot, selected.request.workspaceRoot])) {
71
+ const observed = ObservedMemorySources.capture({ project: root }, [...files.keys()].map(logical => ({ root: "project", path: `.memory-bank/${logical}` })));
72
+ const hashes = observed.hashes();
73
+ for (const [logical, bytes] of files)
74
+ if (hashes.get(memorySourceKey("project", `.memory-bank/${logical}`)) !== createHash("sha256").update(bytes).digest("hex"))
75
+ refuse("installed_pack_bytes_changed");
76
+ observed.revalidate();
77
+ }
78
+ const entry = observeMemoryNormalFlowEntry(context, selected.request.projectRoot);
79
+ const reviewed = new Map(review.reviewedSources(context).filter(ref => ref.root === "workspace").map(ref => [ref.path, ref.sha256]));
80
+ for (const source of entry.sources) {
81
+ const canonical = files.get(source.path.slice(".memory-bank/".length));
82
+ if (canonical ? createHash("sha256").update(canonical).digest("hex") !== source.sha256 : reviewed.get(source.path) !== source.sha256)
83
+ refuse("entry_source_not_reviewed_or_selected_canon");
84
+ }
85
+ tools(selected);
86
+ integration.revalidate(context);
87
+ review.assertCustody(context);
88
+ return { schema_id: "dd-flow/memory-finish-readiness-observation@1", scope: { ...scope },
89
+ bundle_sha256: bundle.sha256, execution_sha256: bundle.execution.sha256,
90
+ bootstrap_authority_sha256: hybridDigest(selected.bootstrap.authority), pack_sha256: layer.packSha256,
91
+ integration: integration.value(), entry, readiness: "entry_prepared", model_access: "unknown" };
92
+ }
93
+ static prepare(context, input) {
94
+ if (!(input.bundle instanceof RetainedExecutionBundle) || input.bundle.descriptor.descriptor.id !== "mb-init")
95
+ refuse("registered_memory_bundle_required");
96
+ if (context.db.isOwnedWriteTransaction?.())
97
+ refuse("preparation_outside_writer_required");
98
+ const integration = PreparedMemoryFinishIntegration.prepare(context, input), review = PreparedMemoryFinishReview.prepare(context, input);
99
+ const selected = context.db.readSnapshot(() => accepted(context, input.scope, input.bundle));
100
+ const value = this.observe(context, input.scope, input.bundle, review, integration);
101
+ const prepared = new _a(token, input.scope, input.bundle, integration, review, hybridDigest(selected), value);
102
+ prepared.assertCustody(context);
103
+ return prepared;
104
+ }
105
+ value() { return structuredClone(this.#value); }
106
+ assertCustody(context) {
107
+ this.#integration.assertCustody(context);
108
+ this.#review.assertCustody(context);
109
+ if (hybridDigest(accepted(context, this.#scope, this.#bundle)) !== this.#acceptedSha256)
110
+ refuse("readiness_custody_changed");
111
+ }
112
+ revalidate(context) {
113
+ const integration = this.#integration;
114
+ if (context.db.isOwnedWriteTransaction?.())
115
+ refuse("physical_revalidation_outside_writer_required");
116
+ this.assertCustody(context);
117
+ if (hybridDigest(_a.observe(context, this.#scope, this.#bundle, this.#review, integration)) !== hybridDigest(this.#value))
118
+ refuse("ordinary_readiness_changed");
119
+ this.assertCustody(context);
120
+ }
121
+ }
122
+ _a = PreparedMemoryFinishReadiness;
@@ -0,0 +1,151 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+ import { createHash } from "node:crypto";
5
+ /** One reviewed inventory for fresh Git observation and historical terminal custody. */
6
+ export function reviewedMemoryGitFiles(refs) {
7
+ return refs.filter(ref => ref.root === "workspace" && (ref.path.startsWith(".memory-bank/") || ref.path === ".mb-lint.json"))
8
+ .map(ref => ({ root: "workspace", path: ref.path, sha256: ref.sha256 }))
9
+ .sort((a, b) => a.path.localeCompare(b.path, "en"));
10
+ }
11
+ import { AppError } from "../shared/errors.js";
12
+ import { hybridDigest } from "./hybrid-contracts.js";
13
+ import { ObservedMemorySources } from "./memory-source-observations.js";
14
+ import { memorySourceKey } from "./memory-stage-results.js";
15
+ function refuse(reason) {
16
+ throw new AppError("memory_git_integration_invalid", "Memory finish lacks observed Git integration", 2, { reason, effect: "no_effect" });
17
+ }
18
+ function root(value) {
19
+ if (typeof value !== "string" || !path.isAbsolute(value))
20
+ refuse("absolute_root_required");
21
+ try {
22
+ const resolved = fs.realpathSync(value);
23
+ if (!fs.statSync(resolved).isDirectory())
24
+ refuse("repository_root_required");
25
+ return resolved;
26
+ }
27
+ catch {
28
+ return refuse("repository_root_unavailable");
29
+ }
30
+ }
31
+ /** Read-only physical observation, NOT policy selection, accepted MB content,
32
+ * full owned-file coverage or SQL/Stage authority. The finish owner must bind
33
+ * this selection to retained project policy and independently reviewed bytes.
34
+ * No checkout, commit, merge, push, tool repair or configuration mutation. */
35
+ export function observeMemoryGitIntegration(input, env) {
36
+ if (!input || typeof input.integrationBranch !== "string" || !input.integrationBranch
37
+ || input.integrationBranch.length > 1024 || [...input.integrationBranch].some(character => character.charCodeAt(0) <= 32 || character.charCodeAt(0) === 127)
38
+ || input.integrationBranch.startsWith("-") || typeof input.gitExecutable !== "string"
39
+ || !path.isAbsolute(input.gitExecutable))
40
+ refuse("explicit_git_selection_required");
41
+ const projectRoot = root(input.projectRoot), workspaceRoot = root(input.workspaceRoot);
42
+ const cleanEnv = Object.fromEntries(Object.entries(env).filter(([key]) => !key.startsWith("GIT_")));
43
+ const git = (directory, args, expectedStatus = 0) => {
44
+ const result = spawnSync(input.gitExecutable, ["-c", "core.fsmonitor=false", "-c", "core.untrackedCache=false",
45
+ "-C", directory, ...args], { env: { ...cleanEnv, GIT_OPTIONAL_LOCKS: "0" }, encoding: "utf8",
46
+ timeout: 10000, maxBuffer: 2 * 1024 * 1024, windowsHide: true, shell: false });
47
+ if (result.error || result.signal || result.status !== expectedStatus)
48
+ refuse("git_observation_failed");
49
+ return result.stdout;
50
+ };
51
+ const integrationRef = `refs/heads/${input.integrationBranch}`;
52
+ git(projectRoot, ["check-ref-format", integrationRef]);
53
+ for (const directory of [projectRoot, workspaceRoot]) {
54
+ if (git(directory, ["rev-parse", "--is-bare-repository"]).trim() !== "false"
55
+ || root(git(directory, ["rev-parse", "--show-toplevel"]).trim()) !== directory)
56
+ refuse("not_repository_root");
57
+ }
58
+ const common = (directory) => root(path.resolve(directory, git(directory, ["rev-parse", "--git-common-dir"]).trim()));
59
+ const repository = common(projectRoot);
60
+ if (common(workspaceRoot) !== repository)
61
+ refuse("foreign_workspace_repository");
62
+ const workspaceBranch = git(workspaceRoot, ["symbolic-ref", "--short", "HEAD"]).trim();
63
+ const head = git(workspaceRoot, ["rev-parse", "--verify", "HEAD^{commit}"]).trim();
64
+ const integrationHead = git(projectRoot, ["rev-parse", "--verify", `${integrationRef}^{commit}`]).trim();
65
+ if (![head, integrationHead].every(value => /^[a-f0-9]{40,64}$/.test(value)))
66
+ refuse("invalid_commit_identity");
67
+ // status intentionally trusts index visibility flags. Do not accept that
68
+ // trust for MB readiness or mutate the user's index to clear those flags.
69
+ const visibility = git(workspaceRoot, ["ls-files", "-v", "-z", "--", ".memory-bank"]);
70
+ if (visibility.split("\0").filter(Boolean).some(entry => !entry.startsWith("H ")))
71
+ refuse("memory_index_visibility_override");
72
+ const status = git(workspaceRoot, ["status", "--porcelain=v1", "-z", "--untracked-files=all", "--", ".memory-bank"]);
73
+ if (status)
74
+ refuse("memory_bank_not_committed");
75
+ const index = git(workspaceRoot, ["ls-files", "--stage", "-z", "--", ".memory-bank/index.md"]);
76
+ if (!/^100644 [a-f0-9]{40,64} 0\t\.memory-bank\/index\.md\0$/.test(index))
77
+ refuse("tracked_memory_bank_index_required");
78
+ // Failure is a typed refusal; neither an unmerged feature nor an unborn
79
+ // branch can become an accepted integration by supplying its own hash.
80
+ git(projectRoot, ["merge-base", "--is-ancestor", head, integrationHead]);
81
+ const workspaceMemoryTree = git(workspaceRoot, ["rev-parse", "--verify", `${head}:.memory-bank`]).trim();
82
+ const integrationMemoryTree = git(projectRoot, ["rev-parse", "--verify", `${integrationHead}:.memory-bank`]).trim();
83
+ if (!/^[a-f0-9]{40,64}$/.test(workspaceMemoryTree) || workspaceMemoryTree !== integrationMemoryTree)
84
+ refuse("integrated_memory_tree_changed");
85
+ return { schema_id: "dd-flow/memory-git-integration-observation@1", project_root: projectRoot,
86
+ workspace_root: workspaceRoot, repository, workspace_branch: workspaceBranch, integration_ref: integrationRef, workspace_head: head,
87
+ integration_head: integrationHead, memory_tree: workspaceMemoryTree,
88
+ tracked_index_entry_sha256: hybridDigest(index), memory_index_visibility_sha256: hybridDigest(visibility),
89
+ memory_status_sha256: hybridDigest(status) };
90
+ }
91
+ /** Exact reviewed MB/owned root lint bytes in the physical workspace and integrated Git
92
+ * tree. References must be selected by the accepted review owner; this helper
93
+ * validates physical correspondence only and grants no semantic authority. */
94
+ export function observeReviewedMemoryGitIntegration(input, env, references) {
95
+ if (!Array.isArray(references) || !references.length || references.length > 4096
96
+ || references.some(ref => !ref || ref.root !== "workspace" || typeof ref.path !== "string"
97
+ || !(ref.path.startsWith(".memory-bank/") || ref.path === ".mb-lint.json") || ref.path.length > 4096 || ref.path.includes("\\") || ref.path.includes("\0")
98
+ || ref.path.split("/").some((segment) => !segment || segment === "." || segment === "..")
99
+ || typeof ref.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(ref.sha256)
100
+ || Object.keys(ref).sort().join() !== "path,root,sha256")
101
+ || new Set(references.map(ref => ref.path)).size !== references.length
102
+ || !references.some(ref => ref.path === ".memory-bank/index.md"))
103
+ refuse("invalid_reviewed_memory_inventory");
104
+ const selected = structuredClone([...references]).sort((a, b) => a.path.localeCompare(b.path, "en"));
105
+ const integration = observeMemoryGitIntegration(input, env);
106
+ const observed = ObservedMemorySources.capture({ workspace: integration.workspace_root }, selected.map(({ root, path }) => ({ root, path })));
107
+ const hashes = observed.hashes();
108
+ const cleanEnv = Object.fromEntries(Object.entries(env).filter(([key]) => !key.startsWith("GIT_")));
109
+ const git = (args, directory = integration.workspace_root) => {
110
+ const result = spawnSync(input.gitExecutable, ["-c", "core.fsmonitor=false", "-c", "core.untrackedCache=false",
111
+ "-C", directory, ...args], { env: { ...cleanEnv, GIT_OPTIONAL_LOCKS: "0" },
112
+ timeout: 10000, maxBuffer: 16 * 1024 * 1024, windowsHide: true, shell: false });
113
+ if (result.error || result.signal || result.status !== 0)
114
+ refuse("reviewed_git_blob_unavailable");
115
+ return result.stdout;
116
+ };
117
+ let total = 0;
118
+ for (const ref of selected) {
119
+ if (hashes.get(memorySourceKey("workspace", ref.path)) !== ref.sha256)
120
+ refuse("reviewed_workspace_bytes_changed");
121
+ const entry = git(["ls-tree", "-z", integration.workspace_head, "--", ref.path]).toString("utf8");
122
+ const separator = entry.indexOf("\t");
123
+ if (separator < 0 || !/^100644 blob [a-f0-9]{40,64}$/.test(entry.slice(0, separator))
124
+ || entry.slice(separator + 1) !== `${ref.path}\0`)
125
+ refuse("reviewed_regular_git_blob_required");
126
+ const content = git(["cat-file", "blob", `${integration.workspace_head}:${ref.path}`]);
127
+ total += content.length;
128
+ if (total > 64 * 1024 * 1024)
129
+ refuse("reviewed_git_bytes_exceed_bound");
130
+ if (createHash("sha256").update(content).digest("hex") !== ref.sha256)
131
+ refuse("reviewed_git_bytes_changed");
132
+ if (ref.path === ".mb-lint.json") {
133
+ for (const root of new Set([integration.workspace_root, integration.project_root])) {
134
+ const visibility = git(["ls-files", "-v", "-z", "--", ref.path], root).toString("utf8");
135
+ if (visibility !== `H ${ref.path}\0`)
136
+ refuse("lint_configuration_index_visibility_override");
137
+ const source = ObservedMemorySources.capture({ workspace: root }, [{ root: "workspace", path: ref.path }]);
138
+ if (source.hashes().get(memorySourceKey("workspace", ref.path)) !== ref.sha256)
139
+ refuse("integrated_lint_configuration_changed");
140
+ source.revalidate();
141
+ }
142
+ const integrated = git(["cat-file", "blob", `${integration.integration_head}:${ref.path}`], integration.project_root);
143
+ if (createHash("sha256").update(integrated).digest("hex") !== ref.sha256)
144
+ refuse("integrated_lint_configuration_changed");
145
+ }
146
+ }
147
+ observed.revalidate();
148
+ if (hybridDigest(observeMemoryGitIntegration(input, env)) !== hybridDigest(integration))
149
+ refuse("integration_changed_during_observation");
150
+ return { ...integration, reviewed_files_sha256: hybridDigest(selected) };
151
+ }