@deksden-com/dd-flow-cli 0.9.0-beta.137 → 0.9.0-beta.139

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 (283) hide show
  1. package/CHANGELOG.md +19 -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/runtime-process.js +1 -1
  27. package/dist/cli/setup.js +149 -0
  28. package/dist/cli.js +56 -2
  29. package/dist/domain/stage-handler-identities.js +20 -0
  30. package/dist/harness-runtime/lib/daemon-operations.d.mts +1 -0
  31. package/dist/harness-runtime/lib/daemon-operations.mjs +4 -3
  32. package/dist/harness-runtime/lib/dd-agy-daemon.mjs +53 -22
  33. package/dist/harness-runtime/lib/dd-agy.mjs +16 -5
  34. package/dist/harness-runtime/lib/dd-codex-daemon.mjs +12 -6
  35. package/dist/harness-runtime/lib/dd-codex.mjs +24 -12
  36. package/dist/harness-runtime/lib/dd-droid-daemon.mjs +2 -1
  37. package/dist/harness-runtime/lib/dd-droid.mjs +32 -9
  38. package/dist/harness-runtime/lib/dd-grok-daemon.mjs +11 -3
  39. package/dist/harness-runtime/lib/dd-opencode-daemon.mjs +100 -15
  40. package/dist/harness-runtime/lib/dd-opencode.mjs +51 -20
  41. package/dist/harness-runtime/lib/dd-zcode-daemon.mjs +22 -10
  42. package/dist/harness-runtime/lib/dd-zcode.mjs +17 -4
  43. package/dist/harness-runtime/lib/delegation-instructions.d.mts +1 -0
  44. package/dist/harness-runtime/lib/delegation-instructions.mjs +16 -3
  45. package/dist/harness-runtime/lib/lease-renewal.mjs +2 -1
  46. package/dist/harness-runtime/lib/lifecycle-command.mjs +1 -0
  47. package/dist/harness-runtime/lib/managed-daemon.d.mts +1 -0
  48. package/dist/harness-runtime/lib/managed-daemon.mjs +50 -15
  49. package/dist/harness-runtime/lib/managed-flow-env.mjs +15 -0
  50. package/dist/harness-runtime/lib/native-children.d.mts +2 -0
  51. package/dist/harness-runtime/lib/native-children.mjs +20 -1
  52. package/dist/harness-runtime/lib/native-hook-command.mjs +1 -1
  53. package/dist/harness-runtime/lib/native-request-proof.d.mts +2 -0
  54. package/dist/harness-runtime/lib/native-request-proof.mjs +65 -0
  55. package/dist/harness-runtime/lib/opencode-plugin.mjs +32 -9
  56. package/dist/harness-runtime/lib/operation-progress.mjs +3 -1
  57. package/dist/harness-runtime/lib/provider-overload-policy.d.mts +13 -0
  58. package/dist/harness-runtime/lib/provider-overload-policy.mjs +138 -0
  59. package/dist/runtime/context.js +1 -1
  60. package/dist/schemas/discovery-result.schema.json +184 -0
  61. package/dist/schemas/execution-bundle.schema.json +205 -0
  62. package/dist/schemas/execution-settings.schema.json +162 -0
  63. package/dist/schemas/flow-descriptor.schema.json +54 -0
  64. package/dist/schemas/flow-run.schema.json +26 -0
  65. package/dist/schemas/harness-catalog.schema.json +80 -0
  66. package/dist/schemas/mb-init-compose-result.schema.json +457 -0
  67. package/dist/schemas/mb-init-discover-result.schema.json +500 -0
  68. package/dist/schemas/mb-init-finish-result.schema.json +422 -0
  69. package/dist/schemas/mb-init-preflight-result.schema.json +367 -0
  70. package/dist/schemas/mb-init-review-result.schema.json +396 -0
  71. package/dist/schemas/mb-init-verify-result.schema.json +391 -0
  72. package/dist/schemas/memory-bootstrap-receipt-v2.schema.json +67 -0
  73. package/dist/schemas/memory-bootstrap-receipt.schema.json +354 -0
  74. package/dist/schemas/memory-composition-work-result.schema.json +38 -0
  75. package/dist/schemas/memory-repair-work-result.schema.json +49 -0
  76. package/dist/schemas/memory-review-correction-plan.schema.json +30 -0
  77. package/dist/schemas/memory-review-work-result.schema.json +43 -0
  78. package/dist/schemas/model-catalog.schema.json +153 -0
  79. package/dist/schemas/profile-settings.schema.json +122 -0
  80. package/dist/schemas/release-manifest.schema.json +291 -0
  81. package/dist/schemas/run-engine-binding.schema.json +1 -1
  82. package/dist/services/check-runner-evidence.js +51 -4
  83. package/dist/services/cli-operation-classifier.js +8 -47
  84. package/dist/services/cli-upgrade-allowlist.js +27 -0
  85. package/dist/services/code-checks.js +387 -34
  86. package/dist/services/controller-child-capacity.js +13 -0
  87. package/dist/services/controller-child-observer.js +18 -2
  88. package/dist/services/controller-execution.js +88 -0
  89. package/dist/services/controller-fanout.js +71 -12
  90. package/dist/services/dashboard.js +8 -2
  91. package/dist/services/descriptor-coordinator-session.js +35 -0
  92. package/dist/services/descriptor-root-work.js +53 -0
  93. package/dist/services/descriptor-run-settlement.js +105 -0
  94. package/dist/services/descriptor-run-start.js +449 -0
  95. package/dist/services/descriptor-stage-coordinator.js +197 -0
  96. package/dist/services/descriptor-stage-settlement.js +222 -0
  97. package/dist/services/descriptor-stage-start.js +139 -0
  98. package/dist/services/discovery/agy-discovery.js +2 -0
  99. package/dist/services/discovery/codex-discovery.js +2 -0
  100. package/dist/services/discovery/droid-discovery.js +2 -0
  101. package/dist/services/discovery/grok-discovery.js +2 -0
  102. package/dist/services/discovery/opencode-discovery.js +2 -0
  103. package/dist/services/discovery/static.js +31 -0
  104. package/dist/services/discovery/zcode-discovery.js +2 -0
  105. package/dist/services/engines.js +39 -22
  106. package/dist/services/eval-snapshots.js +83 -25
  107. package/dist/services/execution-bundles.js +263 -0
  108. package/dist/services/execution-policy.js +20 -16
  109. package/dist/services/executor-onboarding.js +56 -0
  110. package/dist/services/external-work-launch.js +93 -15
  111. package/dist/services/flow-catalog.js +21 -0
  112. package/dist/services/flow-descriptors.js +82 -0
  113. package/dist/services/frozen-engine.js +250 -0
  114. package/dist/services/generic-execution.js +164 -0
  115. package/dist/services/harness-adapter.js +28 -2
  116. package/dist/services/harness-discovery.js +29 -0
  117. package/dist/services/harness-doctor.js +75 -0
  118. package/dist/services/harness-probe-effects.js +51 -0
  119. package/dist/services/harness-version-probe.js +100 -0
  120. package/dist/services/hooks.js +24 -6
  121. package/dist/services/hybrid-contracts.js +483 -0
  122. package/dist/services/ids.js +29 -1
  123. package/dist/services/installation-lock.js +95 -0
  124. package/dist/services/installed-flow-source.js +183 -0
  125. package/dist/services/lifecycle-invocations.js +257 -78
  126. package/dist/services/managed-processes.js +31 -10
  127. package/dist/services/memory-accepted-composition.js +77 -0
  128. package/dist/services/memory-baseline-preservation.js +65 -0
  129. package/dist/services/memory-completed-correction-wave.js +320 -0
  130. package/dist/services/memory-compose-handler.js +258 -0
  131. package/dist/services/memory-composition-acceptance.js +199 -0
  132. package/dist/services/memory-composition-graph.js +134 -0
  133. package/dist/services/memory-composition-registration.js +146 -0
  134. package/dist/services/memory-composition-work-custody.js +151 -0
  135. package/dist/services/memory-composition-work-results.js +96 -0
  136. package/dist/services/memory-corrected-review-acceptance.js +129 -0
  137. package/dist/services/memory-corrected-review-batch.js +30 -0
  138. package/dist/services/memory-corrected-review-findings.js +106 -0
  139. package/dist/services/memory-corrected-review-inputs.js +210 -0
  140. package/dist/services/memory-corrected-review-projection.js +78 -0
  141. package/dist/services/memory-corrected-review-receipt.js +170 -0
  142. package/dist/services/memory-corrected-review-registration.js +191 -0
  143. package/dist/services/memory-correction-check-receipt.js +219 -0
  144. package/dist/services/memory-correction-input-owner.js +6 -0
  145. package/dist/services/memory-discover-handler.js +104 -0
  146. package/dist/services/memory-finish-evidence.js +435 -0
  147. package/dist/services/memory-finish-handler.js +215 -0
  148. package/dist/services/memory-finish-integration.js +280 -0
  149. package/dist/services/memory-finish-readiness.js +122 -0
  150. package/dist/services/memory-git-integration.js +151 -0
  151. package/dist/services/memory-historical-corrected-review.js +206 -0
  152. package/dist/services/memory-historical-original-review.js +130 -0
  153. package/dist/services/memory-historical-repair-registration.js +99 -0
  154. package/dist/services/memory-historical-second-review.js +128 -0
  155. package/dist/services/memory-init-bootstrap-readiness.js +210 -0
  156. package/dist/services/memory-init-contexts.js +89 -0
  157. package/dist/services/memory-init-controller-start.js +84 -0
  158. package/dist/services/memory-init-engine-release.js +223 -0
  159. package/dist/services/memory-init-git-policy.js +136 -0
  160. package/dist/services/memory-init-lint-capability.js +185 -0
  161. package/dist/services/memory-init-native-capacity.js +296 -0
  162. package/dist/services/memory-init-preflight.js +330 -0
  163. package/dist/services/memory-init-project-bootstrap.js +599 -0
  164. package/dist/services/memory-init-request-candidate.js +157 -0
  165. package/dist/services/memory-init-request-input.js +66 -0
  166. package/dist/services/memory-init-resource-storage.js +125 -0
  167. package/dist/services/memory-init-run-allocation.js +81 -0
  168. package/dist/services/memory-init-run-bundle.js +98 -0
  169. package/dist/services/memory-init-start.js +181 -0
  170. package/dist/services/memory-init-startup-custody.js +80 -0
  171. package/dist/services/memory-init-startup-input.js +240 -0
  172. package/dist/services/memory-init-storage.js +81 -0
  173. package/dist/services/memory-init-stores.js +359 -0
  174. package/dist/services/memory-init-worktrunk-capability.js +178 -0
  175. package/dist/services/memory-layer-publication.js +611 -0
  176. package/dist/services/memory-layer.js +156 -0
  177. package/dist/services/memory-lint-config.js +85 -0
  178. package/dist/services/memory-native-review-copy.js +165 -0
  179. package/dist/services/memory-normal-flow-entry.js +75 -0
  180. package/dist/services/memory-ordinary-defaults.js +112 -0
  181. package/dist/services/memory-preflight-bootstrap-custody.js +115 -0
  182. package/dist/services/memory-preflight-handler.js +259 -0
  183. package/dist/services/memory-repair-current-view.js +299 -0
  184. package/dist/services/memory-repair-document-delta.js +451 -0
  185. package/dist/services/memory-repair-history.js +491 -0
  186. package/dist/services/memory-repair-physical-binding.js +312 -0
  187. package/dist/services/memory-repair-publication-files.js +152 -0
  188. package/dist/services/memory-repair-publication-intent.js +27 -0
  189. package/dist/services/memory-repair-publication-plan.js +160 -0
  190. package/dist/services/memory-repair-publication-source.js +225 -0
  191. package/dist/services/memory-repair-publication.js +794 -0
  192. package/dist/services/memory-repair-registration.js +239 -0
  193. package/dist/services/memory-repair-work-baseline.js +413 -0
  194. package/dist/services/memory-repair-work-completion.js +97 -0
  195. package/dist/services/memory-repair-work-custody.js +73 -0
  196. package/dist/services/memory-repair-work-results.js +89 -0
  197. package/dist/services/memory-repair-workspace.js +252 -0
  198. package/dist/services/memory-repair-writer-copy.js +735 -0
  199. package/dist/services/memory-result-binding.js +173 -0
  200. package/dist/services/memory-retained-review-authority.js +129 -0
  201. package/dist/services/memory-retained-review-results.js +70 -0
  202. package/dist/services/memory-retained-second-baseline.js +102 -0
  203. package/dist/services/memory-retained-second-repairs.js +156 -0
  204. package/dist/services/memory-review-acceptance.js +214 -0
  205. package/dist/services/memory-review-correction-plan.js +171 -0
  206. package/dist/services/memory-review-handler.js +585 -0
  207. package/dist/services/memory-review-inputs.js +200 -0
  208. package/dist/services/memory-review-receipt.js +180 -0
  209. package/dist/services/memory-review-registration.js +108 -0
  210. package/dist/services/memory-review-work-custody.js +124 -0
  211. package/dist/services/memory-review-work-input.js +72 -0
  212. package/dist/services/memory-review-work-results.js +73 -0
  213. package/dist/services/memory-second-corrected-review-inputs.js +165 -0
  214. package/dist/services/memory-second-correction-plan.js +76 -0
  215. package/dist/services/memory-second-repair-baseline.js +91 -0
  216. package/dist/services/memory-second-repair-launch.js +614 -0
  217. package/dist/services/memory-second-repair-registration.js +88 -0
  218. package/dist/services/memory-second-repair-source.js +187 -0
  219. package/dist/services/memory-second-review-acceptance.js +115 -0
  220. package/dist/services/memory-source-observations.js +117 -0
  221. package/dist/services/memory-stage-dispatch.js +28 -0
  222. package/dist/services/memory-stage-results.js +199 -0
  223. package/dist/services/memory-verification-acceptance.js +135 -0
  224. package/dist/services/memory-verification-checks.js +252 -0
  225. package/dist/services/memory-verification-receipt.js +159 -0
  226. package/dist/services/memory-verify-handler.js +135 -0
  227. package/dist/services/memory-work-result-preparation.js +63 -0
  228. package/dist/services/merge-server.js +2 -4
  229. package/dist/services/model-catalog.js +109 -0
  230. package/dist/services/model-discovery.js +46 -0
  231. package/dist/services/operator-profiles.js +236 -0
  232. package/dist/services/projects.js +17 -2
  233. package/dist/services/{codex-capacity.js → provider-capacity.js} +34 -16
  234. package/dist/services/recovery-snapshot-database.js +417 -2
  235. package/dist/services/release-archive-producer.js +52 -0
  236. package/dist/services/release-archive.js +143 -0
  237. package/dist/services/release-cache.js +142 -0
  238. package/dist/services/release-compatibility.js +18 -0
  239. package/dist/services/release-download.js +67 -0
  240. package/dist/services/release-envelope.js +104 -0
  241. package/dist/services/release-manifest.js +45 -0
  242. package/dist/services/release-payload.js +114 -0
  243. package/dist/services/release-producer.js +63 -0
  244. package/dist/services/release-provenance.js +160 -0
  245. package/dist/services/release-registry.js +66 -0
  246. package/dist/services/release-source.js +194 -0
  247. package/dist/services/repair-intents.js +1 -1
  248. package/dist/services/review-copy.js +10 -2
  249. package/dist/services/run-control.js +80 -9
  250. package/dist/services/run-controller-adapter.js +1 -1
  251. package/dist/services/run-controller-capture.js +10 -3
  252. package/dist/services/run-controller-process.js +21 -0
  253. package/dist/services/run-controller-recovery.js +5 -5
  254. package/dist/services/run-controller-state.js +1 -1
  255. package/dist/services/run-controller.js +158 -64
  256. package/dist/services/run-recovery-guard.js +5 -3
  257. package/dist/services/run-recovery-runtime.js +3 -3
  258. package/dist/services/run-recovery.js +145 -5
  259. package/dist/services/run-start-custody.js +177 -0
  260. package/dist/services/runs.js +413 -30
  261. package/dist/services/setup.js +377 -0
  262. package/dist/services/stage-context.js +9 -1
  263. package/dist/services/stage-handlers.js +74 -0
  264. package/dist/services/stage-report-renderer.js +63 -0
  265. package/dist/services/stage-work-graph.js +17 -0
  266. package/dist/services/vnext-code-review.js +1 -1
  267. package/dist/services/vnext-code.js +6 -6
  268. package/dist/services/vnext-execution-profile.js +2 -2
  269. package/dist/services/vnext-fanout.js +73 -11
  270. package/dist/services/vnext-merge.js +8 -3
  271. package/dist/services/vnext-plan-review.js +7 -5
  272. package/dist/services/vnext-plan.js +3 -3
  273. package/dist/services/vnext-protocolize.js +20 -6
  274. package/dist/services/vnext-workspace-policy.js +7 -2
  275. package/dist/services/work-registry.js +616 -71
  276. package/dist/services/workspace-files.js +1 -1
  277. package/dist/storage/database.js +84 -6
  278. package/dist/storage/writer-contract.js +11 -7
  279. package/package.json +13 -3
  280. package/tools/create-canon-release.mjs +33 -0
  281. package/tools/produce-hybrid-release.mjs +41 -0
  282. package/dist/harness-runtime/lib/codex-capacity-policy.d.mts +0 -10
  283. package/dist/harness-runtime/lib/codex-capacity-policy.mjs +0 -102
@@ -0,0 +1,159 @@
1
+ import { AppError } from "../shared/errors.js";
2
+ import { hybridDigest } from "./hybrid-contracts.js";
3
+ import { appendAudit } from "./audit.js";
4
+ import { PreparedCheckEvidence, PreparedRetainedCheckEvidence } from "./code-checks.js";
5
+ import { PreparedMemoryStageResult } from "./memory-stage-results.js";
6
+ import { PreparedMemoryVerificationAcceptance } from "./memory-verification-acceptance.js";
7
+ import { readAcceptedStageData } from "./stage-report-renderer.js";
8
+ const event = "memory.verification_acceptance";
9
+ function refuse(reason) {
10
+ throw new AppError("memory_verification_receipt_invalid", "Verification lacks committed historical check custody", 2, { reason, effect: "no_effect" });
11
+ }
12
+ function stage(context, scope) {
13
+ const run = context.db.get("SELECT index_json FROM runs WHERE project_id=? AND id=?", [scope.projectId, scope.runId]);
14
+ if (!run)
15
+ refuse("run_missing");
16
+ const index = JSON.parse(run.index_json), selected = index.stage_runs.find((value) => value.stage === "verify");
17
+ if (!selected || typeof selected.attempt !== "string" || !/^try-[0-9]{3,}$/.test(selected.attempt)
18
+ || typeof selected.input_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(selected.input_sha256))
19
+ refuse("verify_identity_missing");
20
+ return { current: index.current_stage, attempt: selected.attempt, input_sha256: selected.input_sha256, status: selected.status };
21
+ }
22
+ function receiptRows(context, scope) {
23
+ return context.db.all("SELECT payload_json FROM audit_events WHERE project_id=? AND event_type=? AND json_extract(payload_json,'$.run_id')=? LIMIT 2", [scope.projectId, event, scope.runId]);
24
+ }
25
+ /** Independent receipt written in the same transaction as fresh acceptance.
26
+ * A later replay does not re-evaluate today's policy or repaired workspace.
27
+ * It proves only that these exact bytes/checks were accepted historically.
28
+ */
29
+ export class PreparedMemoryVerificationReceipt {
30
+ #scope;
31
+ #receipt;
32
+ #checks;
33
+ #acceptance;
34
+ #result;
35
+ constructor(scope, receipt, checks, acceptance, result) {
36
+ this.#scope = { ...scope };
37
+ this.#receipt = structuredClone(receipt);
38
+ this.#checks = checks;
39
+ this.#acceptance = acceptance;
40
+ this.#result = result;
41
+ Object.freeze(this);
42
+ }
43
+ static prepare(context, input) {
44
+ if (!(input.result instanceof PreparedMemoryStageResult) || !(input.checks instanceof PreparedCheckEvidence)
45
+ || !(input.acceptance instanceof PreparedMemoryVerificationAcceptance))
46
+ refuse("prepared_acceptance_required");
47
+ input.checks.assertOwner({ ...input.scope, scope: "aggregate" });
48
+ input.acceptance.assertCustody(context, input.result, input.checks);
49
+ const selected = context.db.readSnapshot(() => stage(context, input.scope)), value = input.result.value();
50
+ if (selected.current !== "verify" || selected.status !== "running" || value.stage !== "verify" || (value.outcome !== "accepted" && value.outcome !== "needs_repair")
51
+ || value.run_id !== input.scope.runId || value.input_sha256 !== selected.input_sha256)
52
+ refuse("active_result_required");
53
+ if (receiptRows(context, input.scope).length)
54
+ refuse("fresh_receipt_already_exists");
55
+ const receipt = { schema_id: "dd-flow/memory-verification-acceptance@1", run_id: input.scope.runId, stage: "verify", project_id: input.scope.projectId,
56
+ attempt: selected.attempt, input_sha256: selected.input_sha256, result_sha256: input.result.sha256, checks: input.checks.retainedReferences(context) };
57
+ if (!receipt.checks.length || receipt.checks.length > 4096 || Buffer.byteLength(JSON.stringify(receipt)) > 2 * 1024 * 1024)
58
+ refuse("receipt_exceeds_bound");
59
+ return new PreparedMemoryVerificationReceipt(input.scope, receipt, input.checks, input.acceptance, input.result);
60
+ }
61
+ record(context) {
62
+ this.#checks.assertCustody(context);
63
+ this.#acceptance.assertCustody(context, this.#result);
64
+ const selected = stage(context, this.#scope);
65
+ if (selected.current !== "verify" || selected.status !== "running" || selected.attempt !== this.#receipt.attempt
66
+ || selected.input_sha256 !== this.#receipt.input_sha256 || receiptRows(context, this.#scope).length)
67
+ refuse("fresh_custody_changed");
68
+ if (hybridDigest(this.#checks.retainedReferences(context)) !== hybridDigest(this.#receipt.checks))
69
+ refuse("selected_checks_changed");
70
+ appendAudit(context, { projectId: this.#scope.projectId, eventType: event, payload: this.#receipt });
71
+ }
72
+ }
73
+ export class PreparedAcceptedMemoryVerification {
74
+ #scope;
75
+ #authority;
76
+ #receiptSha256;
77
+ #checks;
78
+ constructor(scope, authority, receiptSha256, checks) {
79
+ this.#scope = { ...scope };
80
+ this.#authority = authority;
81
+ this.#receiptSha256 = receiptSha256;
82
+ this.#checks = checks;
83
+ Object.freeze(this);
84
+ }
85
+ static prepare(context, input) {
86
+ if (!(input.bytes instanceof Uint8Array) || !input.bytes.length || input.bytes.length > 1024 * 1024)
87
+ refuse("invalid_replay_bytes");
88
+ const selected = context.db.readSnapshot(() => accepted(context, input));
89
+ if (!selected.data.bytes.equals(input.bytes))
90
+ refuse("accepted_result_changed");
91
+ const checks = PreparedRetainedCheckEvidence.prepare(context, { projectId: input.projectId, runId: input.runId, references: selected.receipt.checks,
92
+ ...(JSON.parse(selected.data.bytes.toString("utf8")).outcome === "needs_repair" ? { mode: "diagnostic" } : {}) });
93
+ const prepared = new PreparedAcceptedMemoryVerification(input, selected.authority, selected.sha256, checks);
94
+ prepared.assertCustody(context);
95
+ return prepared;
96
+ }
97
+ assertCustody(context) {
98
+ const selected = context.db.readSnapshot(() => accepted(context, this.#scope));
99
+ if (selected.sha256 !== this.#receiptSha256 || selected.authority !== this.#authority)
100
+ refuse("accepted_custody_changed");
101
+ this.#checks.assertCustody(context);
102
+ }
103
+ revalidate(context) { this.#checks.revalidate(context); this.assertCustody(context); }
104
+ qualityPassed(context) { this.assertCustody(context); return JSON.parse(accepted(context, this.#scope).data.bytes.toString("utf8")).outcome === "accepted"; }
105
+ diagnosticFindings(context) {
106
+ this.assertCustody(context);
107
+ const findings = JSON.parse(accepted(context, this.#scope).data.bytes.toString("utf8")).findings;
108
+ return findings.map(finding => ({ ...structuredClone(finding), evidence: finding.evidence.map(source => ({ ...source, root: source.root === "run" ? "run" : "workspace", path: source.root === "canon" ? `.memory-bank/${source.path}` : source.path })) }));
109
+ }
110
+ finalChecks(context) { if (!this.qualityPassed(context))
111
+ refuse("verification_quality_not_passed"); return structuredClone(accepted(context, this.#scope).receipt.checks); }
112
+ /** SQL-only policy identity from the independently accepted check rows,
113
+ * not today's mutable project profile or coordinator result JSON. */
114
+ profileHash(context) {
115
+ this.assertCustody(context);
116
+ const selected = accepted(context, this.#scope);
117
+ const hashes = selected.receipt.checks.map(reference => {
118
+ const row = context.db.get("SELECT profile_hash FROM check_receipts WHERE project_id=? AND run_id=? AND id=?", [this.#scope.projectId, this.#scope.runId, reference.id]);
119
+ if (!row || (row.profile_hash !== null && (typeof row.profile_hash !== "string" || !/^[a-f0-9]{64}$/.test(row.profile_hash))))
120
+ refuse("accepted_policy_hash_missing");
121
+ return row.profile_hash;
122
+ });
123
+ if (!hashes.length || hashes.some(value => value !== hashes[0]))
124
+ refuse("accepted_policy_hash_inconsistent");
125
+ return hashes[0];
126
+ }
127
+ }
128
+ function accepted(context, scope) {
129
+ const selected = stage(context, scope), data = readAcceptedStageData(context, { ...scope, stage: "verify" }), rows = receiptRows(context, scope);
130
+ if (selected.status !== "done" || rows.length !== 1 || Buffer.byteLength(rows[0].payload_json) > 2 * 1024 * 1024)
131
+ refuse("accepted_receipt_missing");
132
+ let receipt;
133
+ try {
134
+ receipt = JSON.parse(rows[0].payload_json);
135
+ }
136
+ catch {
137
+ return refuse("invalid_receipt_json");
138
+ }
139
+ if (!receipt || typeof receipt !== "object" || Array.isArray(receipt) || hybridDigest({ ...receipt, checks: undefined }) !== hybridDigest({ schema_id: "dd-flow/memory-verification-acceptance@1", run_id: scope.runId, stage: "verify", project_id: scope.projectId,
140
+ attempt: selected.attempt, input_sha256: selected.input_sha256, result_sha256: data.result.sha256 }) || data.data_schema_id !== "dd-flow/mb-init-verify-result@1"
141
+ || data.input_sha256 !== selected.input_sha256)
142
+ refuse("accepted_receipt_changed");
143
+ const value = JSON.parse(data.bytes.toString("utf8"));
144
+ const compose = readAcceptedStageData(context, { ...scope, stage: "compose" }), composed = JSON.parse(compose.bytes.toString("utf8"));
145
+ if (compose.data_schema_id !== "dd-flow/mb-init-compose-result@1"
146
+ || composed.schema_id !== compose.data_schema_id || composed.stage !== "compose" || composed.run_id !== scope.runId
147
+ || composed.outcome !== "accepted" || composed.input_sha256 !== compose.input_sha256
148
+ || hybridDigest(value.predecessors) !== hybridDigest([{ stage: "compose", result: compose.result, input_sha256: compose.input_sha256 }]))
149
+ refuse("accepted_predecessor_changed");
150
+ if (value.stage !== "verify" || value.run_id !== scope.runId || !["accepted", "needs_repair"].includes(value.outcome) || value.input_sha256 !== selected.input_sha256
151
+ || !Array.isArray(receipt.checks) || receipt.checks.some(reference => !reference || typeof reference !== "object" || Array.isArray(reference)
152
+ || Object.keys(reference).some(key => !["id", "path", "sha256", "sql_sha256"].includes(key))
153
+ || typeof reference.id !== "string" || !reference.id || typeof reference.path !== "string"
154
+ || typeof reference.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(reference.sha256)
155
+ || typeof reference.sql_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(reference.sql_sha256)) || !Array.isArray(value.details?.checks)
156
+ || receipt.checks.some(({ path, sha256 }) => !value.details.checks.some((reference) => reference.path === path && reference.sha256 === sha256)))
157
+ refuse("accepted_check_references_changed");
158
+ return { data, receipt, sha256: hybridDigest(receipt), authority: hybridDigest({ verify: data.authority_sha256, compose: compose.authority_sha256 }) };
159
+ }
@@ -0,0 +1,135 @@
1
+ import { createHash } from "node:crypto";
2
+ import { hybridDigest } from "./hybrid-contracts.js";
3
+ import { AppError } from "../shared/errors.js";
4
+ import { readFlowRunExecutionBundle, assertPreparedRunBundleSqlAuthority } from "./runs.js";
5
+ import { requireProjectByRoot } from "./projects.js";
6
+ import { resolveProjectRoot } from "../storage/paths.js";
7
+ import { PreparedMemoryBaselinePreservation } from "./memory-baseline-preservation.js";
8
+ import { readAcceptedStageData } from "./stage-report-renderer.js";
9
+ import { PreparedDescriptorStageStart } from "./descriptor-stage-start.js";
10
+ import { PreparedDescriptorStageCoordinator } from "./descriptor-stage-coordinator.js";
11
+ import { PreparedMemoryVerificationChecks } from "./memory-verification-checks.js";
12
+ import { PreparedMemoryResultBinding } from "./memory-result-binding.js";
13
+ import { PreparedMemoryStageResult } from "./memory-stage-results.js";
14
+ import { PreparedAcceptedMemoryComposition } from "./memory-accepted-composition.js";
15
+ import { PreparedMemoryVerificationAcceptance, projectMemoryVerificationChecks, projectMemoryVerificationArtifacts } from "./memory-verification-acceptance.js";
16
+ import { PreparedDescriptorStageSettlement } from "./descriptor-stage-settlement.js";
17
+ import { PreparedMemoryVerificationReceipt, PreparedAcceptedMemoryVerification } from "./memory-verification-receipt.js";
18
+ function refuse(reason) {
19
+ throw new AppError("memory_verify_handler_invalid", "Memory verification lacks registered scope or accepted composition", 2, { reason, effect: "no_effect" });
20
+ }
21
+ /** CLI-owned finite checks for the active verifier. Callers supply no command,
22
+ * profile hash, receipt path or alternate executable. Public routing remains
23
+ * pending complete memory handler registration.
24
+ */
25
+ export function runMemoryVerificationChecks(context, input) {
26
+ const bundle = readFlowRunExecutionBundle(context, input);
27
+ if (!bundle || bundle.descriptor.descriptor.id !== "mb-init" || bundle.descriptor.successor("compose", "accepted") !== "verify"
28
+ || bundle.descriptor.successor("verify", "accepted") !== "review")
29
+ refuse("registered_memory_bundle_required");
30
+ const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot)), scope = { projectId: project.id, runId: input.runId };
31
+ const run = assertPreparedRunBundleSqlAuthority(context, scope, bundle), index = JSON.parse(run.index_json);
32
+ if (index.current_stage !== "verify" || index.stage_runs.find((stage) => stage.stage === "verify")?.status !== "running")
33
+ refuse("active_verification_required");
34
+ return PreparedMemoryVerificationChecks.prepare(context, { ...scope, execution: bundle.execution, flow: bundle.descriptor }).execute(context);
35
+ }
36
+ /** Productive entry implementation, not registered until the complete six-stage
37
+ * memory group is ready. Checks and semantic assessment acceptance belong to
38
+ * its finish path, not this allocation of a verifier coordinator.
39
+ */
40
+ export function startMemoryVerification(context, input) {
41
+ if (!(input.bytes instanceof Uint8Array) || input.bytes.length > 1024 * 1024)
42
+ refuse("invalid_input_bytes");
43
+ const bundle = readFlowRunExecutionBundle(context, input);
44
+ if (!bundle || bundle.descriptor.descriptor.id !== "mb-init" || bundle.descriptor.successor("compose", "accepted") !== "verify"
45
+ || bundle.descriptor.successor("verify", "accepted") !== "review")
46
+ refuse("registered_memory_bundle_required");
47
+ const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot)), scope = { projectId: project.id, runId: input.runId, stage: "verify" };
48
+ const run = assertPreparedRunBundleSqlAuthority(context, scope, bundle), index = JSON.parse(run.index_json);
49
+ if (index.stage_runs.find((stage) => stage.stage === "verify")?.status === "done")
50
+ refuse("use_accepted_finish_replay");
51
+ const preservation = PreparedMemoryBaselinePreservation.prepare(context, bundle.execution);
52
+ const inherited = context.db.readSnapshot(() => ({ compose: readAcceptedStageData(context, { ...scope, stage: "compose" }), preflight: readAcceptedStageData(context, { ...scope, stage: "preflight" }) }));
53
+ const compose = JSON.parse(inherited.compose.bytes.toString("utf8")), preflight = JSON.parse(inherited.preflight.bytes.toString("utf8"));
54
+ if (compose.schema_id !== "dd-flow/mb-init-compose-result@1" || compose.run_id !== input.runId || compose.outcome !== "accepted"
55
+ || preflight.schema_id !== "dd-flow/mb-init-preflight-result@1" || preflight.run_id !== input.runId || preflight.outcome !== "accepted")
56
+ refuse("accepted_composition_and_baseline_required");
57
+ const fence = (writer) => {
58
+ preservation.assertCustody(writer);
59
+ if (readAcceptedStageData(writer, { ...scope, stage: "compose" }).authority_sha256 !== inherited.compose.authority_sha256
60
+ || readAcceptedStageData(writer, { ...scope, stage: "preflight" }).authority_sha256 !== inherited.preflight.authority_sha256)
61
+ refuse("inherited_custody_changed");
62
+ };
63
+ const start = PreparedDescriptorStageStart.prepare(context, { ...input, stage: "verify" });
64
+ preservation.revalidate(context);
65
+ const stage = start.apply(context, fence), coordinator = PreparedDescriptorStageCoordinator.prepare(context, { ...input, stage: "verify" });
66
+ preservation.revalidate(context);
67
+ return { ...stage, ...coordinator.apply(context, fence) };
68
+ }
69
+ /** Verification settlement. Restore composition from committed SQL and
70
+ * its original registration, never from verifier-proposed predecessor bytes.
71
+ * Replay consumes independent historical acceptance/check custody instead of
72
+ * current workspace policy. No partial registry is enabled by this path.
73
+ */
74
+ export function finishMemoryVerification(context, input) {
75
+ if (!(input.bytes instanceof Uint8Array) || !input.bytes.length || input.bytes.length > 1024 * 1024)
76
+ refuse("invalid_result_bytes");
77
+ const bundle = readFlowRunExecutionBundle(context, input);
78
+ if (!bundle || bundle.descriptor.descriptor.id !== "mb-init" || bundle.descriptor.successor("compose", "accepted") !== "verify"
79
+ || bundle.descriptor.successor("verify", "accepted") !== "review")
80
+ refuse("registered_memory_bundle_required");
81
+ const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot)), scope = { projectId: project.id, runId: input.runId };
82
+ const run = assertPreparedRunBundleSqlAuthority(context, scope, bundle), index = JSON.parse(run.index_json);
83
+ if (index.stage_runs.find((stage) => stage.stage === "verify")?.status === "done") {
84
+ const data = readAcceptedStageData(context, { ...scope, stage: "verify" });
85
+ const accepted = PreparedAcceptedMemoryVerification.prepare(context, { ...scope, bytes: data.bytes });
86
+ const value = JSON.parse(data.bytes.toString("utf8"));
87
+ let proposal;
88
+ try {
89
+ proposal = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.bytes));
90
+ }
91
+ catch {
92
+ refuse("invalid_result_bytes");
93
+ }
94
+ if (hybridDigest(projectMemoryVerificationChecks(projectMemoryVerificationArtifacts(proposal, value.units, value.units.flatMap((unit) => unit.artifacts)), value.details.checks, bundle.descriptor.stage("verify").directory, createHash("sha256").update(input.bytes).digest("hex"))) !== hybridDigest(value))
95
+ refuse("accepted_result_changed");
96
+ const settlement = PreparedDescriptorStageSettlement.prepare(context, { ...input, stage: "verify", bytes: data.bytes });
97
+ accepted.revalidate(context);
98
+ return settlement.apply(context, writer => accepted.assertCustody(writer));
99
+ }
100
+ const composition = PreparedAcceptedMemoryComposition.prepare(context, { scope, bundle }), compose = composition.result();
101
+ const composedUnits = compose.value().units;
102
+ const preservation = PreparedMemoryBaselinePreservation.prepare(context, bundle.execution);
103
+ const policy = PreparedMemoryVerificationChecks.prepare(context, { ...scope, execution: bundle.execution, flow: bundle.descriptor });
104
+ const checks = policy.evidence(context, "diagnostic");
105
+ const binding = PreparedMemoryResultBinding.prepare(context, { ...scope, stage: "verify", flow: bundle.descriptor, execution: bundle.execution, bundle,
106
+ sources: composition.captureSources(context, input.bytes, checks), checks });
107
+ let proposal;
108
+ try {
109
+ proposal = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.bytes));
110
+ }
111
+ catch {
112
+ PreparedMemoryStageResult.prepare(input.bytes, bundle.descriptor, binding.binding());
113
+ refuse("invalid_result_bytes");
114
+ }
115
+ const references = checks.sourceReferences().map(({ path, sha256 }) => ({ path, sha256 }));
116
+ const result = PreparedMemoryStageResult.prepare(Buffer.from(JSON.stringify(projectMemoryVerificationChecks(projectMemoryVerificationArtifacts(proposal, composedUnits, composition.outputReferences(), createHash("sha256").update(input.bytes).digest("hex")), references, bundle.descriptor.stage("verify").directory, createHash("sha256").update(input.bytes).digest("hex")))), bundle.descriptor, binding.binding());
117
+ const acceptance = PreparedMemoryVerificationAcceptance.prepare({ scope, result, compose, checks, composition, roots: bundle.execution.snapshot().scope });
118
+ const receipt = PreparedMemoryVerificationReceipt.prepare(context, { scope, result, checks, acceptance });
119
+ const settlement = PreparedDescriptorStageSettlement.prepare(context, { ...input, stage: "verify", bytes: result.bytes() });
120
+ composition.revalidate(context);
121
+ binding.revalidate(context);
122
+ // Binding owns the check artifact references and has just revalidated the
123
+ // same checks; acceptance adds SQL custody, not another physical pass.
124
+ preservation.revalidate(context);
125
+ policy.revalidate(context);
126
+ acceptance.assertCustody(context, result);
127
+ return settlement.apply(context, writer => {
128
+ composition.assertCustody(writer);
129
+ binding.assertCustody(writer);
130
+ preservation.assertCustody(writer);
131
+ policy.assertCustody(writer);
132
+ acceptance.assertCustody(writer, result);
133
+ receipt.record(writer);
134
+ });
135
+ }
@@ -0,0 +1,63 @@
1
+ import { AppError } from "../shared/errors.js";
2
+ import { readFlowRunExecutionBundle } from "./runs.js";
3
+ import { readWorkBatchCustody } from "./work-registry.js";
4
+ import { selectMemoryCompositionRegistration } from "./memory-composition-registration.js";
5
+ import { PreparedRegisteredMemoryCompositionWorkResult } from "./memory-composition-work-custody.js";
6
+ import { PreparedRegisteredMemoryReviewWorkResult } from "./memory-review-work-custody.js";
7
+ import { PreparedPublishedMemoryRepairWorkResult } from "./memory-repair-work-custody.js";
8
+ import { restoreMemoryRepairPublicationFromStage, prepareMemorySecondRepairPublicationFromStage } from "./memory-review-handler.js";
9
+ /** Public Work finish selects authority from SQL and the retained bundle, not
10
+ * from worker JSON, mutable schemas, caller profile or a supplied request key.
11
+ * Preparation is read-only and occurs before the settlement writer.
12
+ */
13
+ export function prepareRegisteredMemoryWorkResult(context, work, result) {
14
+ if (!["dd-flow/memory-composition-work-result@1", "dd-flow/memory-review-work-result@1", "dd-flow/memory-repair-work-result@1"].includes(work.result_schema ?? ""))
15
+ return null;
16
+ const bytes = Buffer.from(result, "utf8");
17
+ if (work.result_schema === "dd-flow/memory-repair-work-result@1") {
18
+ if (!bytes.length || bytes.length > 1024 * 1024)
19
+ throw new AppError("memory_work_result_preparation_required", "Repair finish requires bounded published result bytes", 1, { effect: "no_effect" });
20
+ const run = context.db.get("SELECT project_root FROM runs WHERE project_id=? AND id=?", [work.project_id, work.run_id]);
21
+ const link = context.db.get("SELECT id,start_receipt_json FROM work_sessions WHERE work_id=? AND status='running'", [work.work_id]);
22
+ let wave, workspaceRoot;
23
+ try {
24
+ const payload = JSON.parse(work.payload_json ?? "null");
25
+ if (payload?.kind !== "memory-repair" || payload.read_only !== false)
26
+ throw new Error("Invalid repair payload");
27
+ wave = payload.memory_repair?.wave;
28
+ if (!link?.start_receipt_json || Buffer.byteLength(link.start_receipt_json) > 32 * 1024 * 1024)
29
+ throw new Error("Missing bounded start receipt");
30
+ const receipt = JSON.parse(link.start_receipt_json);
31
+ if (typeof receipt.context !== "string" || Buffer.byteLength(receipt.context) > 16 * 1024 * 1024)
32
+ throw new Error("Missing bounded context");
33
+ workspaceRoot = JSON.parse(receipt.context).workspace_root;
34
+ }
35
+ catch {
36
+ throw new AppError("memory_work_result_preparation_required", "Repair finish requires retained physical start scope", 1, { effect: "no_effect" });
37
+ }
38
+ if (!run || !link || wave !== 1 && wave !== 2 || typeof workspaceRoot !== "string" || !workspaceRoot)
39
+ throw new AppError("memory_work_result_preparation_required", "Repair finish requires its exact wave and WorkSession", 1, { effect: "no_effect" });
40
+ const input = { projectRoot: run.project_root, runId: work.run_id, workId: work.work_id, workSessionId: link.id, workspaceRoot };
41
+ const publication = wave === 1 ? restoreMemoryRepairPublicationFromStage(context, input) : prepareMemorySecondRepairPublicationFromStage(context, input);
42
+ if (!publication.bytes().equals(bytes))
43
+ throw new AppError("memory_work_result_preparation_required", "Submitted repair bytes differ from the retained publication", 1, { effect: "no_effect" });
44
+ return PreparedPublishedMemoryRepairWorkResult.prepare(context, publication);
45
+ }
46
+ if (work.result_schema === "dd-flow/memory-review-work-result@1") {
47
+ return PreparedRegisteredMemoryReviewWorkResult.prepare(context, { workId: work.work_id, bytes });
48
+ }
49
+ const run = context.db.get("SELECT project_root FROM runs WHERE project_id=? AND id=?", [work.project_id, work.run_id]);
50
+ const bundle = run && readFlowRunExecutionBundle(context, { projectRoot: run.project_root, runId: work.run_id });
51
+ if (!bundle || bundle.descriptor.descriptor.id !== "mb-init")
52
+ throw new AppError("memory_work_result_preparation_required", "Composition finish requires its retained memory flow", 1, { effect: "no_effect" });
53
+ const custody = readWorkBatchCustody(context, work.work_id);
54
+ const registration = selectMemoryCompositionRegistration(context, {
55
+ project_id: work.project_id, run_id: work.run_id, stage: "compose", attempt: custody.scope.attempt,
56
+ cycle: custody.scope.cycle, parent_work_id: custody.scope.parent_work_id
57
+ });
58
+ return PreparedRegisteredMemoryCompositionWorkResult.prepareFromStageCustody(context, bytes, bundle.engine(), {
59
+ projectId: work.project_id, runId: work.run_id, workId: work.work_id,
60
+ coordinatorWorkId: registration.scope.parent_work_id, requestKey: registration.scope.request_key,
61
+ flow: bundle.descriptor, execution: bundle.execution, bundle
62
+ });
63
+ }
@@ -1,6 +1,6 @@
1
1
  import { managedInvocationContext, managedLifecycleCommand } from "./lifecycle-invocations.js";
2
2
  import { stageLifecycleInstruction } from "../harness-runtime/lib/delegation-instructions.mjs";
3
- import { continueCodexCapacity } from "./codex-capacity.js";
3
+ import { continueProviderCapacity } from "./provider-capacity.js";
4
4
  import { errorRecord } from "./run-controller-state.js";
5
5
  import crypto from "node:crypto";
6
6
  import fs from "node:fs";
@@ -164,11 +164,9 @@ async function dispatch(context, input) {
164
164
  prompted = await adapterCall(["session", "prompt", "--session-id", sessionId, "--prompt-file", promptFile, ...common], null, `MERGE ${input.request.merge_request_id} Session ${sessionId} is still running`);
165
165
  }
166
166
  catch (error) {
167
- if (!["codex-desktop", "opencode-server"].includes(runtimeHarness(input.profile)))
168
- throw error;
169
167
  const capacityIntents = (retained.capacity_intents ?? []);
170
168
  const capacityFailures = (retained.capacity_failures ?? []);
171
- const resumed = await continueCodexCapacity({ error, sessionId, operationId: String(retained.prompt_operation_id), allowSettledDescendants: true,
169
+ const resumed = await continueProviderCapacity({ error, sessionId, operationId: String(retained.prompt_operation_id), allowSettledDescendants: true,
172
170
  signal: ownership.signal,
173
171
  continuationText: `Continue only MERGE ${input.request.merge_request_id}, Work ${input.request.executor_work_id}, RUN ${input.request.run_id} in this same Session and target ${input.request.target_workspace}. First inspect current durable Stage/Work/MERGE receipts. Never repeat Stage start, accepted answer, completed apply or checks. Follow only the current exact issued successor and stop at the original boundary.`,
174
172
  retainedIntents: () => capacityIntents,
@@ -0,0 +1,109 @@
1
+ import { AppError } from "../shared/errors.js";
2
+ import { validateHybridContract } from "./hybrid-contracts.js";
3
+ import { mergeModelListing } from "./model-discovery.js";
4
+ import { readBoundedReleaseFile } from "./release-source.js";
5
+ // Reviewed adapter fallbacks and retained eval configurations, not vendor
6
+ // availability promises or recommended defaults. Missing facts stay unknown;
7
+ // historical eval qualification/capacity claims are deliberately not imported.
8
+ const seed = {
9
+ schema_id: "dd-flow/model-catalog@1", version: "1.1.0",
10
+ entries: [{ harness: "agy", provider: "google", model: "gemini-3.1-pro-high",
11
+ settings: { reasoning: ["unknown"], mode: ["unknown"] },
12
+ provenance: { kind: "reviewed-seed", source: "https://github.com/deksden-com/dd-flow-cli/blob/2ba2ce2364705cf59fdc48bdd5dc5b37fb7cd784/src/harness-runtime/lib/dd-agy.mjs#L7",
13
+ reviewed_at: "2026-10-07T00:00:00Z" }, availability: "not-observed", qualification: { scope: "seed-only", evidence: null } },
14
+ { harness: "grok", provider: "xai", model: "grok-4.6",
15
+ settings: { reasoning: ["high"], mode: ["bypassPermissions"] },
16
+ provenance: { kind: "reviewed-seed", source: "https://github.com/deksden-com/dd-flow-cli/blob/2ba2ce2364705cf59fdc48bdd5dc5b37fb7cd784/src/harness-runtime/lib/dd-grok.mjs#L48",
17
+ reviewed_at: "2026-10-07T00:00:00Z" }, availability: "not-observed", qualification: { scope: "seed-only", evidence: null } },
18
+ { harness: "codex", provider: "unknown", model: "gpt-6.1-sol",
19
+ settings: { reasoning: ["high"], mode: ["unknown"] },
20
+ provenance: { kind: "reviewed-seed", source: "https://github.com/deksden-com/dd-eval/blob/9715970544096fc0288ac8e18f95d4e35997be54/profiles/codex-desktop-gpt-6-1-sol-high.json",
21
+ reviewed_at: "2026-10-07T00:00:00Z" }, availability: "not-observed", qualification: { scope: "seed-only", evidence: null } },
22
+ { harness: "zcode", provider: "builtin:zai-coding-plan", model: "GLM-5.3",
23
+ settings: { reasoning: ["high"], mode: ["yolo"] },
24
+ provenance: { kind: "reviewed-seed", source: "https://github.com/deksden-com/dd-eval/blob/9715970544096fc0288ac8e18f95d4e35997be54/profiles/zcode-acp-zai-glm-5-3-high.json",
25
+ reviewed_at: "2026-10-07T00:00:00Z" }, availability: "not-observed", qualification: { scope: "seed-only", evidence: null } },
26
+ { harness: "opencode", provider: "opencode", model: "big-pickle",
27
+ settings: { reasoning: ["default"], mode: ["build"] },
28
+ provenance: { kind: "reviewed-seed", source: "https://github.com/deksden-com/dd-eval/blob/9715970544096fc0288ac8e18f95d4e35997be54/profiles/opencode-server-opencode-big-pickle-default.json",
29
+ reviewed_at: "2026-10-07T00:00:00Z" }, availability: "not-observed", qualification: { scope: "seed-only", evidence: null } },
30
+ { harness: "droid", provider: "openai", model: "gpt-5.6-sol",
31
+ settings: { reasoning: ["high"], mode: ["auto-high"] },
32
+ provenance: { kind: "reviewed-seed", source: "https://github.com/deksden-com/dd-eval/blob/9715970544096fc0288ac8e18f95d4e35997be54/profiles/droid-cli-openai-gpt-5-6-sol-high.json",
33
+ reviewed_at: "2026-10-07T00:00:00Z" }, availability: "not-observed", qualification: { scope: "seed-only", evidence: null } }]
34
+ };
35
+ const families = ["codex", "zcode", "agy", "opencode", "grok", "droid"];
36
+ const identifier = (value, max = 128) => typeof value === "string" && value.length <= max && /^[A-Za-z0-9][A-Za-z0-9._:/+-]*$/.test(value);
37
+ const providerId = (value) => typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(value);
38
+ function invalid() {
39
+ throw new AppError("model_catalog_input", "Model catalog input requires reviewed seed data and an explicit observation scope; see dd-flow models --help", 2, { effect: "no_effect", help: "models" });
40
+ }
41
+ function readJson(file) {
42
+ let bytes;
43
+ try {
44
+ bytes = readBoundedReleaseFile(file, 4 * 1024 * 1024);
45
+ }
46
+ catch {
47
+ throw new AppError("model_input_unavailable", "Model input must be a bounded readable regular file; see dd-flow models --help", 1, { effect: "no_effect", help: "models" });
48
+ }
49
+ try {
50
+ return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
51
+ }
52
+ catch {
53
+ return invalid();
54
+ }
55
+ }
56
+ export function readModelSeed(file) {
57
+ const input = file === undefined ? structuredClone(seed) : readJson(file);
58
+ if (validateHybridContract("model-catalog", input).length)
59
+ invalid();
60
+ const catalog = input;
61
+ if (catalog.entries.length > 10_000 || catalog.entries.some(entry => {
62
+ let source;
63
+ try {
64
+ source = new URL(entry.provenance.source);
65
+ }
66
+ catch {
67
+ return true;
68
+ }
69
+ const reviewed = entry.provenance.reviewed_at;
70
+ return entry.qualification.scope !== "seed-only" || !providerId(entry.provider) || !identifier(entry.model, 256)
71
+ || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(reviewed) || !Number.isFinite(Date.parse(reviewed))
72
+ || new Date(reviewed).toISOString() !== reviewed.replace(/Z$/, reviewed.includes(".") ? "Z" : ".000Z")
73
+ || source.protocol !== "https:" || source.username !== "" || source.password !== "" || source.search !== ""
74
+ || entry.provenance.source.length > 2048
75
+ || [...entry.settings.reasoning, ...entry.settings.mode].some(value => !identifier(value, 64));
76
+ }))
77
+ invalid();
78
+ return structuredClone(catalog);
79
+ }
80
+ /** Offline reader only. A supplied snapshot is an observation, not account authorization. */
81
+ export function listOperatorModels(input) {
82
+ if (input.harness !== undefined && !families.includes(input.harness) || input.provider !== undefined && !providerId(input.provider)
83
+ || input.model !== undefined && !identifier(input.model, 256)
84
+ || input.accountSha256 !== undefined && !/^[a-f0-9]{64}$/.test(input.accountSha256)
85
+ || (input.accountSha256 !== undefined || input.observationFile !== undefined) && (!input.harness || !input.provider || !input.accountSha256))
86
+ invalid();
87
+ // Validate all selectors before opening operator files.
88
+ const catalog = readModelSeed(input.seedFile);
89
+ let entries, reason, observedAt = null;
90
+ if (input.accountSha256) {
91
+ const scope = { harness: input.harness, provider: input.provider, account_sha256: input.accountSha256 };
92
+ const observation = input.observationFile === undefined ? undefined : readJson(input.observationFile);
93
+ const merged = mergeModelListing(catalog, scope, observation);
94
+ entries = merged.entries.map(entry => ({ harness: scope.harness, provider: scope.provider, ...entry }));
95
+ reason = merged.reason;
96
+ observedAt = merged.observed_at;
97
+ }
98
+ else {
99
+ entries = catalog.entries.filter(entry => (!input.harness || entry.harness === input.harness) && (!input.provider || entry.provider === input.provider))
100
+ .map(entry => ({ harness: entry.harness, provider: entry.provider, model: entry.model, seed: structuredClone(entry),
101
+ availability: "unknown", account_access: "unknown", qualified: "unknown" }));
102
+ reason = "account_scope_not_selected";
103
+ }
104
+ if (input.model)
105
+ entries = entries.filter(entry => entry.model === input.model);
106
+ return { catalog_version: catalog.version, observed_at: observedAt, reason, entries,
107
+ families: families.map(harness => ({ harness, seed_status: catalog.entries.some(entry => entry.harness === harness) ? "reviewed_entries" : "not_curated",
108
+ account_access: "unknown", qualified: "unknown" })) };
109
+ }
@@ -0,0 +1,46 @@
1
+ import { AppError } from "../shared/errors.js";
2
+ import { validateHybridContract } from "./hybrid-contracts.js";
3
+ const invalid = () => new AppError("model_discovery_input", "Model discovery input does not match its scoped contract", 2, { effect: "no_effect" });
4
+ export function validateModelScope(scope) {
5
+ if (!scope || typeof scope !== "object" || Array.isArray(scope)
6
+ || ["harness", "provider", "account_sha256"].some(key => !Object.hasOwn(scope, key))
7
+ || Object.keys(scope).some(key => !["harness", "provider", "account_sha256"].includes(key))
8
+ || !["codex", "zcode", "agy", "opencode", "grok", "droid"].includes(scope.harness)
9
+ || typeof scope.provider !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(scope.provider)
10
+ || typeof scope.account_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(scope.account_sha256))
11
+ throw invalid();
12
+ }
13
+ /** Merge only a matching account/provider snapshot. Listed is not authorized or qualified. */
14
+ export function mergeModelListing(seed, scope, observation) {
15
+ validateModelScope(scope);
16
+ if (validateHybridContract("model-catalog", seed).length)
17
+ throw invalid();
18
+ if (observation !== undefined) {
19
+ if (!observation || typeof observation !== "object" || Array.isArray(observation)
20
+ || ["scope", "observed_at", "status", "complete", "models"].some(key => !Object.hasOwn(observation, key)))
21
+ throw invalid();
22
+ validateModelScope(observation.scope);
23
+ if (Object.keys(observation).some(key => !["scope", "observed_at", "status", "complete", "models"].includes(key))
24
+ || typeof observation.observed_at !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(observation.observed_at)
25
+ || !Number.isFinite(Date.parse(observation.observed_at))
26
+ || new Date(observation.observed_at).toISOString() !== observation.observed_at.replace(/Z$/, observation.observed_at.includes(".") ? "Z" : ".000Z")
27
+ || !["listed", "unavailable"].includes(observation.status)
28
+ || typeof observation.complete !== "boolean" || !Array.isArray(observation.models)
29
+ || observation.models.length > 10_000 || observation.models.some(model => typeof model !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:/+-]{0,255}$/.test(model))
30
+ || new Set(observation.models).size !== observation.models.length
31
+ || observation.status === "unavailable" && (observation.models.length > 0 || observation.complete))
32
+ throw invalid();
33
+ }
34
+ const matches = observation && observation.scope.harness === scope.harness && observation.scope.provider === scope.provider
35
+ && observation.scope.account_sha256 === scope.account_sha256;
36
+ const listing = matches && observation.status === "listed" ? observation : undefined;
37
+ const entries = seed.entries.filter(entry => entry.harness === scope.harness && entry.provider === scope.provider);
38
+ const seeded = new Map(entries.map(entry => [entry.model, entry]));
39
+ const names = new Set([...seeded.keys(), ...(listing?.models ?? [])]);
40
+ return { scope: { ...scope }, observed_at: matches ? observation.observed_at : null,
41
+ reason: !observation ? "not_observed" : !matches ? "foreign_scope_ignored" : !listing ? "listing_unavailable" : "scoped_listing",
42
+ entries: [...names].sort().map(model => ({ model,
43
+ seed: seeded.has(model) ? structuredClone(seeded.get(model)) : null,
44
+ availability: listing?.models.includes(model) ? "listed" : listing?.complete ? "not_listed" : "unknown",
45
+ account_access: "unknown", qualified: "unknown" })) };
46
+ }