@cassiomc1/forgeloop 1.13.0 → 2.0.0-rc.2

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 (395) hide show
  1. package/AGENT_COMPATIBILITY.md +8 -0
  2. package/CONTRIBUTING.md +11 -7
  3. package/DOCS_INDEX.md +37 -4
  4. package/ENG/c-development-eng.md +2 -2
  5. package/ENG/cpp-development-eng.md +2 -2
  6. package/ENG/dotnet-aspnetcore-development-eng.md +7 -1
  7. package/ENG/flutter-development-eng.md +4 -2
  8. package/ENG/go-development-eng.md +2 -2
  9. package/ENG/java-development-eng.md +3 -3
  10. package/ENG/nodejs-backend-development-eng.md +10 -6
  11. package/ENG/php-development-eng.md +3 -3
  12. package/ENG/python-development-eng.md +205 -0
  13. package/ENG/rust-development-eng.md +42 -1
  14. package/ENG/sec-code-eng.md +7 -7
  15. package/ENG/sql-development-eng.md +4 -4
  16. package/ENG/swift-development-eng.md +5 -4
  17. package/ENG/test-code-eng.md +1 -1
  18. package/ENG/typescript-development-eng.md +4 -4
  19. package/EXECUTION_STATE.md +12 -0
  20. package/GUIDE_ROUTER.md +6 -0
  21. package/LICENSE +133 -21
  22. package/LOOP_ENGINEERING.md +28 -2
  23. package/LOOP_SYSTEM_DESIGN.md +10 -10
  24. package/ORCHESTRATOR_INTEGRATION.md +9 -5
  25. package/PROTOCOL_INTEGRATION.md +55 -2
  26. package/README.md +63 -59
  27. package/TERMINOLOGY.md +2 -0
  28. package/THIRD_PARTY_NOTICES.md +3 -4
  29. package/THREAT_MODEL.md +140 -1
  30. package/completions/_forgeloop +29 -3
  31. package/completions/forgeloop.bash +55 -3
  32. package/completions/forgeloop.fish +183 -1
  33. package/docs/ADVISORY_CONTEXT.md +25 -0
  34. package/docs/AGENT_BROWSER_ADAPTER.md +81 -0
  35. package/docs/AGENT_BROWSER_VERIFICATION.md +6 -0
  36. package/docs/AGENT_PROTOCOL_SUMMARY.md +74 -39
  37. package/docs/AGENT_SKILL.md +66 -0
  38. package/docs/ARTIFACT_REFERENCE.md +171 -35
  39. package/docs/AUDIT_UX.md +46 -0
  40. package/docs/BROWSER_VERIFICATION.md +136 -0
  41. package/docs/CLI_REFERENCE.md +512 -8
  42. package/docs/CODE_ATTESTATION.md +2 -2
  43. package/docs/DOCUMENTATION_GUIDE.md +34 -11
  44. package/docs/GETTING_STARTED.md +3 -3
  45. package/docs/JEV_BENCHMARKS.md +31 -0
  46. package/docs/MODEL_ROUTING.md +37 -0
  47. package/docs/OPENSRC_ADAPTER.md +241 -0
  48. package/docs/PACKAGE_CONTENTS.md +46 -18
  49. package/docs/PROVIDERS.md +126 -0
  50. package/docs/PROVIDER_ARCHITECTURE.md +199 -0
  51. package/docs/RECIPES.md +9 -0
  52. package/docs/RELEASE_CHECKLIST.md +116 -12
  53. package/docs/SECURITY_REVIEW.md +71 -0
  54. package/docs/SELF_HOSTED_TESTS.md +121 -0
  55. package/docs/SEMANTIC_DECISION_PLANE.md +71 -0
  56. package/docs/TEST_INTELLIGENCE.md +29 -0
  57. package/docs/TEST_PRUNING.md +14 -0
  58. package/docs/TROUBLESHOOTING.md +198 -1
  59. package/docs/UNIVERSAL_INTEGRATION.md +31 -0
  60. package/docs/assets/diagrams/forgeloop-code-attestation-flow.html +2 -2
  61. package/docs/assets/diagrams/forgeloop-code-attestation-flow.receipt.json +5 -5
  62. package/docs/assets/diagrams/forgeloop-code-attestation-flow.svg +1 -1
  63. package/docs/assets/diagrams/forgeloop-engineering-flow.html +39 -26
  64. package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
  65. package/docs/assets/diagrams/forgeloop-engineering-flow.svg +26 -26
  66. package/docs/assets/diagrams/forgeloop-verification-trust-flow.html +2 -1
  67. package/docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json +5 -5
  68. package/docs/assets/diagrams/forgeloop-verification-trust-flow.svg +1 -1
  69. package/docs/diagrams/README.md +13 -9
  70. package/docs/diagrams/forgeloop-code-attestation-flow.workflow.json +1 -1
  71. package/docs/diagrams/forgeloop-engineering-flow.workflow.json +24 -19
  72. package/docs/diagrams/forgeloop-verification-trust-flow.workflow.json +1 -0
  73. package/docs/diagrams/reviews/forgeloop-code-attestation-flow.review.json +4 -4
  74. package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +4 -4
  75. package/docs/diagrams/reviews/forgeloop-verification-trust-flow.review.json +4 -4
  76. package/docs/documentation-manifest.json +774 -16
  77. package/docs/protocol-requirements.json +24 -0
  78. package/package.json +43 -11
  79. package/schemas/config.schema.json +14 -0
  80. package/schemas/context-plan.schema.json +18 -0
  81. package/schemas/semantic-decision.schema.json +46 -0
  82. package/schemas/task-bundle.schema.json +7 -2
  83. package/schemas/test-utility.schema.json +44 -0
  84. package/scripts/CI_VALIDATORS.md +6 -6
  85. package/scripts/benchmark-jev.mjs +5 -0
  86. package/scripts/benchmark-test-intelligence.mjs +4 -0
  87. package/scripts/generate-agent-protocol-summary.mjs +6 -3
  88. package/scripts/generate-forgeloop-skill.mjs +133 -0
  89. package/scripts/jev-smoke.mjs +19 -0
  90. package/scripts/run-mcp-clean-tests.mjs +30 -0
  91. package/skills/forgeloop/README.md +9 -0
  92. package/skills/forgeloop/SKILL.md +77 -0
  93. package/skills/forgeloop/references/lifecycle.md +9 -0
  94. package/skills/forgeloop/references/recovery.md +7 -0
  95. package/skills/forgeloop/references/verification.md +7 -0
  96. package/src/adapters/agent-browser/assertions.js +47 -0
  97. package/src/adapters/agent-browser/commands.js +54 -0
  98. package/src/adapters/agent-browser/index.js +3 -0
  99. package/src/adapters/agent-browser/locator.js +40 -0
  100. package/src/adapters/agent-browser/process.js +215 -0
  101. package/src/adapters/agent-browser/provider.js +316 -0
  102. package/src/adapters/emulated-services/constants.js +24 -0
  103. package/src/adapters/emulated-services/index.js +7 -0
  104. package/src/adapters/emulated-services/process.js +162 -0
  105. package/src/adapters/emulated-services/provider.js +289 -0
  106. package/src/adapters/opensrc/normalize.js +90 -0
  107. package/src/adapters/opensrc/process.js +248 -0
  108. package/src/adapters/opensrc/provider.js +338 -0
  109. package/src/adapters/opensrc/search.js +264 -0
  110. package/src/adapters/temporary-paths.js +18 -0
  111. package/src/adapters/typesafe/client.js +28 -0
  112. package/src/adapters/typesafe/engine.js +63 -0
  113. package/src/adapters/typesafe/normalize.js +41 -0
  114. package/src/cli.js +218 -174
  115. package/src/commands/action-show.js +5 -2
  116. package/src/commands/advance.js +5 -1
  117. package/src/commands/baseline.js +13 -11
  118. package/src/commands/checkpoint-revalidate.js +192 -0
  119. package/src/commands/complete.js +19 -3
  120. package/src/commands/context-plan.js +38 -0
  121. package/src/commands/contract-create.js +266 -0
  122. package/src/commands/contract-revise.js +236 -0
  123. package/src/commands/decision-show.js +14 -0
  124. package/src/commands/decision-status.js +22 -0
  125. package/src/commands/discover.js +41 -0
  126. package/src/commands/doctor.js +51 -13
  127. package/src/commands/gate-record.js +194 -0
  128. package/src/commands/gate-revalidate.js +135 -0
  129. package/src/commands/handoff-list.js +6 -1
  130. package/src/commands/handoff-show.js +6 -1
  131. package/src/commands/migrate-protocol.js +2 -2
  132. package/src/commands/model-route.js +40 -0
  133. package/src/commands/next.js +7 -1
  134. package/src/commands/policy-diff.js +25 -4
  135. package/src/commands/progress.js +17 -11
  136. package/src/commands/record-diagnosis.js +3 -0
  137. package/src/commands/route.js +146 -18
  138. package/src/commands/semantic-plan.js +41 -0
  139. package/src/commands/status.js +6 -1
  140. package/src/commands/storage-backup.js +15 -0
  141. package/src/commands/storage-migrate.js +5 -0
  142. package/src/commands/storage-migration-resume.js +5 -0
  143. package/src/commands/storage-migration-status.js +44 -0
  144. package/src/commands/storage-restore-resume.js +7 -0
  145. package/src/commands/storage-restore.js +8 -0
  146. package/src/commands/task-abandon.js +224 -0
  147. package/src/commands/task-list.js +8 -43
  148. package/src/commands/task-migrate-contract-bootstrap-repair.js +288 -0
  149. package/src/commands/task-migrate.js +3 -3
  150. package/src/commands/task-recover.js +10 -2
  151. package/src/commands/task-repair-contract-bootstrap.js +263 -0
  152. package/src/commands/task-scope.js +18 -2
  153. package/src/commands/task-show.js +16 -2
  154. package/src/commands/test-inventory.js +5 -0
  155. package/src/commands/test-prune-plan.js +5 -0
  156. package/src/commands/test-prune-probe.js +5 -0
  157. package/src/commands/test-utility.js +5 -0
  158. package/src/commands/validate-protocol.js +39 -15
  159. package/src/commands/validate-receipt.js +8 -1
  160. package/src/config/guides.json +4 -0
  161. package/src/core/action-ledger-projection.js +10 -3
  162. package/src/core/actions.js +38 -75
  163. package/src/core/activation.js +11 -3
  164. package/src/core/approvals.js +17 -51
  165. package/src/core/artifact-registry.js +83 -2
  166. package/src/core/artifacts.js +81 -27
  167. package/src/core/attachment-paths.js +45 -0
  168. package/src/core/attestation-coverage.js +6 -1
  169. package/src/core/attestation-verifier.js +21 -2
  170. package/src/core/attestation.js +17 -6
  171. package/src/core/audit-ux.js +514 -0
  172. package/src/core/audit.js +6 -1
  173. package/src/core/browser-verification/constants.js +149 -0
  174. package/src/core/browser-verification/normalize.js +254 -0
  175. package/src/core/browser-verification/provider.js +519 -0
  176. package/src/core/browser-verification/service.js +115 -0
  177. package/src/core/bundles.js +160 -66
  178. package/src/core/checkpoint-revalidation.js +321 -0
  179. package/src/core/cli-command-definitions.js +350 -7
  180. package/src/core/code-manifest.js +9 -3
  181. package/src/core/command-executors.js +274 -108
  182. package/src/core/command-input.js +136 -44
  183. package/src/core/completion-artifacts.js +85 -34
  184. package/src/core/completion-ownership.js +19 -8
  185. package/src/core/completion-recovery-rebind.js +11 -1
  186. package/src/core/completion.js +64 -15
  187. package/src/core/config.js +3 -0
  188. package/src/core/conformance.js +2 -1
  189. package/src/core/context-compiler/budget.js +9 -0
  190. package/src/core/context-compiler/candidates.js +39 -0
  191. package/src/core/context-compiler/compiler.js +63 -0
  192. package/src/core/context-compiler/fingerprint.js +11 -0
  193. package/src/core/context-compiler/policy.js +13 -0
  194. package/src/core/context-compiler/result.js +23 -0
  195. package/src/core/continuity-lint.js +13 -2
  196. package/src/core/continuity-reconciliation.js +16 -3
  197. package/src/core/continuity.js +16 -8
  198. package/src/core/contract-bootstrap-recovery.js +656 -0
  199. package/src/core/contract-revision.js +209 -0
  200. package/src/core/decision/artifact.js +70 -0
  201. package/src/core/decision/benchmarks.js +103 -0
  202. package/src/core/decision/cache.js +27 -0
  203. package/src/core/decision/constants.js +58 -0
  204. package/src/core/decision/cutover.js +34 -0
  205. package/src/core/decision/engine.js +22 -0
  206. package/src/core/decision/errors.js +68 -0
  207. package/src/core/decision/events.js +104 -0
  208. package/src/core/decision/freshness.js +19 -0
  209. package/src/core/decision/normalizers/index.js +115 -0
  210. package/src/core/decision/policy.js +18 -0
  211. package/src/core/decision/projection.js +16 -0
  212. package/src/core/decision/question-registry.js +201 -0
  213. package/src/core/decision/request.js +26 -0
  214. package/src/core/decision/resolver.js +138 -0
  215. package/src/core/decision/result.js +58 -0
  216. package/src/core/decision/service.js +181 -0
  217. package/src/core/decision/state-builder.js +65 -0
  218. package/src/core/decision/task-bindings.js +35 -0
  219. package/src/core/decision/test-provider.js +32 -0
  220. package/src/core/decision/thresholds.js +15 -0
  221. package/src/core/diagnosis-model.js +11 -0
  222. package/src/core/diagnosis.js +10 -1
  223. package/src/core/diagnostic-projection.js +29 -12
  224. package/src/core/diagnostic-record.js +28 -3
  225. package/src/core/efficiency.js +6 -1
  226. package/src/core/error-codes.js +278 -0
  227. package/src/core/events.js +535 -147
  228. package/src/core/evidence-readiness.js +9 -0
  229. package/src/core/execution-prerequisites.js +36 -13
  230. package/src/core/execution-profile.js +63 -38
  231. package/src/core/execution.js +26 -40
  232. package/src/core/filesystem.js +11 -0
  233. package/src/core/gate-artifact.js +105 -19
  234. package/src/core/gate-provenance.js +125 -0
  235. package/src/core/handoff.js +26 -14
  236. package/src/core/history.js +39 -35
  237. package/src/core/hypothesis-projection.js +32 -0
  238. package/src/core/information-gain-projection.js +104 -100
  239. package/src/core/inspect.js +12 -3
  240. package/src/core/integration-invocation-policy.js +37 -4
  241. package/src/core/integration-resources.js +119 -87
  242. package/src/core/ledger-event-collection.js +189 -0
  243. package/src/core/ledger-relations.js +23 -0
  244. package/src/core/model-router/constants.js +10 -0
  245. package/src/core/model-router/policy.js +103 -0
  246. package/src/core/model-router/router.js +37 -0
  247. package/src/core/native-storage.js +12 -0
  248. package/src/core/next-action-continuity.js +31 -9
  249. package/src/core/next-action-model.js +58 -0
  250. package/src/core/next-action-phases.js +131 -43
  251. package/src/core/next-action-refresh.js +43 -9
  252. package/src/core/next-action-review-phase.js +9 -4
  253. package/src/core/next-action-verification-phase.js +2 -1
  254. package/src/core/next-action.js +56 -9
  255. package/src/core/phase.js +266 -103
  256. package/src/core/policy-engine.js +12 -13
  257. package/src/core/preflight-consistency.js +89 -62
  258. package/src/core/preflight-loaders.js +48 -9
  259. package/src/core/preflight-model.js +6 -1
  260. package/src/core/preflight.js +29 -12
  261. package/src/core/progress.js +21 -10
  262. package/src/core/protocol-info.js +68 -0
  263. package/src/core/protocol-migration.js +7 -6
  264. package/src/core/protocol.js +20 -0
  265. package/src/core/reconcile-closure.js +281 -67
  266. package/src/core/recovery-history.js +67 -47
  267. package/src/core/reflection.js +72 -77
  268. package/src/core/responsibility.js +25 -13
  269. package/src/core/resumability.js +154 -44
  270. package/src/core/route-artifact.js +28 -4
  271. package/src/core/router.js +67 -1
  272. package/src/core/runtime-context.js +123 -61
  273. package/src/core/schema-validation.js +11 -3
  274. package/src/core/security-review/constants.js +64 -0
  275. package/src/core/security-review/normalize.js +245 -0
  276. package/src/core/security-review/provider.js +204 -0
  277. package/src/core/security-review/service.js +134 -0
  278. package/src/core/semantic-planning/constants.js +19 -0
  279. package/src/core/semantic-planning/projection.js +94 -0
  280. package/src/core/semantic-planning/service.js +15 -0
  281. package/src/core/settlement.js +12 -1
  282. package/src/core/signing/bundle-file.js +53 -0
  283. package/src/core/signing/bundle-reference.js +15 -0
  284. package/src/core/signing/sigstore.js +60 -33
  285. package/src/core/signing/statement-file.js +27 -0
  286. package/src/core/sources.js +37 -0
  287. package/src/core/storage-runtime-registry.js +41 -0
  288. package/src/core/structural-quality/artifacts.js +20 -14
  289. package/src/core/structural-quality/service.js +81 -56
  290. package/src/core/target-layout.js +1 -1
  291. package/src/core/task-claim-state.js +249 -15
  292. package/src/core/task-command.js +21 -1
  293. package/src/core/task-conflict-inspection.js +133 -102
  294. package/src/core/task-descriptor.js +29 -22
  295. package/src/core/task-discovery.js +288 -90
  296. package/src/core/task-lock.js +66 -306
  297. package/src/core/task-migration-validation.js +5 -5
  298. package/src/core/task-migration.js +17 -287
  299. package/src/core/task-paths.js +13 -0
  300. package/src/core/task-recovery.js +1 -0
  301. package/src/core/task-snapshot.js +51 -8
  302. package/src/core/task-storage-migration.js +18 -0
  303. package/src/core/templates.js +23 -8
  304. package/src/core/test-intelligence/benchmarks.js +68 -0
  305. package/src/core/test-intelligence/inventory.js +73 -0
  306. package/src/core/test-intelligence/prune.js +94 -0
  307. package/src/core/test-intelligence/semantic-state.js +15 -0
  308. package/src/core/test-intelligence/service.js +40 -0
  309. package/src/core/test-intelligence/utility.js +50 -0
  310. package/src/core/trace.js +164 -105
  311. package/src/core/trajectory-evaluation.js +15 -3
  312. package/src/core/trajectory-metrics.js +47 -32
  313. package/src/core/transaction.js +16 -287
  314. package/src/core/verification-scope.js +20 -12
  315. package/src/core/work-state.js +100 -59
  316. package/src/integration.d.ts +492 -0
  317. package/src/integration.js +54 -0
  318. package/src/providers/README.md +47 -0
  319. package/src/providers/capabilities.js +46 -0
  320. package/src/providers/errors.js +15 -0
  321. package/src/providers/index.js +29 -0
  322. package/src/providers/json-snapshot.js +105 -0
  323. package/src/providers/registry.js +152 -0
  324. package/src/repository-index/binary-manager.js +7 -5
  325. package/src/storage/artifact-bytes.js +22 -0
  326. package/src/storage/attachment-binding.js +11 -0
  327. package/src/storage/attachment-files.js +82 -0
  328. package/src/storage/attachment-references.js +36 -0
  329. package/src/storage/backup.js +53 -0
  330. package/src/storage/capabilities.js +31 -0
  331. package/src/storage/connection-owner.js +119 -0
  332. package/src/storage/connection.js +376 -0
  333. package/src/storage/existing-project-scope.js +45 -0
  334. package/src/storage/export-destination.js +44 -0
  335. package/src/storage/exporter.js +284 -0
  336. package/src/storage/file-durability.js +46 -0
  337. package/src/storage/importer.js +461 -0
  338. package/src/storage/index.js +74 -0
  339. package/src/storage/leases.js +63 -0
  340. package/src/storage/ledger-event-snapshot.js +150 -0
  341. package/src/storage/ledger-relations.js +220 -0
  342. package/src/storage/legacy-maintenance-boundary.js +18 -0
  343. package/src/storage/legacy-signature.js +115 -0
  344. package/src/storage/maintenance-handoff.js +129 -0
  345. package/src/storage/maintenance-owner.js +42 -0
  346. package/src/storage/maintenance.js +310 -0
  347. package/src/storage/metadata-json.js +35 -0
  348. package/src/storage/migration-activate.js +59 -0
  349. package/src/storage/migration-archive.js +57 -0
  350. package/src/storage/migration-attachment-root.js +61 -0
  351. package/src/storage/migration-candidate.js +398 -0
  352. package/src/storage/migration-publication.js +143 -0
  353. package/src/storage/migration-rollback-admission.js +54 -0
  354. package/src/storage/migration-rollback-preparation.js +132 -0
  355. package/src/storage/migration-rollback-publication.js +183 -0
  356. package/src/storage/migration-rollback-release.js +66 -0
  357. package/src/storage/migration-rollback-source-stage.js +103 -0
  358. package/src/storage/migration-rollback.js +43 -0
  359. package/src/storage/migration-source-partition.js +56 -0
  360. package/src/storage/migration-source.js +255 -0
  361. package/src/storage/migration-terminal.js +39 -0
  362. package/src/storage/migration-validation.js +75 -0
  363. package/src/storage/migration.js +43 -0
  364. package/src/storage/operational-attachments.js +49 -0
  365. package/src/storage/operational-context.js +50 -0
  366. package/src/storage/project-backup.js +98 -0
  367. package/src/storage/project-bootstrap.js +24 -0
  368. package/src/storage/project-boundary.js +79 -0
  369. package/src/storage/project-read-snapshot.js +18 -0
  370. package/src/storage/project-replacement.js +137 -0
  371. package/src/storage/project-restore.js +203 -0
  372. package/src/storage/record-definitions.js +45 -0
  373. package/src/storage/repository.js +507 -0
  374. package/src/storage/restore-journal.js +62 -0
  375. package/src/storage/restore-layout.js +30 -0
  376. package/src/storage/restore-preparation.js +84 -0
  377. package/src/storage/restore-replacement-archive.js +162 -0
  378. package/src/storage/restore-replacement-owner.js +31 -0
  379. package/src/storage/restore-replacement-preparation.js +128 -0
  380. package/src/storage/restore-replacement.js +142 -0
  381. package/src/storage/restore-terminal.js +32 -0
  382. package/src/storage/runtime.js +31 -0
  383. package/src/storage/schema.js +214 -0
  384. package/src/storage/singleton-import.js +84 -0
  385. package/src/storage/snapshot-continuity.js +16 -0
  386. package/src/storage/snapshot.js +36 -0
  387. package/src/storage/storage-marker.js +79 -0
  388. package/src/storage/task-guards.js +107 -0
  389. package/src/storage/topology.js +42 -0
  390. package/src/storage/transaction.js +179 -0
  391. package/src/storage/unit-of-work.js +663 -0
  392. package/src/storage/windows-process-incarnation.js +206 -0
  393. package/.forgeloop/forgeloop.gitignore +0 -4
  394. package/LICENSE-DOCS.md +0 -13
  395. package/src/core/transaction-maintenance.js +0 -70
@@ -19,10 +19,17 @@ function validatePolicyInput(command, options) {
19
19
  if (command !== "policy" && options.policy) throw inputError(`Policy name is not valid for ${command}`);
20
20
  }
21
21
 
22
- function validateTaskCreationInput(command, options) {
22
+ function validateContractBootstrapRepairMigrationInput(command, options) {
23
+ if (command !== "task-migrate-contract-bootstrap-repair") return;
24
+ if (!options.taskId) throw inputError("task-migrate-contract-bootstrap-repair requires --task");
25
+ if (!options.acknowledgeMigration) throw inputError("task-migrate-contract-bootstrap-repair requires --acknowledge-migration");
26
+ }
27
+
28
+ function validateTaskCreationInput(command, options, help = false) {
29
+ if (help) return;
23
30
  if (command === "bundle" && !options.taskId) throw inputError("bundle requires --task");
24
31
  if (command === "task-create" && !options.taskId) throw inputError("task-create requires --task");
25
- if (command !== "task-create" && (options.preset || options.preview)) {
32
+ if (!["task-create", "contract-create", "contract-revise"].includes(command) && (options.preset || options.preview)) {
26
33
  throw inputError(`preset/preview options are not valid for ${command}`);
27
34
  }
28
35
  if (command === "task-create" && options.preview && !options.preset && !options.contractFile) {
@@ -31,6 +38,15 @@ function validateTaskCreationInput(command, options) {
31
38
  if (command === "task-create" && options.preset && options.contractFile) {
32
39
  throw inputError("task-create accepts either --preset or --contract-file, not both");
33
40
  }
41
+ if (command === "contract-create" && !options.taskId) throw inputError("contract-create requires --task");
42
+ if (command === "contract-create" && !options.preset && !options.contractFile) throw inputError("contract-create requires --preset or --contract-file");
43
+ if (command === "contract-create" && options.preset && options.contractFile) throw inputError("contract-create accepts either --preset or --contract-file, not both");
44
+ if (command === "contract-revise" && !options.taskId) throw inputError("contract-revise requires --task");
45
+ if (command === "contract-revise" && !options.preset && !options.contractFile) throw inputError("contract-revise requires --preset or --contract-file");
46
+ if (command === "contract-revise" && options.preset && options.contractFile) throw inputError("contract-revise accepts either --preset or --contract-file, not both");
47
+ if (command === "task-repair-contract-bootstrap" && !options.taskId) throw inputError("task-repair-contract-bootstrap requires --task");
48
+ if (command === "task-repair-contract-bootstrap" && !options.acknowledgeRepair) throw inputError("task-repair-contract-bootstrap requires --acknowledge-repair");
49
+ validateContractBootstrapRepairMigrationInput(command, options);
34
50
  const hasTaskListFilter = Boolean(
35
51
  options.phase
36
52
  || options.active
@@ -42,6 +58,15 @@ function validateTaskCreationInput(command, options) {
42
58
  }
43
59
  }
44
60
 
61
+ function validateGateRecordInput(command, options, help) {
62
+ if (command !== "gate-record" || help) return;
63
+ if (!options.taskId) throw inputError("gate-record requires --task");
64
+ if (!options.gate) throw inputError("gate-record requires --gate");
65
+ if (!["satisfied", "unverified", "blocked"].includes(options.gateStatus)) {
66
+ throw inputError("gate-record --status must be satisfied, unverified, or blocked");
67
+ }
68
+ }
69
+
45
70
  function validateExecutionProfileInput(command, options) {
46
71
  if (options.executionProfile !== null && options.executionProfile !== undefined) {
47
72
  if (command !== "route") throw inputError(`executionProfile is not valid for ${command}`);
@@ -91,54 +116,91 @@ function validateOutputInput(command, options) {
91
116
  if (command !== "next" && options.explain === true) throw inputError(`explain output is not valid for ${command}`);
92
117
  }
93
118
 
94
- function validateTaskSelectorsInput(command, options, help) {
95
- const taskCommands = ["workspace-bind", "workspace-status", "handoff-create", "handoff-list", "handoff-show", "handoff-accept", "responsibility-set", "responsibility-status", "verify-scope", "attestation-create", "attestation-status", "attestation-verify"];
96
- if (taskCommands.includes(command) && !options.taskId) throw inputError(`${command} requires --task`);
97
- if (command === "handoff-show" && !help && !options.handoffId) throw inputError("handoff-show requires --id");
98
- if (command === "handoff-accept" && !help) {
99
- if (!options.handoffId) throw inputError("handoff-accept requires --handoff");
100
- if (!options.consumerId) throw inputError("handoff-accept requires --consumer-id");
119
+ const TASK_SELECTOR_COMMANDS = new Set([
120
+ "context-plan",
121
+ "model-route",
122
+ "semantic-plan",
123
+ "test-utility",
124
+ "checkpoint-revalidate",
125
+ "workspace-bind",
126
+ "workspace-status",
127
+ "handoff-create",
128
+ "handoff-list",
129
+ "handoff-show",
130
+ "handoff-accept",
131
+ "responsibility-set",
132
+ "responsibility-status",
133
+ "verify-scope",
134
+ "attestation-create",
135
+ "attestation-status",
136
+ "attestation-verify",
137
+ ]);
138
+
139
+ function validateRequiredTaskSelector(command, options, help) {
140
+ if (TASK_SELECTOR_COMMANDS.has(command) && !help && !options.taskId) {
141
+ throw inputError(`${command} requires --task`);
101
142
  }
143
+ }
144
+
145
+ function validateHandoffShowInput(command, options, help) {
146
+ if (command === "handoff-show" && !help && !options.handoffId) throw inputError("handoff-show requires --id");
147
+ }
148
+
149
+ function validateHandoffAcceptInput(command, options, help) {
150
+ if (command !== "handoff-accept" || help) return;
151
+ if (!options.handoffId) throw inputError("handoff-accept requires --handoff");
152
+ if (!options.consumerId) throw inputError("handoff-accept requires --consumer-id");
153
+ }
154
+
155
+ function validateResponsibilityInput(command, options, help) {
102
156
  if (command === "responsibility-set" && !help && !options.responsibilityLabel) {
103
157
  throw inputError("responsibility-set requires --label");
104
158
  }
105
- if (command === "verify-scope" && !help) {
106
- const mode = String(options.verificationScopeMode ?? "AUTO").toUpperCase();
107
- if (!["AUTO", "CHANGED", "CLAIMED", "FULL"].includes(mode)) throw inputError("verify-scope --mode must be AUTO, CHANGED, CLAIMED, or FULL");
108
- }
109
- if (command === "attestation-verify-range" && !help) {
110
- if (!options.baseRevision) throw inputError("attestation-verify-range requires --base");
111
- if (!options.headRevision) throw inputError("attestation-verify-range requires --head");
159
+ }
160
+
161
+ function validateVerificationScopeInput(command, options, help) {
162
+ if (command !== "verify-scope" || help) return;
163
+ const mode = String(options.verificationScopeMode ?? "AUTO").toUpperCase();
164
+ if (!["AUTO", "CHANGED", "CLAIMED", "FULL"].includes(mode)) throw inputError("verify-scope --mode must be AUTO, CHANGED, CLAIMED, or FULL");
165
+ }
166
+
167
+ function validateAttestationRangeInput(command, options, help) {
168
+ if (command !== "attestation-verify-range" || help) return;
169
+ if (!options.baseRevision) throw inputError("attestation-verify-range requires --base");
170
+ if (!options.headRevision) throw inputError("attestation-verify-range requires --head");
171
+ }
172
+
173
+ function validateTaskSelectorsInput(command, options, help) {
174
+ validateRequiredTaskSelector(command, options, help);
175
+ validateHandoffShowInput(command, options, help);
176
+ validateHandoffAcceptInput(command, options, help);
177
+ validateResponsibilityInput(command, options, help);
178
+ validateVerificationScopeInput(command, options, help);
179
+ validateAttestationRangeInput(command, options, help);
180
+ }
181
+
182
+ function validateRecordCheckInput(options) {
183
+ if (!options.checkId) throw inputError("record-check requires --id");
184
+ if (!options.checkRequirement) throw inputError("record-check requires --requirement");
185
+ if (!options.checkStatus) throw inputError("record-check requires --status");
186
+ if (!options.checkEvidenceKind) throw inputError("record-check requires --evidence-kind");
187
+ if (!options.checkCommand && !options.checkResult) throw inputError("record-check requires --command or --result");
188
+ }
189
+
190
+ function validateCommandCheckInput(options, command) {
191
+ if (!options.checkId) throw inputError(`${command} requires --id`);
192
+ if (!options.checkRequirement) throw inputError(`${command} requires --requirement`);
193
+ if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
194
+ || options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
195
+ throw inputError(`${command} accepts only --id, --requirement, --details,${command === "run-check" ? " --timeout-ms, --scope-ref," : ""} and -- <argv>`);
112
196
  }
197
+ if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) throw inputError(`${command} requires -- followed by an exact command argv`);
113
198
  }
114
199
 
115
200
  function validateCheckInput(command, options, help) {
116
- if (command === "record-check" && !help) {
117
- if (!options.checkId) throw inputError("record-check requires --id");
118
- if (!options.checkRequirement) throw inputError("record-check requires --requirement");
119
- if (!options.checkStatus) throw inputError("record-check requires --status");
120
- if (!options.checkEvidenceKind) throw inputError("record-check requires --evidence-kind");
121
- if (!options.checkCommand && !options.checkResult) throw inputError("record-check requires --command or --result");
122
- }
123
- if (command === "run-check" && !help) {
124
- if (!options.checkId) throw inputError("run-check requires --id");
125
- if (!options.checkRequirement) throw inputError("run-check requires --requirement");
126
- if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
127
- || options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
128
- throw inputError("run-check accepts only --id, --requirement, --details, --timeout-ms, --scope-ref, and -- <argv>");
129
- }
130
- if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) throw inputError("run-check requires -- followed by an exact command argv");
131
- }
132
- if (command === "reconcile-closure" && !help) {
133
- if (!options.taskId) throw inputError("reconcile-closure requires --task");
134
- if (!options.checkId) throw inputError("reconcile-closure requires --id");
135
- if (!options.checkRequirement) throw inputError("reconcile-closure requires --requirement");
136
- if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
137
- || options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
138
- throw inputError("reconcile-closure accepts only --id, --requirement, --details, and -- <argv>");
139
- }
140
- if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) throw inputError("reconcile-closure requires -- followed by an exact command argv");
141
- }
201
+ if (help) return;
202
+ if (command === "record-check") validateRecordCheckInput(options);
203
+ if (command === "run-check" || command === "reconcile-closure") validateCommandCheckInput(options, command);
142
204
  }
143
205
 
144
206
  /**
@@ -172,6 +234,8 @@ export function defaultCommandInputValues() {
172
234
  assetPath: null,
173
235
  binaryPath: null,
174
236
  force: false,
237
+ acknowledgeRepair: false,
238
+ acknowledgeMigration: false,
175
239
  workType: null,
176
240
  surfaces: [],
177
241
  risks: [],
@@ -193,6 +257,13 @@ export function defaultCommandInputValues() {
193
257
  file: null,
194
258
  contractFile: null,
195
259
  preset: null,
260
+ gate: null,
261
+ gateStatus: null,
262
+ gateArtifacts: [],
263
+ gateDecisions: [],
264
+ gateUnknowns: [],
265
+ gateAssumptions: [],
266
+ gateEvidenceFile: null,
196
267
  preview: false,
197
268
  phase: null,
198
269
  active: false,
@@ -253,6 +324,25 @@ export function defaultCommandInputValues() {
253
324
  };
254
325
  }
255
326
 
327
+ function validateStorageRestoreInput(command, options, help) {
328
+ if (command === "storage-restore-resume" && !help && (options.writersQuiesced !== true || typeof options.operationId !== "string" || !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(options.operationId) || typeof options.expectedOwnerId !== "string" || !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(options.expectedOwnerId))) throw inputError("storage-restore-resume requires exact --operation and --expected-owner UUIDs and explicit --writers-quiesced");
329
+ if (["storage-restore", "storage-restore-resume"].includes(command) && options.replaceActive !== undefined && typeof options.replaceActive !== "boolean") throw inputError("Active replacement selection must be a boolean");
330
+ if (command === "storage-restore" && !help && (typeof options.source !== "string" || !options.source || options.writersQuiesced !== true)) throw inputError("storage-restore requires --source and explicit --writers-quiesced");
331
+ if (command === "storage-backup" && !help && (typeof options.destination !== "string" || !options.destination)) throw inputError("storage-backup requires --destination");
332
+ }
333
+
334
+ function validateStorageMigrationInput(command, options, help) {
335
+ if (command === "storage-rollback-resume" && !help && !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(options.expectedOwnerId ?? "")) throw inputError("storage-rollback-resume requires exact --expected-owner UUID");
336
+ if (["storage-rollback", "storage-rollback-resume"].includes(command) && !help && (typeof options.destination !== "string" || !options.destination || typeof options.legacyRoot !== "string" || !options.legacyRoot || options.writersQuiesced !== true || options.nativeWritesExcluded !== true)) throw inputError("storage-rollback requires --destination, --legacy-root, explicit --writers-quiesced and --native-writes-excluded");
337
+ if (command === "storage-migrate" && !help && (typeof options.destination !== "string" || !options.destination || options.writersQuiesced !== true)) throw inputError("storage-migrate requires --destination and explicit --writers-quiesced");
338
+ if (command === "storage-migration-resume" && !help && (typeof options.destination !== "string" || !options.destination || options.writersQuiesced !== true || typeof options.expectedOwnerId !== "string" || !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(options.expectedOwnerId))) throw inputError("storage-migration-resume requires --destination, exact --expected-owner UUID and explicit --writers-quiesced");
339
+ validateUnboundHandoffAcknowledgement(command, options);
340
+ }
341
+
342
+ function validateUnboundHandoffAcknowledgement(command, options) {
343
+ if (command === "storage-migration-resume" && options.acknowledgeUnboundHandoff !== undefined && typeof options.acknowledgeUnboundHandoff !== "boolean") throw inputError("--reconcile-unbound-handoff must be a boolean acknowledgement");
344
+ }
345
+
256
346
  /**
257
347
  * Canonical semantic validation shared by the CLI argv parser and
258
348
  * programmatic integrations. JSON-Schema style checks cannot express these
@@ -261,9 +351,11 @@ export function defaultCommandInputValues() {
261
351
  export function validateForgeLoopCommandInput({ command, input, help = false } = {}) {
262
352
  const options = input ?? {};
263
353
  if (!command) return;
264
-
354
+ validateStorageRestoreInput(command, options, help);
355
+ validateStorageMigrationInput(command, options, help);
265
356
  validatePolicyInput(command, options);
266
- validateTaskCreationInput(command, options);
357
+ validateTaskCreationInput(command, options, help);
358
+ validateGateRecordInput(command, options, help);
267
359
  validateSearchCommandInput(command, options, help);
268
360
  validateProfileAndUsageInput(command, options, help);
269
361
  validateOutputInput(command, options);
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import { getTaskTransaction, withTaskTransaction } from "./transaction.js";
2
3
  import {
3
4
  ARTIFACT_PATHS,
4
5
  canonicalFingerprint,
@@ -18,7 +19,7 @@ import { readPersistedRoute } from "./route-artifact.js";
18
19
  import { createReceipt, validateReceipt } from "./receipt.js";
19
20
  import { readWorkState, mutateWorkState } from "./work-state.js";
20
21
  import { assertExecutionPrerequisites, hasExecutionStarted } from "./execution-prerequisites.js";
21
- import { normalizeRequirements } from "./evidence-readiness.js";
22
+ import { normalizeRequirements, terminalRequirementsForContract } from "./evidence-readiness.js";
22
23
  import { classifyCommandResolution, validateVerificationAuthority } from "./verification-capability.js";
23
24
  import { readExecutionArtifact, validateExecutionBinding } from "./execution.js";
24
25
  import { taskArtifactPath, taskExecutionPath } from "./task-paths.js";
@@ -80,10 +81,18 @@ function requiredString(value, label) {
80
81
  return value;
81
82
  }
82
83
 
84
+ async function readBoundReceiptArtifact(target, receiptRel, packageRoot) {
85
+ try { return await readJsonArtifact(target, receiptRel, "execution-receipt", packageRoot); }
86
+ catch (error) {
87
+ if (error.code === "E_STORAGE_PAYLOAD_MISMATCH" && error.taskIdentityMismatch) throw artifactError("E_RECEIPT_TASK_MISMATCH", "Execution receipt does not belong to the current contract task", [receiptRel]);
88
+ throw error;
89
+ }
90
+ }
91
+
83
92
  async function readCurrentReceipt(target, packageRoot, options = {}) {
84
93
  const receiptRel = options.receiptPath ?? (options.taskId ? taskArtifactPath(options.taskId, "receipt") : ARTIFACT_PATHS.receipt);
85
94
  try {
86
- return await readJsonArtifact(target, receiptRel, "execution-receipt", packageRoot);
95
+ return await readBoundReceiptArtifact(target, receiptRel, packageRoot);
87
96
  } catch (error) {
88
97
  if (error.code === "ARTIFACT_MISSING") {
89
98
  throw artifactError(
@@ -245,7 +254,21 @@ export async function requiredEvidenceForTarget({
245
254
  ])].sort();
246
255
  }
247
256
 
248
- export async function prepareCompletion({
257
+ async function resolveCompletionTaskOptions(options) {
258
+ if (options.taskId) return options;
259
+ const state = await readWorkState(options.target, { packageRoot: options.packageRoot, statePath: options.statePath });
260
+ return { ...options, taskId: state?.taskId };
261
+ }
262
+
263
+ export async function prepareCompletion(options = {}) {
264
+ options = await resolveCompletionTaskOptions(options);
265
+ if (!options.taskId) return prepareSelectedCompletion(options);
266
+ return withTaskTransaction({ target: options.target, packageRoot: options.packageRoot,
267
+ taskId: options.taskId, operation: "prepare-completion", recordCommitEvent: true },
268
+ () => prepareSelectedCompletion(options));
269
+ }
270
+
271
+ async function prepareSelectedCompletion({
249
272
  target,
250
273
  packageRoot,
251
274
  authorityContext,
@@ -255,6 +278,7 @@ export async function prepareCompletion({
255
278
  routePath = null,
256
279
  statePath = null,
257
280
  receiptPath = null,
281
+ eventsPath,
258
282
  }) {
259
283
  const contract = await readContract(target, packageRoot, { taskId, contractPath });
260
284
  const route = await readPersistedRoute(target, packageRoot, { taskId, routePath });
@@ -266,12 +290,12 @@ export async function prepareCompletion({
266
290
  throw artifactError("E_STATE_MISSING", "Work state is required before preparing completion", [stateRel]);
267
291
  }
268
292
  if (hasExecutionStarted(state.phase)) {
269
- await assertExecutionPrerequisites({ target, state, packageRoot, taskId, statePath, contractPath, routePath });
293
+ await assertExecutionPrerequisites({ target, state, packageRoot, taskId, statePath, contractPath, routePath, eventsPath });
270
294
  }
271
295
 
272
296
  let existing = null;
273
297
  try {
274
- existing = await readJsonArtifact(target, receiptRel, "execution-receipt", packageRoot);
298
+ existing = await readBoundReceiptArtifact(target, receiptRel, packageRoot);
275
299
  await validateReceipt(existing.value, packageRoot, {
276
300
  target,
277
301
  taskId: contract.value.taskId,
@@ -463,7 +487,7 @@ export async function assertRecordCheckPrerequisites({
463
487
  [stateRel],
464
488
  );
465
489
  }
466
- await assertExecutionPrerequisites({ target, state, packageRoot, taskId, statePath, contractPath, routePath });
490
+ await assertExecutionPrerequisites({ target, state, packageRoot, taskId, statePath, contractPath, routePath, eventsPath });
467
491
 
468
492
  const contract = await readContract(target, packageRoot, { taskId, contractPath });
469
493
  const route = await readPersistedRoute(target, packageRoot, { taskId, routePath });
@@ -478,10 +502,15 @@ export async function assertRecordCheckPrerequisites({
478
502
  const requested = normalizeRequirements(requiredEvidence).find((item) => (
479
503
  item.id === requirement || item.text === requirement
480
504
  ));
481
- if (requested?.terminalOwned && status === "passed" && evidenceKind === "OBSERVED") {
505
+ const terminalRequested = terminalRequirementsForContract(contract.value, {
506
+ additionalEvidence: preflight.policy?.requiredEvidence ?? [],
507
+ }).find((item) => (
508
+ item.id === requirement || item.text === requirement
509
+ ));
510
+ if ((requested?.terminalOwned || terminalRequested) && status === "passed" && evidenceKind === "OBSERVED") {
482
511
  throw artifactError(
483
512
  "E_FUTURE_LIFECYCLE_EVIDENCE",
484
- `Terminal-owned requirement cannot be recorded before its authoritative result: ${requested.text}`,
513
+ `Terminal-owned requirement cannot be recorded before its authoritative result: ${(requested ?? terminalRequested).text}`,
485
514
  [stateRel, eventsRel],
486
515
  );
487
516
  }
@@ -516,29 +545,15 @@ export async function assertRecordCheckPrerequisites({
516
545
  };
517
546
  }
518
547
 
519
- export async function recordCheck({
520
- target,
521
- packageRoot,
522
- id,
523
- kind = "command",
524
- requirement,
525
- status,
526
- evidenceKind,
527
- command,
528
- result,
529
- exitCode,
530
- details,
531
- executionRef,
532
- provenance,
533
- authorityContext,
534
- runtimeContext,
535
- taskId = null,
536
- contractPath = null,
537
- routePath = null,
538
- statePath = null,
539
- receiptPath = null,
540
- eventsPath = null,
541
- }) {
548
+ export async function recordCheck(options) {
549
+ validateRecordCheckInput(options);
550
+ options = await resolveCompletionTaskOptions(options);
551
+ if (!options.taskId) return recordValidatedCheck(options);
552
+ return withTaskTransaction({ target: options.target, packageRoot: options.packageRoot,
553
+ taskId: options.taskId, operation: "record-check", recordCommitEvent: true }, () => recordValidatedCheck(options));
554
+ }
555
+
556
+ function validateRecordCheckInput({ id, kind = "command", requirement, status, evidenceKind, command, result, details, executionRef, provenance }) {
542
557
  requiredString(id, "check id");
543
558
  requiredString(kind, "check kind");
544
559
  requiredString(requirement, "check requirement");
@@ -563,7 +578,31 @@ export async function recordCheck({
563
578
  && (typeof result !== "string" || result.trim() === "")) {
564
579
  throw artifactError("E_CHECK_INVALID", "record-check requires --command or --result");
565
580
  }
581
+ }
566
582
 
583
+ async function recordValidatedCheck({
584
+ target,
585
+ packageRoot,
586
+ id,
587
+ kind = "command",
588
+ requirement,
589
+ status,
590
+ evidenceKind,
591
+ command,
592
+ result,
593
+ exitCode,
594
+ details,
595
+ executionRef,
596
+ provenance,
597
+ authorityContext,
598
+ runtimeContext,
599
+ taskId = null,
600
+ contractPath = null,
601
+ routePath = null,
602
+ statePath = null,
603
+ receiptPath = null,
604
+ eventsPath = null,
605
+ }) {
567
606
  const context = await assertRecordCheckPrerequisites({
568
607
  target,
569
608
  packageRoot,
@@ -775,7 +814,15 @@ export async function recordCheck({
775
814
  };
776
815
  }
777
816
 
778
- export async function recordTerminalResult({
817
+ export async function recordTerminalResult(options = {}) {
818
+ if (!options.target) return recordTerminalResultInTransaction(options);
819
+ if (await getTaskTransaction(options.target)) return recordTerminalResultInTransaction(options);
820
+ const state = await readWorkState(options.target, { packageRoot: options.packageRoot, taskId: options.taskId, statePath: options.statePath });
821
+ if (!state) return recordTerminalResultInTransaction(options);
822
+ return withTaskTransaction({ target: options.target, taskId: state.taskId, packageRoot: options.packageRoot, operation: "record-terminal-result" }, () => recordTerminalResultInTransaction({ ...options, taskId: options.taskId ?? state.taskId }));
823
+ }
824
+
825
+ async function recordTerminalResultInTransaction({
779
826
  target,
780
827
  packageRoot,
781
828
  requirement,
@@ -827,8 +874,12 @@ export async function recordTerminalResult({
827
874
  additionalEvidence: preflight.policy?.requiredEvidence ?? [],
828
875
  });
829
876
 
830
- const normalized = normalizeRequirements(requiredEvidence);
831
- const requested = normalized.find((item) => (
877
+ const terminalRequirements = terminalRequirementsForContract(contract.value, {
878
+ additionalEvidence: preflight.policy?.requiredEvidence ?? [],
879
+ });
880
+ const requested = terminalRequirements.find((item) => (
881
+ item.id === requirement || item.text === requirement
882
+ )) ?? normalizeRequirements(requiredEvidence).find((item) => (
832
883
  item.id === requirement || item.text === requirement
833
884
  ));
834
885
 
@@ -1,7 +1,19 @@
1
+ import { isLedgerEventCollection } from "./ledger-event-collection.js";
1
2
  import { LIFECYCLE_MILESTONES, validateStateLedgerCoherence } from "./events.js";
2
3
 
3
4
  export const CANONICAL_COMPLETION_EVENT = "COMPLETION_VALIDATED";
4
5
 
6
+ function completionCandidates(events, taskId) {
7
+ let count = 0;
8
+ let candidate = null;
9
+ for (const event of events) {
10
+ if (event.event !== CANONICAL_COMPLETION_EVENT || event.taskId !== taskId) continue;
11
+ count += 1;
12
+ candidate = event;
13
+ }
14
+ return { count, candidate };
15
+ }
16
+
5
17
  /**
6
18
  * Canonical completion ownership proof: the minimal, validator-backed evidence
7
19
  * that the lifecycle itself officially reached COMPLETE. This is intentionally
@@ -28,31 +40,30 @@ export function validateCompletionOwnershipProof({ taskId, state, ledger }) {
28
40
  }
29
41
 
30
42
  let completionEvent = null;
31
- if (ledger && Array.isArray(ledger.events)) {
43
+ if (ledger && isLedgerEventCollection(ledger.events)) {
32
44
  if (ledger.events.some((event) => event.taskId !== taskId)) {
33
45
  errors.push({
34
46
  code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
35
47
  message: "Ledger contains an event belonging to a different task",
36
48
  });
37
49
  }
38
- const candidates = ledger.events
39
- .filter((event) => event.event === CANONICAL_COMPLETION_EVENT && event.taskId === taskId);
40
- if (candidates.length === 0) {
50
+ const { count, candidate } = completionCandidates(ledger.events, taskId);
51
+ if (count === 0) {
41
52
  errors.push({
42
53
  code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
43
54
  message: `No canonical ${CANONICAL_COMPLETION_EVENT} event exists for this task`,
44
55
  });
45
- } else if (candidates.length > 1) {
56
+ } else if (count > 1) {
46
57
  errors.push({
47
58
  code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
48
59
  message: `Multiple ${CANONICAL_COMPLETION_EVENT} events exist; completion is ambiguous`,
49
60
  });
50
61
  } else {
51
- completionEvent = candidates[0];
62
+ completionEvent = candidate;
52
63
  }
53
64
  }
54
65
 
55
- if (state && ledger && Array.isArray(ledger.events)) {
66
+ if (state && ledger && isLedgerEventCollection(ledger.events)) {
56
67
  const coherenceErrors = validateStateLedgerCoherence(state, ledger.events);
57
68
  if (coherenceErrors.length > 0) {
58
69
  for (const error of coherenceErrors) {
@@ -67,7 +78,7 @@ export function validateCompletionOwnershipProof({ taskId, state, ledger }) {
67
78
  // No contradictory lifecycle activity may follow the canonical completion:
68
79
  // any milestone at or after VERIFICATION_RECORDED occurring after the
69
80
  // completion event means the lifecycle moved past terminal state.
70
- if (completionEvent && ledger && Array.isArray(ledger.events)) {
81
+ if (completionEvent && ledger && isLedgerEventCollection(ledger.events)) {
71
82
  const completionIndex = ledger.events.indexOf(completionEvent);
72
83
  const contradiction = ledger.events.slice(completionIndex + 1).find((event) => {
73
84
  const index = LIFECYCLE_MILESTONES.indexOf(event.event);
@@ -3,6 +3,7 @@ import { appendProtocolEvent, validateCompletionRecoveryAuthorization, validateE
3
3
  import { createReceipt } from "./receipt.js";
4
4
  import { taskArtifactPath } from "./task-paths.js";
5
5
  import { mutateWorkState, readWorkState } from "./work-state.js";
6
+ import { getTaskTransaction, withTaskTransaction } from "./transaction.js";
6
7
 
7
8
  function resolveArtifactPath(key, taskId, override) {
8
9
  if (override) return override;
@@ -65,7 +66,16 @@ function findMatchingRejectionEvent(events, attempt, cycle) {
65
66
  * rejection carrying the current fingerprints is appended,
66
67
  * - the execution receipt is re-bound to the current checkpoint when present.
67
68
  */
68
- export async function rebindCompletionRejectionSnapshot({
69
+ export async function rebindCompletionRejectionSnapshot(options = {}) {
70
+ if (await getTaskTransaction(options.target)) return rebindRejectionInTransaction(options);
71
+ const state = await readWorkState(options.target, { packageRoot: options.packageRoot,
72
+ taskId: options.taskId, statePath: resolveArtifactPath("state", options.taskId, options.statePath) });
73
+ if (!state) return { rebound: false };
74
+ return withTaskTransaction({ target: options.target, taskId: options.taskId ?? state.taskId,
75
+ packageRoot: options.packageRoot, operation: "rebind-completion-rejection" }, () => rebindRejectionInTransaction(options));
76
+ }
77
+
78
+ async function rebindRejectionInTransaction({
69
79
  target,
70
80
  packageRoot,
71
81
  taskId = null,