@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
package/src/core/phase.js CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  LIFECYCLE_MILESTONES,
6
6
  validateEventLedger,
7
7
  validateStateLedgerCoherence,
8
+ isRevisionEpochPlanRepeat,
8
9
  } from "./events.js";
9
10
  import { readPersistedRoute } from "./route-artifact.js";
10
11
  import { assertWorkPhase, isValidTransition } from "./protocol.js";
@@ -13,7 +14,7 @@ import { authorizeCompletionRecoveryOrRebind } from "./completion-recovery-rebin
13
14
  import { evaluateCompletion, runComplete as persistCompletion } from "./completion.js";
14
15
  import { evaluatePreflight } from "./preflight.js";
15
16
  import { requiredEvidenceForTarget } from "./completion-artifacts.js";
16
- import { assertCompletionRelationships, assertStateIdentity } from "./completion-relationships.js";
17
+ import { assertCompletionRelationships, assertStateIdentity, stateIdentityErrors } from "./completion-relationships.js";
17
18
  import { createReceipt, validateReceipt } from "./receipt.js";
18
19
  import { assertExecutionPrerequisites, hasExecutionStarted } from "./execution-prerequisites.js";
19
20
  import { taskArtifactPath } from "./task-paths.js";
@@ -27,6 +28,13 @@ import {
27
28
  evaluateStructuredDiagnosticStall,
28
29
  } from "./information-gain-projection.js";
29
30
  import { assertStructuralQualityExecutionReady } from "./structural-quality/service.js";
31
+ import { getTaskTransaction, withTaskTransaction } from "./transaction.js";
32
+ import {
33
+ CONTRACT_BOOTSTRAP_REPAIR_EVENT,
34
+ ROUTE_CHECKPOINT_BOUND_EVENT,
35
+ resolveCanonicalPostRepairCheckpointBinding,
36
+ sameCanonicalGuideList,
37
+ } from "./contract-bootstrap-recovery.js";
30
38
 
31
39
  function phaseError(code, message, artifacts = []) {
32
40
  const error = new Error(message);
@@ -55,44 +63,135 @@ const LATE_PHASES = new Set([
55
63
  "COMPLETE",
56
64
  ]);
57
65
 
58
- async function assertPersistedStateIdentity(target, state, toPhase, packageRoot, options = {}) {
66
+ /**
67
+ * Pure prerequisite for the `DIAGNOSING -> CORRECTING` edge.
68
+ *
69
+ * Extracted so the filesystem advance and the SQLite store advance enforce the
70
+ * identical rule through one owner. The store implementation must not restate
71
+ * this; it calls this function with its own loaded evidence.
72
+ *
73
+ * Throws `E_DIAGNOSIS_REQUIRED` when no append-only diagnosis exists for the
74
+ * active verification cycle, and `E_DIAGNOSIS_NO_NEW_INFORMATION` when the
75
+ * diagnosis carries no new information.
76
+ */
77
+ export function assertDiagnosingToCorrectingTransition({ state, events, resolveDiagnosis }) {
78
+ const cycle = state?.verificationCycle ?? 1;
79
+ const diagEvent = resolveDiagnosis(events, state?.taskId, cycle);
80
+ if (!diagEvent) {
81
+ throw phaseError(
82
+ "E_DIAGNOSIS_REQUIRED",
83
+ "DIAGNOSING -> CORRECTING requires an append-only diagnosis record for the active verification cycle",
84
+ [],
85
+ );
86
+ }
87
+ const structured = diagEvent.sourceModel === "STRUCTURED_DIAGNOSTIC_CASE_V1";
88
+ const gainClassification = structured ? null : diagEvent.details?.informationGain;
89
+ if (!diagEvent.details || (structured ? false : gainClassification === "NONE")) {
90
+ throw phaseError(
91
+ "E_DIAGNOSIS_NO_NEW_INFORMATION",
92
+ "The proposed retry repeats the previous hypothesis with the same evidence without new information",
93
+ [],
94
+ );
95
+ }
96
+ return diagEvent;
97
+ }
98
+
99
+ async function readPhaseIdentityArtifacts(target, state, toPhase, packageRoot, options, paths) {
59
100
  const scopedTaskId = options.taskId ?? null;
60
101
  const requireContract = LATE_PHASES.has(state.phase)
61
102
  || ["CONTRACT_READY", "ROUTED", "EXECUTING"].includes(toPhase);
62
- const requireRoute = LATE_PHASES.has(state.phase)
103
+ const requireRoute = state.phase === "ROUTED"
104
+ || LATE_PHASES.has(state.phase)
63
105
  || ["ROUTED", "EXECUTING"].includes(toPhase);
64
- const contractRel = options.contractPath ?? (scopedTaskId ? taskArtifactPath(scopedTaskId, "contract") : ARTIFACT_PATHS.contract);
65
- const routeRel = options.routePath ?? (scopedTaskId ? taskArtifactPath(scopedTaskId, "route") : ARTIFACT_PATHS.route);
66
-
67
106
  let contract = null;
68
107
  let route = null;
69
108
  try {
70
109
  contract = await readContract(target, packageRoot, { taskId: scopedTaskId, contractPath: options.contractPath });
71
110
  } catch (error) {
111
+ if (error.code === "E_STATE_REVISION_CONFLICT") throw error;
72
112
  if (error.code === "ARTIFACT_MISSING" && !requireContract) {
73
113
  contract = null;
74
114
  } else {
75
115
  throw phaseError(
76
116
  requireContract ? "E_PHASE_PREREQUISITE_MISSING" : error.code ?? "E_CONTRACT_INVALID",
77
117
  `${requireContract ? `Phase ${toPhase} requires current contract` : "Unable to validate current contract"}: ${error.message}`,
78
- [contractRel],
118
+ [paths.contractRel],
79
119
  );
80
120
  }
81
121
  }
82
122
  try {
83
123
  route = await readPersistedRoute(target, packageRoot, { taskId: scopedTaskId, routePath: options.routePath });
84
124
  } catch (error) {
125
+ if (error.code === "E_STATE_REVISION_CONFLICT") throw error;
85
126
  if (error.code === "ARTIFACT_MISSING" && !requireRoute) {
86
127
  route = null;
87
128
  } else {
88
129
  throw phaseError(
89
130
  requireRoute ? "E_PHASE_PREREQUISITE_MISSING" : error.code ?? "E_ROUTE_INVALID",
90
131
  `${requireRoute ? `Phase ${toPhase} requires persisted route` : "Unable to validate persisted route"}: ${error.message}`,
91
- [routeRel],
132
+ [paths.routeRel],
92
133
  );
93
134
  }
94
135
  }
136
+ return { contract, route };
137
+ }
138
+
139
+ function assertRepairedCheckpointIdentity({ contract, route, state, events, contractRel, routeRel, stateRel }) {
140
+ const repairMarker = events.find((event) => event.event === CONTRACT_BOOTSTRAP_REPAIR_EVENT);
141
+ if (!repairMarker || state.phase === "ROUTED") return false;
142
+ const checkpoint = resolveCanonicalPostRepairCheckpointBinding(events, repairMarker);
143
+ if (!checkpoint) {
144
+ throw phaseError(
145
+ "E_PHASE_CHRONOLOGY_INVALID",
146
+ "Repaired late-phase state requires a canonical route checkpoint binding",
147
+ [contractRel, routeRel],
148
+ );
149
+ }
150
+ const identityErrors = [
151
+ ...stateIdentityErrors({ contract, state }),
152
+ ...stateIdentityErrors({ contract, route }),
153
+ ];
154
+ if (state.routeFingerprint !== checkpoint.routeFingerprint
155
+ || !sameCanonicalGuideList(state.selectedGuides, checkpoint.selectedGuides)) {
156
+ identityErrors.push({
157
+ code: "E_ROUTE_GUIDE_MISMATCH",
158
+ message: "Work state does not match the immutable route checkpoint identity",
159
+ artifacts: [routeRel, stateRel],
160
+ });
161
+ }
162
+ if (identityErrors.length > 0) {
163
+ const first = identityErrors[0];
164
+ throw phaseError(first.code, first.message, first.artifacts);
165
+ }
166
+ return true;
167
+ }
168
+
169
+ async function assertPersistedStateIdentity(target, state, toPhase, packageRoot, options = {}) {
170
+ const scopedTaskId = options.taskId ?? null;
171
+ const events = options.events ?? [];
172
+ const contractRel = options.contractPath ?? (scopedTaskId ? taskArtifactPath(scopedTaskId, "contract") : ARTIFACT_PATHS.contract);
173
+ const routeRel = options.routePath ?? (scopedTaskId ? taskArtifactPath(scopedTaskId, "route") : ARTIFACT_PATHS.route);
174
+ const stateRel = options.statePath ?? (scopedTaskId ? taskArtifactPath(scopedTaskId, "state") : ARTIFACT_PATHS.state);
175
+ const { contract, route } = await readPhaseIdentityArtifacts(
176
+ target,
177
+ state,
178
+ toPhase,
179
+ packageRoot,
180
+ options,
181
+ { contractRel, routeRel },
182
+ );
95
183
  if (!contract && !route) return;
184
+ if (toPhase !== "ROUTED" && assertRepairedCheckpointIdentity({
185
+ contract,
186
+ route,
187
+ state,
188
+ events,
189
+ contractRel,
190
+ routeRel,
191
+ stateRel,
192
+ })) {
193
+ return;
194
+ }
96
195
  try {
97
196
  assertStateIdentity({ contract, route, state });
98
197
  } catch (error) {
@@ -116,24 +215,29 @@ function reconcileImplementationStep(state, toPhase) {
116
215
  };
117
216
  }
118
217
 
218
+ function isRevisionPlanRepeat(events, eventType) {
219
+ return eventType === "PLAN_RECORDED"
220
+ && isRevisionEpochPlanRepeat(events, events.length, { event: eventType });
221
+ }
222
+
223
+ async function assertRequiredPhaseArtifact(read, toPhase, relativePath) {
224
+ try { await read(); }
225
+ catch (error) {
226
+ if (error.code === "E_STATE_REVISION_CONFLICT") throw error;
227
+ throw phaseError("E_PHASE_PREREQUISITE_MISSING", `Phase ${toPhase} requires ${relativePath}: ${error.message}`, [relativePath]);
228
+ }
229
+ }
230
+
119
231
  async function assertPhasePrerequisites(target, state, toPhase, packageRoot, authorityContext, runtimeContext, options = {}) {
120
232
  const scopedTaskId = options.taskId ?? null;
121
233
  const contractRel = options.contractPath ?? (scopedTaskId ? taskArtifactPath(scopedTaskId, "contract") : ARTIFACT_PATHS.contract);
122
234
  const routeRel = options.routePath ?? (scopedTaskId ? taskArtifactPath(scopedTaskId, "route") : ARTIFACT_PATHS.route);
123
235
 
124
236
  if (toPhase === "CONTRACT_READY" || toPhase === "ROUTED" || toPhase === "EXECUTING") {
125
- try {
126
- await readContract(target, packageRoot, { taskId: scopedTaskId, contractPath: options.contractPath });
127
- } catch (error) {
128
- throw phaseError("E_PHASE_PREREQUISITE_MISSING", `Phase ${toPhase} requires ${contractRel}: ${error.message}`, [contractRel]);
129
- }
237
+ await assertRequiredPhaseArtifact(() => readContract(target, packageRoot, { taskId: scopedTaskId, contractPath: options.contractPath }), toPhase, contractRel);
130
238
  }
131
239
  if (toPhase === "ROUTED" || toPhase === "EXECUTING") {
132
- try {
133
- await readPersistedRoute(target, packageRoot, { taskId: scopedTaskId, routePath: options.routePath });
134
- } catch (error) {
135
- throw phaseError("E_PHASE_PREREQUISITE_MISSING", `Phase ${toPhase} requires ${routeRel}: ${error.message}`, [routeRel]);
136
- }
240
+ await assertRequiredPhaseArtifact(() => readPersistedRoute(target, packageRoot, { taskId: scopedTaskId, routePath: options.routePath }), toPhase, routeRel);
137
241
  }
138
242
  if (toPhase === "EXECUTING" && scopedTaskId) {
139
243
  try {
@@ -146,6 +250,7 @@ async function assertPhasePrerequisites(target, state, toPhase, packageRoot, aut
146
250
  try {
147
251
  await assertExecutionPrerequisites({ target, state, packageRoot, ...options, taskId: scopedTaskId, runtimeContext });
148
252
  } catch (error) {
253
+ if (error.code === "E_STATE_REVISION_CONFLICT") throw error;
149
254
  throw phaseError(error.code, error.message, error.artifacts);
150
255
  }
151
256
  }
@@ -160,8 +265,106 @@ async function assertPhasePrerequisites(target, state, toPhase, packageRoot, aut
160
265
  }
161
266
  }
162
267
 
268
+ async function appendRepairRouteCheckpoint({ target, packageRoot, taskId, state, events, stateRel, eventsRel }) {
269
+ const repairMarker = events.find((event) => event.event === CONTRACT_BOOTSTRAP_REPAIR_EVENT);
270
+ const checkpointAlreadyBound = events.some((event) => event.event === ROUTE_CHECKPOINT_BOUND_EVENT);
271
+ if (!repairMarker || state.phase !== "ROUTED" || checkpointAlreadyBound) return;
272
+ if (typeof state.routeFingerprint !== "string") {
273
+ throw phaseError("E_ROUTE_GUIDE_MISMATCH", "ROUTED state requires a route fingerprint before checkpoint binding", [stateRel, eventsRel]);
274
+ }
275
+ await appendProtocolEvent(target, {
276
+ taskId: state.taskId,
277
+ event: ROUTE_CHECKPOINT_BOUND_EVENT,
278
+ details: {
279
+ routeFingerprint: state.routeFingerprint,
280
+ contractFingerprint: state.contractFingerprint,
281
+ selectedGuides: [...state.selectedGuides].sort(),
282
+ },
283
+ }, packageRoot, { taskId, eventsPath: eventsRel });
284
+ }
285
+
286
+ /** Shared receipt refresh used by phase advancement on either storage source. */
287
+ export async function preparePhaseReceipt({ target, packageRoot, taskId, state, next, receiptRel, contractPath = null, routePath = null, statePath = null, authorityContext, runtimeContext, readers = null }) {
288
+ let nextReceipt = null;
289
+ try {
290
+ const receipt = await (readers?.readReceipt ?? readJsonArtifact)(target, receiptRel, "execution-receipt", packageRoot);
291
+ const contract = await (readers?.readContract ?? readContract)(target, packageRoot, { taskId, contractPath });
292
+ const route = await (readers?.readRoute ?? readPersistedRoute)(target, packageRoot, { taskId, routePath });
293
+ const preflight = await evaluatePreflight({ target, packageRoot, taskId, contractPath, routePath, statePath, readers });
294
+ const requiredEvidence = await requiredEvidenceForTarget({
295
+ target,
296
+ contract,
297
+ route,
298
+ packageRoot,
299
+ additionalEvidence: preflight.policy?.requiredEvidence ?? [],
300
+ });
301
+ await validateReceipt(receipt.value, packageRoot, {
302
+ target,
303
+ taskId: contract?.value?.taskId,
304
+ authorityContext,
305
+ runtimeContext,
306
+ });
307
+ assertCompletionRelationships({
308
+ contract,
309
+ route,
310
+ state,
311
+ receipt: receipt.value,
312
+ requiredEvidence,
313
+ requireRequiredChecks: false,
314
+ requireReceiptStateFingerprint: false,
315
+ target,
316
+ taskId: contract?.value?.taskId,
317
+ authorityContext,
318
+ runtimeContext,
319
+ });
320
+ nextReceipt = await createReceipt({
321
+ ...receipt.value,
322
+ stateFingerprint: canonicalFingerprint(next),
323
+ verificationCycle: next.verificationCycle ?? receipt.value.verificationCycle ?? 1,
324
+ }, packageRoot, {
325
+ target,
326
+ taskId: contract?.value?.taskId,
327
+ authorityContext,
328
+ runtimeContext,
329
+ });
330
+ assertCompletionRelationships({
331
+ contract,
332
+ route,
333
+ state: next,
334
+ receipt: nextReceipt,
335
+ requiredEvidence,
336
+ requireRequiredChecks: false,
337
+ target,
338
+ taskId: contract?.value?.taskId,
339
+ authorityContext,
340
+ runtimeContext,
341
+ });
342
+ } catch (error) {
343
+ if (error.code !== "ARTIFACT_MISSING") throw error;
344
+ }
345
+ return nextReceipt;
346
+ }
347
+
163
348
  export async function advanceWorkState(target, toPhase, options = {}) {
164
- const normalizedOptions = typeof options === "string" ? { packageRoot: options } : options;
349
+ assertWorkPhase(toPhase);
350
+ let normalizedOptions = typeof options === "string" ? { packageRoot: options } : options;
351
+ if (!normalizedOptions.taskId) {
352
+ const state = await readWorkState(target, normalizedOptions);
353
+ normalizedOptions = { ...normalizedOptions, taskId: state?.taskId };
354
+ }
355
+ if (!normalizedOptions.taskId || await getTaskTransaction(target)) {
356
+ return advanceWorkStateInternal(target, toPhase, normalizedOptions);
357
+ }
358
+ return withTaskTransaction({
359
+ target,
360
+ taskId: normalizedOptions.taskId,
361
+ operation: "advance",
362
+ packageRoot: normalizedOptions.packageRoot,
363
+ recordCommitEvent: true,
364
+ }, async () => advanceWorkStateInternal(target, toPhase, normalizedOptions));
365
+ }
366
+
367
+ async function advanceWorkStateInternal(target, toPhase, normalizedOptions) {
165
368
  const {
166
369
  packageRoot,
167
370
  now = new Date().toISOString(),
@@ -174,8 +377,6 @@ export async function advanceWorkState(target, toPhase, options = {}) {
174
377
  receiptPath = null,
175
378
  eventsPath = null,
176
379
  } = normalizedOptions;
177
- assertWorkPhase(toPhase);
178
-
179
380
  let state = await readWorkState(target, { packageRoot, taskId, statePath });
180
381
  const stateRel = statePath ?? (taskId ? taskArtifactPath(taskId, "state") : ARTIFACT_PATHS.state);
181
382
  if (!state) throw phaseError("E_PHASE_PREREQUISITE_MISSING", "Cannot advance without work state", [stateRel]);
@@ -211,7 +412,6 @@ export async function advanceWorkState(target, toPhase, options = {}) {
211
412
  }
212
413
 
213
414
  await assertPhasePrerequisites(target, state, toPhase, packageRoot, authorityContext, runtimeContext, { taskId, statePath, contractPath, routePath, receiptPath, eventsPath });
214
- await assertPersistedStateIdentity(target, state, toPhase, packageRoot, { taskId, contractPath, routePath });
215
415
 
216
416
  if (toPhase === "EXECUTING" && taskId) {
217
417
  // Multi-task checkout scope checks
@@ -259,6 +459,12 @@ export async function advanceWorkState(target, toPhase, options = {}) {
259
459
  if (coherenceErrors.length > 0) {
260
460
  throw phaseError(coherenceErrors[0].code, coherenceErrors[0].message, [stateRel, eventsRel]);
261
461
  }
462
+ await assertPersistedStateIdentity(target, state, toPhase, packageRoot, {
463
+ taskId,
464
+ contractPath,
465
+ routePath,
466
+ events: ledger.events,
467
+ });
262
468
  const eventType = PHASE_EVENTS[toPhase];
263
469
  const reenteringVerification = toPhase === "VERIFYING" && ["CORRECTING", "REVIEWING"].includes(state.phase);
264
470
  if (toPhase === "VERIFYING" && state.phase === "REVIEWING") {
@@ -287,35 +493,35 @@ export async function advanceWorkState(target, toPhase, options = {}) {
287
493
  }
288
494
  }
289
495
  if (toPhase === "CORRECTING" && state.phase === "DIAGNOSING") {
290
- const cycle = state.verificationCycle ?? 1;
291
- const diagEvent = resolveCurrentCycleDiagnostic(ledger.events, state.taskId, cycle);
292
- if (!diagEvent) {
293
- throw phaseError(
294
- "E_DIAGNOSIS_REQUIRED",
295
- "DIAGNOSING -> CORRECTING requires an append-only diagnosis record for the active verification cycle",
296
- [eventsRel],
297
- );
496
+ // Shared rule owner: the same pure prerequisite used by the SQLite store
497
+ // advance, so the two backends cannot drift on this edge.
498
+ let cycle = state.verificationCycle ?? 1;
499
+ let diagEvent;
500
+ try {
501
+ diagEvent = assertDiagnosingToCorrectingTransition({
502
+ state,
503
+ events: ledger.events,
504
+ resolveDiagnosis: resolveCurrentCycleDiagnostic,
505
+ });
506
+ } catch (error) {
507
+ throw phaseError(error.code, error.message, [eventsRel]);
298
508
  }
299
- let stalledNoGain = false;
509
+ cycle = state.verificationCycle ?? 1;
300
510
  if (diagEvent.sourceModel === "STRUCTURED_DIAGNOSTIC_CASE_V1") {
301
- // One canonical structured-stall truth, shared with progress/reflect.
511
+ // The shared rule intentionally omits the structured-stall evaluation,
512
+ // which depends on progress/reflect projections. Re-check it here so the
513
+ // filesystem behavior is unchanged.
302
514
  const stall = evaluateStructuredDiagnosticStall(
303
515
  buildInformationGainProjection(ledger.events, state.taskId),
304
516
  { verificationCycle: cycle },
305
517
  );
306
- stalledNoGain = stall.stalled;
307
- }
308
- const gainClassification = diagEvent.sourceModel === "STRUCTURED_DIAGNOSTIC_CASE_V1"
309
- ? null
310
- : diagEvent.details?.informationGain;
311
- if (!diagEvent.details
312
- || stalledNoGain
313
- || (diagEvent.sourceModel !== "STRUCTURED_DIAGNOSTIC_CASE_V1" && gainClassification === "NONE")) {
314
- throw phaseError(
315
- "E_DIAGNOSIS_NO_NEW_INFORMATION",
316
- "The proposed retry repeats the previous hypothesis with the same evidence without new information",
317
- [eventsRel],
318
- );
518
+ if (stall.stalled) {
519
+ throw phaseError(
520
+ "E_DIAGNOSIS_NO_NEW_INFORMATION",
521
+ "The proposed retry repeats the previous hypothesis with the same evidence without new information",
522
+ [eventsRel],
523
+ );
524
+ }
319
525
  }
320
526
  }
321
527
  if (toPhase === "VERIFYING" && state.phase === "CORRECTING") {
@@ -338,11 +544,15 @@ export async function advanceWorkState(target, toPhase, options = {}) {
338
544
  }
339
545
  const repeatedReview = state.phase === "VERIFYING" && toPhase === "REVIEWING"
340
546
  && ledger.events.some((event) => event.taskId === state.taskId && event.event === "REVIEW_STARTED");
547
+ const revisionPlanRepeat = isRevisionPlanRepeat(ledger.events, eventType);
341
548
  if (eventType && !reenteringVerification && !repeatedReview
549
+ && !revisionPlanRepeat
342
550
  && ledger.events.some((event) => event.taskId === state.taskId && event.event === eventType)) {
343
551
  throw phaseError("E_PHASE_CHRONOLOGY_INVALID", `Lifecycle milestone already exists: ${eventType}`, [eventsRel]);
344
552
  }
345
- const milestoneIndex = reenteringVerification || repeatedReview ? -1 : LIFECYCLE_MILESTONES.indexOf(eventType);
553
+ const milestoneIndex = reenteringVerification || repeatedReview || revisionPlanRepeat
554
+ ? -1
555
+ : LIFECYCLE_MILESTONES.indexOf(eventType);
346
556
  if (milestoneIndex >= 0) {
347
557
  const lastMilestone = ledger.events.reduce((last, event) => Math.max(last, LIFECYCLE_MILESTONES.indexOf(event.event)), -1);
348
558
  if (lastMilestone !== milestoneIndex - 1) {
@@ -361,63 +571,16 @@ export async function advanceWorkState(target, toPhase, options = {}) {
361
571
  }
362
572
  if (reenteringVerification) delete next.lastCompletionAttempt;
363
573
  next.revision = (state.revision ?? 0) + 1;
364
- let nextReceipt = null;
365
- try {
366
- const receipt = await readJsonArtifact(target, receiptRel, "execution-receipt", packageRoot);
367
- const contract = await readContract(target, packageRoot, { taskId, contractPath });
368
- const route = await readPersistedRoute(target, packageRoot, { taskId, routePath });
369
- const preflight = await evaluatePreflight({ target, packageRoot, taskId, contractPath, routePath, statePath });
370
- const requiredEvidence = await requiredEvidenceForTarget({
371
- target,
372
- contract,
373
- route,
374
- packageRoot,
375
- additionalEvidence: preflight.policy?.requiredEvidence ?? [],
376
- });
377
- await validateReceipt(receipt.value, packageRoot, {
378
- target,
379
- taskId: contract?.value?.taskId,
380
- authorityContext,
381
- runtimeContext,
382
- });
383
- assertCompletionRelationships({
384
- contract,
385
- route,
386
- state,
387
- receipt: receipt.value,
388
- requiredEvidence,
389
- requireRequiredChecks: false,
390
- requireReceiptStateFingerprint: false,
391
- target,
392
- taskId: contract?.value?.taskId,
393
- authorityContext,
394
- runtimeContext,
395
- });
396
- nextReceipt = await createReceipt({
397
- ...receipt.value,
398
- stateFingerprint: canonicalFingerprint(next),
399
- verificationCycle: next.verificationCycle ?? receipt.value.verificationCycle ?? 1,
400
- }, packageRoot, {
401
- target,
402
- taskId: contract?.value?.taskId,
403
- authorityContext,
404
- runtimeContext,
405
- });
406
- assertCompletionRelationships({
407
- contract,
408
- route,
409
- state: next,
410
- receipt: nextReceipt,
411
- requiredEvidence,
412
- requireRequiredChecks: false,
413
- target,
414
- taskId: contract?.value?.taskId,
415
- authorityContext,
416
- runtimeContext,
417
- });
418
- } catch (error) {
419
- if (error.code !== "ARTIFACT_MISSING") throw error;
420
- }
574
+ await appendRepairRouteCheckpoint({
575
+ target,
576
+ packageRoot,
577
+ taskId,
578
+ state,
579
+ events: ledger.events,
580
+ stateRel,
581
+ eventsRel,
582
+ });
583
+ const nextReceipt = await preparePhaseReceipt({ target, packageRoot, taskId, state, next, receiptRel, contractPath, routePath, statePath, authorityContext, runtimeContext });
421
584
  await mutateWorkState(target, {
422
585
  expectedRevision: state.revision ?? 0,
423
586
  packageRoot,
@@ -1,10 +1,11 @@
1
+ import { withNativeReadScope } from "./native-storage.js";
1
2
  import { readFile } from "node:fs/promises";
2
3
  import path from "node:path";
3
4
  import { fileExists } from "./filesystem.js";
4
5
  import { PROJECT_ARTIFACT_PATHS, taskArtifactPath } from "./task-paths.js";
5
6
  import { assertJsonLimits } from "./json-safety.js";
6
7
  import { assertSchema, readSchema } from "./schema-validation.js";
7
- import { canonicalFingerprint, writeJsonArtifact } from "./artifacts.js";
8
+ import { canonicalFingerprint, readJsonArtifact, writeJsonArtifact } from "./artifacts.js";
8
9
  import { sha256 } from "./manifest.js";
9
10
  import { BUILTIN_POLICY_RULES, discoverPolicy } from "./policy-discovery.js";
10
11
  import { getPolicyAdapter } from "./policy-adapters.js";
@@ -82,24 +83,22 @@ export async function writePolicyLock(target, lock, packageRoot) {
82
83
  }
83
84
 
84
85
  export async function readTaskPolicySnapshot(target, taskId, packageRoot) {
85
- const relPath = taskArtifactPath(taskId, "policySnapshot");
86
- const fullPath = path.join(target, relPath);
87
- if (!(await fileExists(fullPath))) {
88
- return null;
89
- }
90
- const raw = await readFile(fullPath, "utf8");
91
- assertJsonLimits(raw, relPath);
92
- const parsed = JSON.parse(raw);
93
- const schema = await readSchema("policy-snapshot", packageRoot);
94
- assertSchema(parsed, schema, "policy-snapshot");
95
- return parsed;
86
+ return withNativeReadScope(target, async () => {
87
+ const relPath = taskArtifactPath(taskId, "policySnapshot");
88
+ try {
89
+ return (await readJsonArtifact(target, relPath, "policy-snapshot", packageRoot)).value;
90
+ } catch (error) {
91
+ if (error.code === "ARTIFACT_MISSING") return null;
92
+ throw error;
93
+ }
94
+ });
96
95
  }
97
96
 
98
97
  export async function writeTaskPolicySnapshot(target, taskId, snapshot, packageRoot) {
99
98
  const relPath = taskArtifactPath(taskId, "policySnapshot");
100
99
  const schema = await readSchema("policy-snapshot", packageRoot);
101
100
  assertSchema(snapshot, schema, "policy-snapshot");
102
- await writeJsonArtifact(target, relPath, snapshot, "policy-snapshot", packageRoot);
101
+ await writeJsonArtifact(target, relPath, snapshot, "policy-snapshot", packageRoot, { taskId });
103
102
  return snapshot;
104
103
  }
105
104