@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,614 @@
1
+ import path from "node:path";
2
+ import { createHash } from "node:crypto";
3
+ import { AppError } from "../shared/errors.js";
4
+ import { RetainedExecutionBundle } from "./execution-bundles.js";
5
+ import { hybridDigest } from "./hybrid-contracts.js";
6
+ import { PreparedRetainedSecondMemoryRepairs } from "./memory-retained-second-repairs.js";
7
+ import { PreparedRetainedSecondMemoryBaseline } from "./memory-retained-second-baseline.js";
8
+ import { PreparedMemoryRepairWorkspace } from "./memory-repair-workspace.js";
9
+ import { PreparedDescriptorStageCoordinator } from "./descriptor-stage-coordinator.js";
10
+ import { readDescriptorCoordinatorSession } from "./descriptor-coordinator-session.js";
11
+ import { reviewSourceHash } from "./review-copy.js";
12
+ import { runtimeHarness } from "./execution-policy.js";
13
+ import { appendAudit } from "./audit.js";
14
+ import { memoryRepairCurrentReferences, validateMemoryRepairWorkResult } from "./memory-repair-work-results.js";
15
+ import { RetainedMemoryRepairPublicationPlan } from "./memory-repair-publication-plan.js";
16
+ import { retainedMemoryRepairPublicationPlans } from "./memory-repair-publication-intent.js";
17
+ import { deriveRetainedMemoryRepairViews } from "./memory-repair-publication.js";
18
+ import { readMemoryRepairPhysicalBinding, recordMemoryRepairNativeBinding, memoryRepairExecutionIdentity } from "./memory-repair-physical-binding.js";
19
+ function publicationAction(scope, plan, publicationSha256, workSessionId, index) {
20
+ const action = plan.actions()[index];
21
+ if (!action)
22
+ refuse("invalid_publication_action_index");
23
+ return { schema_id: "dd-flow/memory-repair-publication-action-intent@1", project_id: scope.project_id, run_id: scope.run_id,
24
+ work_id: scope.work_id, generation: scope.generation, work_session_id: workSessionId, publication_intent_sha256: publicationSha256,
25
+ index, source_root: scope.workspace_root, staging_directory: path.join(scope.workspace_root, ".dd-flow", "memory-repair-publications", publicationSha256),
26
+ staging_slot: action.after ? `action-${index}.tmp` : null, before_sha256: plan.state(index).sha256,
27
+ after_sha256: plan.state(index + 1).sha256, action_sha256: hybridDigest(action) };
28
+ }
29
+ const token = Symbol("second-memory-repair-launch");
30
+ function refuse(reason) {
31
+ throw new AppError("memory_second_repair_launch_invalid", "Second repair launch lacks frozen profile, baseline and owned copy custody", 2, { reason, effect: "no_effect" });
32
+ }
33
+ function launchRow(context, workId, route, operationId) {
34
+ const row = readMemoryRepairPhysicalBinding(context, workId, route, operationId);
35
+ if (!row || !row.provider_session_id || !["launching", "start_requested"].includes(row.status)
36
+ || typeof row.profile_json !== "string" || Buffer.byteLength(row.profile_json) > 1024 * 1024)
37
+ refuse("registered_physical_launch_required");
38
+ return row;
39
+ }
40
+ function journal(context, scope, event) {
41
+ 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,'$.work_id')=? AND json_extract(payload_json,'$.generation')=? LIMIT 2", [scope.project_id, event, scope.run_id, scope.work_id, scope.generation]);
42
+ if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 12 * 1024 * 1024)
43
+ refuse("registered_copy_journal_required");
44
+ try {
45
+ return JSON.parse(rows[0].payload_json);
46
+ }
47
+ catch {
48
+ return refuse("invalid_copy_journal");
49
+ }
50
+ }
51
+ /** Read-only pending physical launch proof. No provider dispatch, Hook claim,
52
+ * WorkSession/start or output/publication rights are granted by preparation. */
53
+ export class PreparedSecondMemoryRepairLaunch {
54
+ static isPrepared(value) {
55
+ return typeof value === "object" && value !== null && #baseline in value;
56
+ }
57
+ #baseline;
58
+ #launch;
59
+ #root;
60
+ #runRoot;
61
+ #workspace;
62
+ #intent;
63
+ #build;
64
+ #bundle;
65
+ #coordinator;
66
+ #session;
67
+ #registration;
68
+ #claimToken = null;
69
+ #restoredStart = null;
70
+ #claim = null;
71
+ constructor(key, baseline, launch, root, runRoot, workspace, intent, build, bundle, coordinator, session, registration) {
72
+ if (key !== token)
73
+ refuse("launch_token_required");
74
+ this.#baseline = baseline;
75
+ this.#launch = structuredClone(launch);
76
+ this.#root = root;
77
+ this.#runRoot = runRoot;
78
+ this.#workspace = workspace;
79
+ this.#intent = structuredClone(intent);
80
+ this.#build = structuredClone(build);
81
+ this.#bundle = bundle;
82
+ this.#coordinator = coordinator;
83
+ this.#session = session;
84
+ this.#registration = registration;
85
+ Object.freeze(this);
86
+ }
87
+ static prepare(context, input) {
88
+ return this.#prepare(context, input, null);
89
+ }
90
+ /** Restore SQL start custody only; mutable private/shared documents are not
91
+ * recaptured, and this value cannot claim another Session or publish output. */
92
+ static restoreForStartedWork(context, input) {
93
+ if (typeof input.workSessionId !== "string" || !input.workSessionId || input.workSessionId.length > 256)
94
+ refuse("registered_start_session_required");
95
+ return this.#prepare(context, input, input.workSessionId);
96
+ }
97
+ static #prepare(context, input, workSessionId) {
98
+ if (!PreparedRetainedSecondMemoryRepairs.isPrepared(input.registration) || !PreparedRetainedSecondMemoryBaseline.isPrepared(input.baseline))
99
+ refuse("retained_second_owners_required");
100
+ if (!(input.bundle instanceof RetainedExecutionBundle) || input.bundle.descriptor.descriptor.id !== "mb-init")
101
+ refuse("registered_memory_bundle_required");
102
+ if (context.db.isOwnedWriteTransaction?.())
103
+ refuse("preparation_outside_writer_required");
104
+ const registration = input.registration, baseline = input.baseline, bundle = input.bundle, owner = registration.receipt(), receipt = baseline.receipt();
105
+ registration.assertCustody(context);
106
+ if (workSessionId)
107
+ baseline.revalidate(context);
108
+ else
109
+ baseline.assertCustody(context);
110
+ if (receipt.registration_sha256 !== hybridDigest(owner) || owner.scope.bundle_sha256 !== bundle.sha256
111
+ || owner.scope.execution_sha256 !== bundle.execution.sha256)
112
+ refuse("foreign_registered_baseline");
113
+ const assigned = registration.batch().works.find(item => owner.work_ids[item.key] === receipt.work_id);
114
+ if (!assigned)
115
+ refuse("foreign_second_work");
116
+ const route = bundle.execution.work("review", assigned.payload.execution_override?.agent_profile_id), launch = launchRow(context, receipt.work_id, route);
117
+ let profile;
118
+ try {
119
+ profile = JSON.parse(launch.profile_json);
120
+ }
121
+ catch {
122
+ return refuse("invalid_launch_profile");
123
+ }
124
+ if (!profile || typeof profile !== "object" || Array.isArray(profile))
125
+ refuse("invalid_launch_profile");
126
+ const frozen = { coordinator: route.coordinator, worker: route.worker,
127
+ delegation: { mode: route.delegation.mode, max_parallel: route.delegation.mode === "external" ? route.delegation.max_parallel : null } };
128
+ if (hybridDigest({ coordinator: profile.coordinator, worker: profile.worker, delegation: profile.delegation }) !== hybridDigest(frozen)
129
+ || launch.profile_hash !== createHash("sha256").update(JSON.stringify(frozen)).digest("hex"))
130
+ refuse("launch_profile_not_frozen");
131
+ const run = context.db.get("SELECT run_root,run_dir FROM runs WHERE project_id=? AND id=?", [receipt.project_id, receipt.run_id]);
132
+ if (!run)
133
+ refuse("owned_run_required");
134
+ const runRoot = run.run_root ?? run.run_dir, relative = `works/${receipt.work_id}/writer-workspaces/generation-${receipt.generation}`;
135
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,255}$/.test(receipt.work_id))
136
+ refuse("unsafe_copy_scope");
137
+ const intent = { schema_id: "dd-flow/memory-repair-writer-copy-intent@1", project_id: receipt.project_id, run_id: receipt.run_id,
138
+ work_id: receipt.work_id, generation: receipt.generation, baseline_registration_sha256: hybridDigest(receipt),
139
+ protected_baseline_sha256: receipt.baseline_sha256, exported_baseline_sha256: receipt.exported_baseline_sha256,
140
+ run_root: runRoot, source_root: receipt.workspace_root, copy_relative: relative, input: baseline.copyInput() };
141
+ const ready = journal(context, receipt, "memory.repair_writer_copy_ready");
142
+ if (!ready || typeof ready.workspace_manifest_base64 !== "string")
143
+ refuse("ready_manifest_required");
144
+ const bytes = Buffer.from(ready.workspace_manifest_base64, "base64");
145
+ if (bytes.toString("base64") !== ready.workspace_manifest_base64)
146
+ refuse("changed_ready_manifest");
147
+ const workspace = PreparedMemoryRepairWorkspace.restore(bytes, ready.workspace_manifest_sha256, baseline.exportedBaseline());
148
+ const build = { ...intent, workspace_manifest_sha256: workspace.sha256, workspace_manifest_base64: bytes.toString("base64") };
149
+ if (hybridDigest(profile.memory_repair_copy) !== hybridDigest({ root: relative, copy_intent_sha256: hybridDigest(intent),
150
+ workspace_manifest_sha256: workspace.sha256, baseline_registration_sha256: hybridDigest(receipt) }))
151
+ refuse("launch_copy_changed");
152
+ const coordinator = PreparedDescriptorStageCoordinator.prepare(context, { projectRoot: bundle.execution.snapshot().scope.projectRoot, runId: receipt.run_id, stage: "review" });
153
+ coordinator.assertAllocatedCustody(context);
154
+ const session = readDescriptorCoordinatorSession(context, { scope: { projectId: receipt.project_id, runId: receipt.run_id, stage: "review" }, bundle, workId: owner.scope.parent_work_id }, refuse);
155
+ const prepared = new PreparedSecondMemoryRepairLaunch(token, baseline, launch, path.join(runRoot, relative), runRoot, workspace, intent, build, bundle, coordinator, session, registration);
156
+ if (workSessionId) {
157
+ prepared.#restoredStart = workSessionId;
158
+ prepared.#restoreStart(context, workSessionId);
159
+ }
160
+ else
161
+ prepared.revalidate(context);
162
+ return prepared;
163
+ }
164
+ root() { return this.#root; }
165
+ launch() { return structuredClone(this.#launch); }
166
+ input() {
167
+ const receipt = this.#baseline.receipt(), current = this.#baseline.currentView();
168
+ return { schema_id: current ? "dd-flow/memory-repair-work-input@2" : "dd-flow/memory-repair-work-input@1", work_id: receipt.work_id, generation: receipt.generation,
169
+ packet_sha256: receipt.packet_sha256, baseline_registration_sha256: hybridDigest(receipt),
170
+ protected_baseline_sha256: receipt.baseline_sha256, exported_baseline_sha256: receipt.exported_baseline_sha256,
171
+ copy_intent_sha256: hybridDigest(this.#intent), copy_build_sha256: hybridDigest(this.#build),
172
+ workspace_manifest_sha256: this.#workspace.sha256, workspace_root: this.#root,
173
+ launch_operation_id: this.#launch.operation_id, provider_session_id: this.#launch.provider_session_id,
174
+ ...(current ? { current_view_sha256: current.view_sha256, current_view_receipt_sha256: hybridDigest(current),
175
+ effective_read_inputs: memoryRepairCurrentReferences(this.#registration.batch().works.find(item => this.#registration.receipt().work_ids[item.key] === receipt.work_id).payload.memory_repair, this.#workspace) } : {}) };
176
+ }
177
+ /** Observe bounded private output against the actual packet/start manifest.
178
+ * This grants no shared publication, Work settlement or review acceptance. */
179
+ observeOutput(context, bytes) {
180
+ if (!this.#restoredStart)
181
+ refuse("restored_registered_start_required");
182
+ if (context.db.isOwnedWriteTransaction?.())
183
+ refuse("output_observation_outside_writer_required");
184
+ if (!(bytes instanceof Uint8Array) || !bytes.byteLength || bytes.byteLength > 1024 * 1024)
185
+ refuse("bounded_result_bytes_required");
186
+ this.assertCustody(context);
187
+ const receipt = this.#baseline.receipt(), registration = this.#registration.receipt();
188
+ const assigned = this.#registration.batch().works.find(item => registration.work_ids[item.key] === receipt.work_id);
189
+ if (!assigned || assigned.payload.memory_repair.packet_sha256 !== receipt.packet_sha256)
190
+ refuse("repair_packet_changed");
191
+ const retained = Buffer.from(bytes), packet = assigned.payload.memory_repair;
192
+ const result = validateMemoryRepairWorkResult(retained, this.#bundle.engine(), { workId: receipt.work_id, packet,
193
+ effectiveInputs: memoryRepairCurrentReferences(packet, this.#workspace) });
194
+ this.#workspace.verifyUnchangedInputs(this.#root);
195
+ const delta = this.#baseline.exportedBaseline().observeResult(this.#root, result);
196
+ this.#workspace.verifyUnchangedInputs(this.#root);
197
+ delta.revalidate();
198
+ this.assertCustody(context);
199
+ return { bytes: retained, result, delta, work_session_id: this.#restoredStart,
200
+ start_receipt_sha256: hybridDigest(this.#restoreStart(context, this.#restoredStart)) };
201
+ }
202
+ /** Reserve exact second-wave publication bytes before any shared effect.
203
+ * Initial publication requires the full explained first-wave baseline;
204
+ * settled peer views and partial publication recovery require other owners. */
205
+ reservePublication(context, bytes) {
206
+ const observed = this.observeOutput(context, bytes), receipt = this.#baseline.receipt();
207
+ if (observed.result.outcome !== "accepted")
208
+ refuse("accepted_private_output_required");
209
+ const before = this.#baseline.baseline(), exported = this.#baseline.exportedBaseline();
210
+ this.#workspace.verifySelectedSourceInputs(receipt.workspace_root, this.#baseline.copyInput(), before);
211
+ const transition = before.publicationTransition(exported, observed.delta);
212
+ const plan = { before_sha256: transition.state(0).sha256, before_base64: transition.state(0).bytes().toString("base64"),
213
+ after_sha256: transition.after().sha256, after_base64: transition.after().bytes().toString("base64"),
214
+ actions: transition.actions().map(action => ({ ...action, bytes_base64: action.after?.kind === "file" ? transition.bytes(action.path).toString("base64") : null })) };
215
+ RetainedMemoryRepairPublicationPlan.restore(Buffer.from(JSON.stringify(plan)), hybridDigest(plan));
216
+ const resultSha256 = createHash("sha256").update(observed.bytes).digest("hex");
217
+ const outputSha256 = hybridDigest({ input: this.input(), start_receipt_sha256: observed.start_receipt_sha256,
218
+ work_session_id: observed.work_session_id, result_sha256: resultSha256, delta_sha256: observed.delta.sha256, transition_sha256: transition.sha256 });
219
+ const value = { schema_id: "dd-flow/memory-repair-publication-intent@1", project_id: receipt.project_id, run_id: receipt.run_id,
220
+ work_id: receipt.work_id, generation: receipt.generation, work_session_id: observed.work_session_id,
221
+ source_root: receipt.workspace_root, private_root: this.#root, output_sha256: outputSha256,
222
+ start_receipt_sha256: observed.start_receipt_sha256, result_sha256: resultSha256, result_base64: observed.bytes.toString("base64"),
223
+ delta_sha256: observed.delta.sha256, transition_sha256: transition.sha256, ...plan };
224
+ if (Buffer.byteLength(JSON.stringify(value)) > 160 * 1024 * 1024)
225
+ refuse("unbounded_publication_intent");
226
+ observed.delta.revalidate();
227
+ this.assertCustody(context);
228
+ const expected = hybridDigest(value), eventType = "memory.repair_publication_intent";
229
+ const select = () => {
230
+ 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,'$.work_id')=? AND json_extract(payload_json,'$.generation')=? LIMIT 2", [receipt.project_id, eventType, receipt.run_id, receipt.work_id, receipt.generation]);
231
+ if (!rows.length)
232
+ return false;
233
+ try {
234
+ if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 160 * 1024 * 1024
235
+ || hybridDigest(JSON.parse(rows[0].payload_json)) !== expected)
236
+ refuse("publication_intent_changed");
237
+ }
238
+ catch (error) {
239
+ if (error instanceof AppError && error.code === "memory_second_repair_launch_invalid")
240
+ throw error;
241
+ refuse("invalid_publication_intent");
242
+ }
243
+ return true;
244
+ };
245
+ const result = context.db.writeTransaction(() => {
246
+ if (context.db.isOwnedWriteTransaction?.() !== true)
247
+ refuse("owned_publication_writer_required");
248
+ this.assertCustody(context);
249
+ const replayed = select();
250
+ if (!replayed)
251
+ appendAudit(context, { projectId: receipt.project_id, eventType, payload: value });
252
+ if (!select())
253
+ refuse("publication_intent_readback_failed");
254
+ return { work_id: receipt.work_id, work_session_id: observed.work_session_id, publication_intent_sha256: expected, status: "reserved", replayed };
255
+ });
256
+ try {
257
+ this.assertCustody(context);
258
+ observed.delta.revalidate();
259
+ this.#workspace.verifySelectedSourceInputs(receipt.workspace_root, this.#baseline.copyInput(), before);
260
+ }
261
+ catch (error) {
262
+ throw new AppError("memory_repair_publication_pending", "Second publication intent committed but physical custody changed before apply", 1, { effect: "committed", publication_intent_sha256: expected, cause: error instanceof Error ? error.message : String(error) });
263
+ }
264
+ return result;
265
+ }
266
+ /** Restore the independently reserved plan without adopting current shared
267
+ * or private bytes. Returned metadata does not authorize physical apply. */
268
+ restorePublication(context) {
269
+ if (!this.#restoredStart)
270
+ refuse("restored_registered_start_required");
271
+ if (context.db.isOwnedWriteTransaction?.())
272
+ refuse("publication_preparation_outside_writer_required");
273
+ const start = this.#restoreStart(context, this.#restoredStart);
274
+ const receipt = this.#baseline.receipt();
275
+ const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='memory.repair_publication_intent' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? AND json_extract(payload_json,'$.generation')=? LIMIT 2", [receipt.project_id, receipt.run_id, receipt.work_id, receipt.generation]);
276
+ if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 160 * 1024 * 1024)
277
+ refuse("registered_publication_intent_required");
278
+ try {
279
+ const value = JSON.parse(rows[0].payload_json);
280
+ const { current, plan, privatePlan } = retainedMemoryRepairPublicationPlans(value);
281
+ if (current || value.project_id !== receipt.project_id || value.run_id !== receipt.run_id || value.work_id !== receipt.work_id
282
+ || value.generation !== receipt.generation || value.work_session_id !== this.#restoredStart
283
+ || value.source_root !== receipt.workspace_root || value.private_root !== this.#root
284
+ || value.start_receipt_sha256 !== hybridDigest(start))
285
+ refuse("publication_scope_changed");
286
+ if (typeof value.result_base64 !== "string" || value.result_base64.length > Math.ceil(1024 * 1024 / 3) * 4)
287
+ refuse("bounded_result_bytes_required");
288
+ const bytes = Buffer.from(value.result_base64, "base64"), resultSha256 = createHash("sha256").update(bytes).digest("hex");
289
+ if (!bytes.length || bytes.length > 1024 * 1024 || bytes.toString("base64") !== value.result_base64 || resultSha256 !== value.result_sha256)
290
+ refuse("retained_result_changed");
291
+ const owner = this.#registration.receipt(), assigned = this.#registration.batch().works.find(item => owner.work_ids[item.key] === receipt.work_id);
292
+ if (!assigned || assigned.payload.memory_repair.packet_sha256 !== receipt.packet_sha256)
293
+ refuse("repair_packet_changed");
294
+ const packet = assigned.payload.memory_repair;
295
+ const result = validateMemoryRepairWorkResult(bytes, this.#bundle.engine(), { workId: receipt.work_id, packet,
296
+ effectiveInputs: memoryRepairCurrentReferences(packet, this.#workspace) });
297
+ if (result.outcome !== "accepted")
298
+ refuse("accepted_reserved_result_required");
299
+ const views = deriveRetainedMemoryRepairViews(this.#baseline.baseline(), this.#baseline.exportedBaseline(), plan, result, privatePlan);
300
+ const outputSha256 = hybridDigest({ input: this.input(), start_receipt_sha256: value.start_receipt_sha256,
301
+ work_session_id: this.#restoredStart, result_sha256: resultSha256, delta_sha256: views.deltaSha256, transition_sha256: views.transitionSha256 });
302
+ if (value.delta_sha256 !== views.deltaSha256 || value.transition_sha256 !== views.transitionSha256
303
+ || value.output_sha256 !== outputSha256)
304
+ refuse("reserved_output_identity_changed");
305
+ this.assertCustody(context);
306
+ const currentRow = context.db.get("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='memory.repair_publication_intent' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? AND json_extract(payload_json,'$.generation')=?", [receipt.project_id, receipt.run_id, receipt.work_id, receipt.generation]);
307
+ if (currentRow?.payload_json !== rows[0].payload_json)
308
+ refuse("registered_intent_changed");
309
+ return { receipt: structuredClone(value), sha256: hybridDigest(value), bytes: Buffer.from(bytes), result, plan, exportedAfter: views.exportedAfter };
310
+ }
311
+ catch (error) {
312
+ if (error instanceof AppError)
313
+ throw error;
314
+ return refuse("invalid_retained_publication");
315
+ }
316
+ }
317
+ /** Outside-writer restoration for the common sealed publication owner.
318
+ * Returned data alone is not publication authority. */
319
+ restorePublicationBinding(context) {
320
+ const publication = this.restorePublication(context);
321
+ const start = this.#restoreStart(context, this.#restoredStart);
322
+ this.assertPublicationCustody(context, publication.sha256);
323
+ return { publication, start, workSessionId: this.#restoredStart, privateRoot: this.#root, sourceRoot: this.#baseline.receipt().workspace_root };
324
+ }
325
+ /** SQL-only start and independent intent fence for an owning writer.
326
+ * The expected digest comes from an outside-writer restoration; this does
327
+ * not observe files, recreate receipts or grant application permission. */
328
+ assertPublicationCustody(context, expectedSha256) {
329
+ if (!this.#restoredStart)
330
+ refuse("restored_registered_start_required");
331
+ if (!/^[a-f0-9]{64}$/.test(expectedSha256))
332
+ refuse("publication_digest_required");
333
+ this.assertCustody(context);
334
+ const scope = this.#baseline.receipt();
335
+ const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='memory.repair_publication_intent' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? AND json_extract(payload_json,'$.generation')=? LIMIT 2", [scope.project_id, scope.run_id, scope.work_id, scope.generation]);
336
+ if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 160 * 1024 * 1024)
337
+ refuse("registered_publication_intent_required");
338
+ try {
339
+ const value = JSON.parse(rows[0].payload_json);
340
+ if (hybridDigest(value) !== expectedSha256)
341
+ refuse("registered_intent_changed");
342
+ }
343
+ catch (error) {
344
+ if (error instanceof AppError)
345
+ throw error;
346
+ refuse("invalid_retained_publication");
347
+ }
348
+ this.assertCustody(context);
349
+ }
350
+ /** Verify only an explained ordered prefix. A partial shared publication
351
+ * is never accepted as a replacement baseline or a completed Work. */
352
+ verifyPublicationPrefix(context) {
353
+ const publication = this.restorePublication(context), receipt = this.#baseline.receipt();
354
+ this.#workspace.verifyUnchangedInputs(this.#root);
355
+ publication.exportedAfter.revalidate(this.#root);
356
+ const view = publication.plan.classify(receipt.workspace_root);
357
+ this.#workspace.verifySelectedSourceInputs(receipt.workspace_root, this.#baseline.copyInput(), publication.plan.state(view.completed));
358
+ this.#workspace.verifyUnchangedInputs(this.#root);
359
+ publication.exportedAfter.revalidate(this.#root);
360
+ const current = publication.plan.classify(receipt.workspace_root);
361
+ if (current.completed !== view.completed || current.total !== view.total)
362
+ refuse("publication_cursor_changed");
363
+ this.assertPublicationCustody(context, publication.sha256);
364
+ return { publication_intent_sha256: publication.sha256, work_id: receipt.work_id,
365
+ work_session_id: this.#restoredStart, ...current };
366
+ }
367
+ /** Journal the actual next action before any staging/destination effect.
368
+ * Every already applied prefix action must have its own exact admission. */
369
+ reserveNextPublicationAction(context) {
370
+ const view = this.verifyPublicationPrefix(context), publication = this.restorePublication(context), scope = this.#baseline.receipt();
371
+ if (view.publication_intent_sha256 !== publication.sha256)
372
+ refuse("registered_intent_changed");
373
+ if (view.completed === view.total)
374
+ refuse("publication_has_no_pending_action");
375
+ const expected = (index) => publicationAction(scope, publication.plan, publication.sha256, this.#restoredStart, index);
376
+ const select = (index) => {
377
+ const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='memory.repair_publication_action_intent' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? AND json_extract(payload_json,'$.generation')=? AND json_extract(payload_json,'$.index')=? LIMIT 2", [scope.project_id, scope.run_id, scope.work_id, scope.generation, index]);
378
+ if (!rows.length)
379
+ return false;
380
+ try {
381
+ if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 16 * 1024 || hybridDigest(JSON.parse(rows[0].payload_json)) !== hybridDigest(expected(index)))
382
+ refuse("publication_action_intent_changed");
383
+ }
384
+ catch (error) {
385
+ if (error instanceof AppError)
386
+ throw error;
387
+ refuse("invalid_publication_action_intent");
388
+ }
389
+ return true;
390
+ };
391
+ const receipt = expected(view.completed);
392
+ const reservation = context.db.writeTransaction(() => {
393
+ if (context.db.isOwnedWriteTransaction?.() !== true)
394
+ refuse("owned_publication_writer_required");
395
+ this.assertCustody(context);
396
+ const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='memory.repair_publication_intent' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? AND json_extract(payload_json,'$.generation')=? LIMIT 2", [scope.project_id, scope.run_id, scope.work_id, scope.generation]);
397
+ if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 160 * 1024 * 1024 || hybridDigest(JSON.parse(rows[0].payload_json)) !== publication.sha256)
398
+ refuse("registered_intent_changed");
399
+ for (let index = 0; index < view.completed; index++)
400
+ if (!select(index))
401
+ refuse("unadmitted_publication_prefix");
402
+ const replayed = select(view.completed);
403
+ if (!replayed)
404
+ appendAudit(context, { projectId: scope.project_id, eventType: "memory.repair_publication_action_intent", payload: receipt });
405
+ if (!select(view.completed))
406
+ refuse("publication_action_readback_failed");
407
+ return { ...receipt, status: "reserved", replayed };
408
+ });
409
+ try {
410
+ const current = this.verifyPublicationPrefix(context);
411
+ if (current.completed !== view.completed || current.publication_intent_sha256 !== publication.sha256)
412
+ refuse("publication_cursor_changed");
413
+ }
414
+ catch (error) {
415
+ throw new AppError("memory_repair_publication_pending", "Second action admission committed; retain it for exact reconciliation", 1, { effect: "committed", publication_intent_sha256: publication.sha256, index: view.completed, cause: error instanceof Error ? error.message : String(error) });
416
+ }
417
+ return reservation;
418
+ }
419
+ /** Read-only admission restoration before staging/apply. A physical prefix
420
+ * alone never manufactures its missing independent action receipts. */
421
+ verifyPublicationActionPrefix(context) {
422
+ const view = this.verifyPublicationPrefix(context), publication = this.restorePublication(context), scope = this.#baseline.receipt();
423
+ if (publication.sha256 !== view.publication_intent_sha256)
424
+ refuse("registered_intent_changed");
425
+ const count = view.completed < view.total ? view.completed + 1 : view.completed;
426
+ let next = null;
427
+ for (let index = 0; index < count; index++) {
428
+ const expected = publicationAction(scope, publication.plan, publication.sha256, this.#restoredStart, index);
429
+ const rows = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='memory.repair_publication_action_intent' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? AND json_extract(payload_json,'$.generation')=? AND json_extract(payload_json,'$.index')=? LIMIT 2", [scope.project_id, scope.run_id, scope.work_id, scope.generation, index]);
430
+ if (rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 16 * 1024)
431
+ refuse("registered_publication_action_required");
432
+ try {
433
+ if (hybridDigest(JSON.parse(rows[0].payload_json)) !== hybridDigest(expected))
434
+ refuse("publication_action_intent_changed");
435
+ }
436
+ catch (error) {
437
+ if (error instanceof AppError)
438
+ throw error;
439
+ refuse("invalid_publication_action_intent");
440
+ }
441
+ if (index === view.completed)
442
+ next = expected;
443
+ }
444
+ const current = this.verifyPublicationPrefix(context);
445
+ if (current.completed !== view.completed || current.publication_intent_sha256 !== publication.sha256)
446
+ refuse("publication_cursor_changed");
447
+ return { ...current, next_action: next };
448
+ }
449
+ /** SQL-only claimant validation in the owning Work claim transaction.
450
+ * This does not create a WorkSession or authorize its start by itself. */
451
+ assertClaimIdentity(context, eventKey, identity) {
452
+ const receipt = this.#baseline.receipt();
453
+ if (this.#restoredStart)
454
+ refuse("restored_start_cannot_claim");
455
+ if (!context.db.ownedWriteTransactionToken?.() || context.db.isOwnedWriteTransaction?.() !== true)
456
+ refuse("owned_work_claim_writer_required");
457
+ this.assertCustody(context);
458
+ if (typeof eventKey !== "string" || !eventKey || eventKey.length > 1024)
459
+ refuse("registered_hook_key_required");
460
+ const actual = memoryRepairExecutionIdentity(context, { projectId: receipt.project_id, runId: receipt.run_id, workId: receipt.work_id, eventKey, root: this.#root }).identity;
461
+ const selected = { hookEventId: identity.hookEventId, providerSessionId: identity.providerSessionId,
462
+ sessionId: identity.sessionId, harness: identity.harness, cwd: identity.cwd };
463
+ if (!context.db.get("SELECT 1 FROM hook_events WHERE project_id=? AND event_key=? AND id=? AND event_name='PreToolUse' AND status IN ('observed','claimed')", [receipt.project_id, eventKey, actual.hookEventId]))
464
+ refuse("registered_start_hook_required");
465
+ if (hybridDigest({ hookEventId: actual.hookEventId, providerSessionId: actual.providerSessionId,
466
+ sessionId: actual.sessionId, harness: actual.harness, cwd: actual.cwd }) !== hybridDigest(selected))
467
+ refuse("claim_identity_changed");
468
+ const profile = JSON.parse(this.#launch.profile_json);
469
+ if (actual.providerSessionId !== this.#launch.provider_session_id || actual.harness !== runtimeHarness(profile.worker)
470
+ || actual.cwd !== this.#root)
471
+ refuse("repair_session_or_cwd_mismatch");
472
+ if (context.db.get("SELECT 1 FROM work_sessions ws JOIN works w ON w.work_id=ws.work_id JOIN sessions s ON s.project_id=w.project_id AND s.session_id=ws.session_id WHERE w.project_id=? AND w.run_id=? AND w.work_id<>? AND s.harness=? AND s.provider_session_id=? LIMIT 1", [receipt.project_id, receipt.run_id, receipt.work_id, actual.harness, actual.providerSessionId]))
473
+ refuse("repair_session_not_fresh");
474
+ this.assertCustody(context);
475
+ this.#claimToken = context.db.ownedWriteTransactionToken();
476
+ this.#claim = { eventKey, identity: selected };
477
+ }
478
+ assertCustody(context) {
479
+ if (this.#restoredStart)
480
+ this.#restoreStart(context, this.#restoredStart);
481
+ else
482
+ this.#assertCustody(context);
483
+ }
484
+ #assertCustody(context, startedWorkSessionId) {
485
+ this.#baseline.assertCustody(context);
486
+ this.#coordinator.assertAllocatedCustody(context);
487
+ const receipt = this.#baseline.receipt(), run = context.db.get("SELECT run_root,run_dir FROM runs WHERE project_id=? AND id=?", [receipt.project_id, receipt.run_id]);
488
+ const scope = this.#bundle.execution.snapshot().scope;
489
+ if (!run || (run.run_root ?? run.run_dir) !== this.#runRoot || scope.workspace !== receipt.workspace_root
490
+ || hybridDigest(launchRow(context, receipt.work_id, this.#bundle.execution.work("review", this.#registration.batch().works.find(item => this.#registration.receipt().work_ids[item.key] === receipt.work_id).payload.execution_override?.agent_profile_id), this.#launch.operation_id)) !== hybridDigest(this.#launch))
491
+ refuse("launch_custody_changed");
492
+ const root = this.#intent;
493
+ const parent = context.db.get("SELECT parent_work_id,status FROM works WHERE project_id=? AND run_id=? AND work_id=?", [receipt.project_id, receipt.run_id, receipt.work_id]);
494
+ if (!parent?.parent_work_id)
495
+ refuse("owned_target_required");
496
+ if (startedWorkSessionId) {
497
+ const sessions = context.db.all("SELECT id FROM work_sessions WHERE work_id=? LIMIT 2", [receipt.work_id]);
498
+ if (parent.status !== "running" || sessions.length !== 1 || sessions[0].id !== startedWorkSessionId)
499
+ refuse("registered_start_session_required");
500
+ }
501
+ else if (parent.status !== "created" || context.db.get("SELECT 1 FROM work_sessions WHERE work_id=? LIMIT 1", [receipt.work_id]))
502
+ refuse("unstarted_target_required");
503
+ if (readDescriptorCoordinatorSession(context, { scope: { projectId: receipt.project_id, runId: receipt.run_id, stage: "review" }, bundle: this.#bundle, workId: parent.parent_work_id }, refuse) !== this.#session
504
+ || hybridDigest(journal(context, receipt, "memory.repair_writer_copy_intent")) !== hybridDigest(root)
505
+ || hybridDigest(journal(context, receipt, "memory.repair_writer_copy_build")) !== hybridDigest(this.#build)
506
+ || hybridDigest(journal(context, receipt, "memory.repair_writer_copy_ready")) !== hybridDigest(this.#build))
507
+ refuse("copy_or_coordinator_custody_changed");
508
+ }
509
+ /** Called after generic WorkSession insertion in the very same claim writer.
510
+ * A rollback removes both Session binding and independent start receipt. */
511
+ recordStart(context, workSessionId) {
512
+ const claim = this.#claim;
513
+ if (this.#restoredStart)
514
+ refuse("restored_start_cannot_claim");
515
+ if (!claim || !this.#claimToken || context.db.isOwnedWriteTransaction?.() !== true
516
+ || context.db.ownedWriteTransactionToken?.() !== this.#claimToken)
517
+ refuse("same_work_claim_writer_required");
518
+ if (typeof workSessionId !== "string" || !workSessionId || workSessionId.length > 256)
519
+ refuse("registered_start_session_required");
520
+ const receipt = this.#baseline.receipt(), assigned = this.#registration.batch().works.find(item => this.#registration.receipt().work_ids[item.key] === receipt.work_id);
521
+ recordMemoryRepairNativeBinding(context, receipt.work_id, this.#bundle.execution.work("review", assigned.payload.execution_override?.agent_profile_id), this.#launch, workSessionId);
522
+ return this.#startValue(context, workSessionId, claim, true);
523
+ }
524
+ #restoreStart(context, workSessionId) {
525
+ const receipt = this.#baseline.receipt();
526
+ const row = context.db.get("SELECT hook_event_id FROM work_sessions WHERE id=? AND work_id=?", [workSessionId, receipt.work_id]);
527
+ if (!row || typeof row.hook_event_id !== "string")
528
+ refuse("registered_start_hook_required");
529
+ const actual = memoryRepairExecutionIdentity(context, { projectId: receipt.project_id, runId: receipt.run_id, workId: receipt.work_id, eventKey: row.hook_event_id, root: this.#root }).identity;
530
+ return this.#startValue(context, workSessionId, { eventKey: row.hook_event_id, identity: { hookEventId: actual.hookEventId,
531
+ providerSessionId: actual.providerSessionId, sessionId: actual.sessionId, harness: actual.harness, cwd: actual.cwd } }, false);
532
+ }
533
+ #startValue(context, workSessionId, claim, write) {
534
+ const receipt = this.#baseline.receipt();
535
+ if (claim.identity.providerSessionId !== this.#launch.provider_session_id
536
+ || claim.identity.harness !== runtimeHarness(JSON.parse(this.#launch.profile_json).worker)
537
+ || claim.identity.cwd !== this.#root)
538
+ refuse("repair_session_or_cwd_mismatch");
539
+ this.#assertCustody(context, workSessionId);
540
+ const rows = context.db.all("SELECT ws.session_id,ws.hook_event_id,ws.start_receipt_json FROM work_sessions ws JOIN works w ON w.work_id=ws.work_id JOIN runs r ON r.project_id=w.project_id AND r.id=w.run_id JOIN sessions s ON s.project_id=w.project_id AND s.session_id=ws.session_id WHERE w.project_id=? AND w.run_id=? AND w.work_id=? AND ws.id=? AND w.status='running' AND r.status='running' AND r.workspace_root=? AND ws.status='running' AND s.status='active' AND s.current_stage='work' AND s.worker_id=w.work_id AND s.run_id=w.run_id AND s.workspace_path=r.workspace_root AND s.project_root=r.project_root AND s.harness=? AND s.provider_session_id=? AND s.cwd=? LIMIT 2", [receipt.project_id, receipt.run_id, receipt.work_id, workSessionId, receipt.workspace_root, claim.identity.harness, this.#launch.provider_session_id, this.#root]);
541
+ if (rows.length !== 1 || rows[0].session_id !== claim.identity.sessionId || rows[0].hook_event_id !== claim.eventKey)
542
+ refuse("registered_start_session_required");
543
+ const actual = memoryRepairExecutionIdentity(context, { projectId: receipt.project_id, runId: receipt.run_id, workId: receipt.work_id, eventKey: claim.eventKey, root: this.#root }).identity;
544
+ if (hybridDigest({ hookEventId: actual.hookEventId, providerSessionId: actual.providerSessionId,
545
+ sessionId: actual.sessionId, harness: actual.harness, cwd: actual.cwd }) !== hybridDigest(claim.identity))
546
+ refuse("repair_claim_identity_changed");
547
+ if (!context.db.get("SELECT 1 FROM hook_events WHERE project_id=? AND event_key=? AND id=? AND event_name='PreToolUse' AND status='claimed' AND session_id=? AND COALESCE(provider_session_id,session_id)=? AND harness=?", [receipt.project_id, claim.eventKey, claim.identity.hookEventId, claim.identity.sessionId, this.#launch.provider_session_id, claim.identity.harness]))
548
+ refuse("registered_start_hook_required");
549
+ try {
550
+ const bytes = rows[0].start_receipt_json;
551
+ if (typeof bytes !== "string" || Buffer.byteLength(bytes) > 16 * 1024 * 1024)
552
+ refuse("bounded_start_receipt_required");
553
+ const start = JSON.parse(bytes), hash = (value) => createHash("sha256").update(value).digest("hex");
554
+ if (start.version !== 2 || start.generation !== receipt.generation || typeof start.context !== "string" || typeof start.prompt !== "string"
555
+ || start.context_sha256 !== hash(start.context) || start.prompt_sha256 !== hash(start.prompt)
556
+ || start.response?.work_id !== receipt.work_id || start.response?.work_session_id !== workSessionId
557
+ || start.response?.worker_prompt_markdown !== start.prompt || start.response_sha256 !== hash(JSON.stringify(start.response)))
558
+ refuse("repair_start_receipt_changed");
559
+ const input = JSON.parse(start.context);
560
+ if (input.work_id !== receipt.work_id || input.run_id !== receipt.run_id || input.workspace_root !== this.#root || input.read_only !== false
561
+ || input.result_schema !== "dd-flow/memory-repair-work-result@1" || hybridDigest(input.memory_repair_input) !== hybridDigest(this.input()))
562
+ refuse("repair_start_input_changed");
563
+ const value = { schema_id: "dd-flow/memory-repair-start@1", project_id: receipt.project_id, run_id: receipt.run_id,
564
+ work_id: receipt.work_id, generation: receipt.generation, work_session_id: workSessionId, session_id: claim.identity.sessionId,
565
+ hook_event_id: claim.eventKey, harness: claim.identity.harness, provider_session_id: this.#launch.provider_session_id,
566
+ context_sha256: start.context_sha256, prompt_sha256: start.prompt_sha256, input_sha256: hybridDigest(this.input()) };
567
+ const prior = context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type='memory.repair_work_start' AND json_extract(payload_json,'$.run_id')=? AND json_extract(payload_json,'$.work_id')=? AND json_extract(payload_json,'$.generation')=? LIMIT 2", [receipt.project_id, receipt.run_id, receipt.work_id, receipt.generation]);
568
+ if (prior.length > 1 || prior.length && (Buffer.byteLength(prior[0].payload_json) > 1024 * 1024 || hybridDigest(JSON.parse(prior[0].payload_json)) !== hybridDigest(value)))
569
+ refuse("repair_start_registration_changed");
570
+ if (!prior.length) {
571
+ if (!write)
572
+ refuse("registered_start_receipt_required");
573
+ appendAudit(context, { projectId: receipt.project_id, eventType: "memory.repair_work_start", payload: value });
574
+ }
575
+ if (hybridDigest(journal(context, receipt, "memory.repair_work_start")) !== hybridDigest(value))
576
+ refuse("repair_start_readback_failed");
577
+ this.#assertCustody(context, workSessionId);
578
+ return structuredClone(value);
579
+ }
580
+ catch (error) {
581
+ if (error instanceof AppError && error.code === "memory_second_repair_launch_invalid")
582
+ throw error;
583
+ return refuse("invalid_repair_start_receipt");
584
+ }
585
+ }
586
+ revalidate(context) {
587
+ if (this.#restoredStart) {
588
+ this.#baseline.revalidate(context);
589
+ this.assertCustody(context);
590
+ return;
591
+ }
592
+ this.assertCustody(context);
593
+ this.#baseline.revalidate(context);
594
+ this.#workspace.verify(this.#root);
595
+ const receipt = this.#baseline.receipt(), input = this.#baseline.copyInput();
596
+ this.#baseline.baseline().revalidate(receipt.workspace_root);
597
+ if (reviewSourceHash(receipt.workspace_root, input.explicit_inputs, input.base) !== input.source_sha256)
598
+ refuse("shared_source_changed_before_start");
599
+ this.assertCustody(context);
600
+ }
601
+ revalidateCommittedStart(context, workSessionId) {
602
+ const restoredStart = this.#restoredStart;
603
+ if (context.db.isOwnedWriteTransaction?.())
604
+ refuse("start_verification_outside_writer_required");
605
+ if (restoredStart && restoredStart !== workSessionId)
606
+ refuse("foreign_registered_start_session");
607
+ this.#restoreStart(context, workSessionId);
608
+ this.#baseline.revalidate(context);
609
+ if (restoredStart)
610
+ this.#workspace.verifyUnchangedInputs(this.#root);
611
+ else
612
+ this.#workspace.verify(this.#root);
613
+ }
614
+ }