@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
@@ -0,0 +1,44 @@
1
+ import { lstat } from "node:fs/promises";
2
+ import { assertSafePath } from "../core/filesystem.js";
3
+ import { readMaintenanceOwner } from "../storage/maintenance-owner.js";
4
+
5
+ async function present(target, relativePath) {
6
+ try { await lstat(await assertSafePath(target, relativePath)); return true; }
7
+ catch (error) { if (error.code === "ENOENT") return false; throw error; }
8
+ }
9
+
10
+ async function inspectOwner(target) {
11
+ const marker = await assertSafePath(target, ".forgeloop/.storage-maintenance");
12
+ if (!(await lstat(marker)).isDirectory()) return { status: "MALFORMED", owner: null };
13
+ try {
14
+ const { value } = await readMaintenanceOwner(target);
15
+ return {
16
+ status: "RETAINED",
17
+ owner: { ownerId: value.ownerId, pid: value.pid, acquiredAt: value.acquiredAt, ...(value.hostname ? { hostname: value.hostname } : {}) },
18
+ ownerLiveness: "NOT_VERIFIED",
19
+ handoffPresent: await present(target, ".forgeloop/.storage-maintenance/handoff")
20
+ || await present(target, `.forgeloop/storage-maintenance-history/handoffs/${value.ownerId}.json`),
21
+ };
22
+ } catch (error) {
23
+ if (error.code === "ENOENT") return { status: "INCOMPLETE", owner: null };
24
+ if (error.code === "E_STORAGE_MAINTENANCE_OWNER_INVALID") return { status: "MALFORMED", owner: null };
25
+ throw error;
26
+ }
27
+ }
28
+
29
+ /** Inspect bootstrap layout even when ordinary database dispatch is excluded. */
30
+ export async function runStorageMigrationStatus({ target }) {
31
+ const databasePresent = await present(target, ".forgeloop/state.sqlite");
32
+ const legacyOperationalPaths = [];
33
+ for (const relativePath of [".forgeloop/task-state", ".forgeloop/work-state.json", ".forgeloop/events.ndjson", ".forgeloop/.txn"]) {
34
+ if (await present(target, relativePath)) legacyOperationalPaths.push(relativePath);
35
+ }
36
+ const maintenance = await present(target, ".forgeloop/.storage-maintenance")
37
+ ? await inspectOwner(target) : { status: "NONE", owner: null };
38
+ return {
39
+ schemaVersion: 1,
40
+ layout: databasePresent ? legacyOperationalPaths.length ? "MIXED" : "SQLITE_PRESENT" : "FILESYSTEM",
41
+ databasePresent, legacyOperationalPaths, maintenance,
42
+ databaseValidation: "NOT_RUN", publicationValidation: "NOT_VERIFIED",
43
+ };
44
+ }
@@ -0,0 +1,7 @@
1
+ import { resumeProjectStorageRestore } from "../storage/project-restore.js";
2
+ import { resumeActiveProjectReplacement } from "../storage/project-replacement.js";
3
+
4
+ export async function runStorageRestoreResume({ target, operationId, expectedOwnerId, writersQuiesced = false, replaceActive = false, packageRoot } = {}) {
5
+ const resume = replaceActive === true ? resumeActiveProjectReplacement : resumeProjectStorageRestore;
6
+ return resume(target, { operationId, expectedOwnerId, writersQuiesced, packageRoot });
7
+ }
@@ -0,0 +1,8 @@
1
+ import path from "node:path";
2
+ import { restoreProjectStorageToFreshProject } from "../storage/project-restore.js";
3
+ import { replaceActiveProjectStorage } from "../storage/project-replacement.js";
4
+
5
+ export async function runStorageRestore({ target, source, writersQuiesced = false, replaceActive = false, packageRoot } = {}) {
6
+ const restore = replaceActive === true ? replaceActiveProjectStorage : restoreProjectStorageToFreshProject;
7
+ return restore(path.resolve(target, source), target, { writersQuiesced, packageRoot });
8
+ }
@@ -0,0 +1,224 @@
1
+ import { randomUUID } from "node:crypto";
2
+
3
+ import { resolveTaskContext } from "../core/task-context.js";
4
+ import { inspectTaskConflictState } from "../core/task-conflict-inspection.js";
5
+ import { appendProtocolEvent } from "../core/events.js";
6
+ import { withTaskTransaction } from "../core/transaction.js";
7
+ import { currentRepositoryFingerprint } from "../core/repository.js";
8
+ import { withProjectClaimsLock } from "../core/task-lock.js";
9
+ import { readTaskDescriptor } from "../core/task-descriptor.js";
10
+ import { createTaskRecovery, writeTaskRecovery } from "../core/task-recovery.js";
11
+ import { resolveTaskClaimState } from "../core/task-claim-state.js";
12
+ import {
13
+ E_TASK_ABANDON_AUTHORIZATION_REQUIRED,
14
+ E_TASK_ABANDON_INCONSISTENT,
15
+ E_TASK_ABANDON_INVALID_STATE,
16
+ E_TASK_ABANDON_UNSAFE,
17
+ E_TASK_ALREADY_ABANDONED,
18
+ } from "../core/error-codes.js";
19
+
20
+ const ABANDONMENT_REASON_CODES = Object.freeze(["CALLER_ABANDONED"]);
21
+
22
+ function abandonError(code, message, details = {}) {
23
+ const error = new Error(message);
24
+ error.code = code;
25
+ Object.assign(error, details);
26
+ return error;
27
+ }
28
+
29
+ function sameList(left, right) {
30
+ return Array.isArray(left)
31
+ && Array.isArray(right)
32
+ && left.length === right.length
33
+ && left.every((value, index) => value === right[index]);
34
+ }
35
+
36
+ function snapshot(inspection) {
37
+ return {
38
+ phase: inspection.evidence.phase,
39
+ revision: inspection.evidence.workStateRevision,
40
+ ledgerLastSeq: inspection.evidence.ledgerLastSeq,
41
+ claimState: inspection.evidence.claimState,
42
+ effectiveWriteClaims: [...inspection.evidence.effectiveWriteClaims],
43
+ };
44
+ }
45
+
46
+ function assertSnapshotUnchanged(before, after, taskId) {
47
+ const unchanged = before.phase === after.phase
48
+ && before.revision === after.revision
49
+ && before.ledgerLastSeq === after.ledgerLastSeq
50
+ && before.claimState === after.claimState
51
+ && sameList(before.effectiveWriteClaims, after.effectiveWriteClaims);
52
+ if (!unchanged) {
53
+ throw abandonError(
54
+ E_TASK_ABANDON_UNSAFE,
55
+ `Task ${taskId} changed during abandonment precondition validation`,
56
+ { before, after },
57
+ );
58
+ }
59
+ }
60
+
61
+ async function assertAbandonableOwnership(target, taskId, packageRoot) {
62
+ const projection = await resolveTaskClaimState(target, { taskId, packageRoot });
63
+ if (projection.phase === "COMPLETE" || projection.claimState === "RELEASED_BY_COMPLETION") {
64
+ throw abandonError(E_TASK_ABANDON_INVALID_STATE, `Task ${taskId} is COMPLETE and cannot be abandoned`);
65
+ }
66
+ if (!projection.valid || !projection.ownershipValid) {
67
+ throw abandonError(
68
+ E_TASK_ABANDON_INCONSISTENT,
69
+ `Task ${taskId} claim ownership is inconsistent; abandonment is blocked`,
70
+ { reasonCodes: projection.reasonCodes, ownershipErrors: projection.ownershipErrors },
71
+ );
72
+ }
73
+ if (projection.claimState === "RELEASED_BY_RECOVERY") {
74
+ throw abandonError(
75
+ E_TASK_ALREADY_ABANDONED,
76
+ `Task ${taskId} is already abandoned/recovered`,
77
+ { recovery: projection.recovery },
78
+ );
79
+ }
80
+ if (projection.claimState !== "ACTIVE" || projection.mutationAllowed !== true) {
81
+ throw abandonError(E_TASK_ABANDON_INVALID_STATE, `Task ${taskId} is not an active mutable task`);
82
+ }
83
+ return projection;
84
+ }
85
+
86
+ function assertInspectionAbandonable(taskId, inspection) {
87
+ if (!inspection.evidence.healthy || !inspection.evidence.ledgerValid || !inspection.evidence.ownershipValid) {
88
+ throw abandonError(
89
+ E_TASK_ABANDON_INCONSISTENT,
90
+ `Task ${taskId} cannot be abandoned because canonical ownership or ledger validation failed`,
91
+ { reasonCodes: inspection.reasonCodes, ledgerErrors: inspection.evidence.ledgerErrors },
92
+ );
93
+ }
94
+ if (!inspection.evidence.phase || inspection.evidence.phase === "COMPLETE") {
95
+ throw abandonError(E_TASK_ABANDON_INVALID_STATE, `Task ${taskId} is not eligible for abandonment`);
96
+ }
97
+ if (inspection.evidence.claimState !== "ACTIVE" || inspection.evidence.mutationAllowed !== true) {
98
+ throw abandonError(E_TASK_ABANDON_INVALID_STATE, `Task ${taskId} is not an active mutable task`);
99
+ }
100
+ }
101
+
102
+ export async function runTaskAbandon({
103
+ target,
104
+ packageRoot,
105
+ taskId,
106
+ acknowledgeAbandonment = false,
107
+ } = {}) {
108
+ if (typeof taskId !== "string" || taskId.trim() === "") {
109
+ throw abandonError(E_TASK_ABANDON_INVALID_STATE, "task-abandon requires an explicit --task ID");
110
+ }
111
+ if (!acknowledgeAbandonment) {
112
+ throw abandonError(
113
+ E_TASK_ABANDON_AUTHORIZATION_REQUIRED,
114
+ "task-abandon requires explicit caller acknowledgement: re-run with --acknowledge-abandonment",
115
+ );
116
+ }
117
+
118
+ let context;
119
+ try {
120
+ context = await resolveTaskContext(target, { taskId, packageRoot, explicitRequired: true });
121
+ } catch (error) {
122
+ throw abandonError(
123
+ E_TASK_ABANDON_INCONSISTENT,
124
+ `Task ${taskId} cannot be abandoned because its canonical identity is unreadable`,
125
+ { causeCode: error.code, causeMessage: error.message },
126
+ );
127
+ }
128
+ const effectiveTaskId = context.taskId;
129
+ await assertAbandonableOwnership(target, effectiveTaskId, packageRoot);
130
+
131
+ return withProjectClaimsLock(target, "task-abandon", async () => {
132
+ await assertAbandonableOwnership(target, effectiveTaskId, packageRoot);
133
+ const inspectionBeforeLock = await inspectTaskConflictState(target, {
134
+ taskId: effectiveTaskId,
135
+ packageRoot,
136
+ });
137
+ assertInspectionAbandonable(effectiveTaskId, inspectionBeforeLock);
138
+ const before = snapshot(inspectionBeforeLock);
139
+
140
+ return withTaskTransaction({
141
+ target,
142
+ taskId: effectiveTaskId,
143
+ operation: "task-abandon",
144
+ packageRoot,
145
+ recordCommitEvent: true,
146
+ }, async (transaction) => {
147
+ const descriptor = await readTaskDescriptor(target, context.taskKey, packageRoot);
148
+ if (descriptor.value.taskId !== effectiveTaskId || descriptor.value.taskKey !== context.taskKey) {
149
+ throw abandonError(E_TASK_ABANDON_UNSAFE, `Task ${effectiveTaskId} identity changed during abandonment`);
150
+ }
151
+
152
+ await assertAbandonableOwnership(target, effectiveTaskId, packageRoot);
153
+ const inspection = await inspectTaskConflictState(target, {
154
+ taskId: effectiveTaskId,
155
+ packageRoot,
156
+ ignoredLockId: transaction.lock?.lockId ?? null,
157
+ });
158
+ assertInspectionAbandonable(effectiveTaskId, inspection);
159
+ assertSnapshotUnchanged(before, snapshot(inspection), effectiveTaskId);
160
+
161
+ const repository = await currentRepositoryFingerprint(target);
162
+ const recoveryId = `recovery-${randomUUID()}`;
163
+ const abandonedAt = new Date().toISOString();
164
+ const releasedClaims = [...inspection.evidence.effectiveWriteClaims];
165
+ const details = {
166
+ recoveryId,
167
+ classification: "ABANDONED",
168
+ reasonCodes: [...ABANDONMENT_REASON_CODES],
169
+ previousPhase: inspection.evidence.phase,
170
+ previousRevision: inspection.evidence.workStateRevision,
171
+ previousHead: inspection.evidence.repositoryHead,
172
+ previousBranch: inspection.evidence.repositoryBranch,
173
+ currentHead: repository.head,
174
+ currentBranch: repository.branch,
175
+ releasedClaims,
176
+ authorityKind: "CALLER_ACKNOWLEDGED",
177
+ };
178
+
179
+ const abandonmentEvent = await appendProtocolEvent(target, {
180
+ taskId: effectiveTaskId,
181
+ event: "TASK_ABANDONED",
182
+ at: abandonedAt,
183
+ details,
184
+ }, packageRoot, { taskId: effectiveTaskId });
185
+
186
+ await writeTaskRecovery(target, createTaskRecovery({
187
+ taskId: effectiveTaskId,
188
+ recoveredAt: abandonedAt,
189
+ recoveryId,
190
+ recoveryEventSeq: abandonmentEvent.seq,
191
+ classificationAtRecovery: "ABANDONED",
192
+ reasonCodes: ABANDONMENT_REASON_CODES,
193
+ releasedClaims,
194
+ previousPhase: inspection.evidence.phase,
195
+ previousRevision: inspection.evidence.workStateRevision,
196
+ repositoryFingerprint: repository,
197
+ authority: { kind: "CALLER_ACKNOWLEDGED" },
198
+ }), packageRoot);
199
+
200
+ return {
201
+ taskId: effectiveTaskId,
202
+ taskKey: context.taskKey,
203
+ abandoned: true,
204
+ recovered: true,
205
+ recoveryId,
206
+ classification: "ABANDONED",
207
+ reasonCodes: [...ABANDONMENT_REASON_CODES],
208
+ phase: inspection.evidence.phase,
209
+ claimsReleased: true,
210
+ releasedClaims,
211
+ effectiveWriteClaims: [],
212
+ claimState: "RELEASED_BY_RECOVERY",
213
+ mutationAllowed: false,
214
+ authority: { kind: "CALLER_ACKNOWLEDGED" },
215
+ message: `Task ${effectiveTaskId} explicitly abandoned by caller acknowledgement; write claims released without completion claim`,
216
+ };
217
+ });
218
+ });
219
+ }
220
+
221
+ export function formatTaskAbandonResult(result) {
222
+ const claims = result.releasedClaims.length === 0 ? "none" : result.releasedClaims.join(", ");
223
+ return `${result.message}\nclassification: ${result.classification}\nphase: ${result.phase} (unchanged)\nreleased claims: ${claims}\n`;
224
+ }
@@ -1,60 +1,25 @@
1
- import { discoverTasks } from "../core/task-discovery.js";
1
+ import { discoverTaskListPage } from "../core/task-discovery.js";
2
2
  import { WORK_PHASES } from "../core/protocol.js";
3
3
 
4
4
  export async function runTaskList({ target, packageRoot, phase = null, active = false, limit = null, offset = 0 } = {}) {
5
- const tasks = await discoverTasks(target, packageRoot);
6
5
  const normalizedPhase = typeof phase === "string" && phase.trim() !== "" ? phase.trim().toUpperCase() : null;
6
+ const start = Number.isInteger(offset) && offset >= 0 ? offset : 0;
7
+ const pageLimit = Number.isInteger(limit) && limit >= 0 ? limit : null;
8
+ const { tasks: projected, total } = await discoverTaskListPage(target, packageRoot, { phase: normalizedPhase, active, offset: start, limit: pageLimit });
7
9
  if (normalizedPhase && !WORK_PHASES.includes(normalizedPhase)) {
8
10
  const error = new Error(`Unknown task phase filter: ${phase}`);
9
11
  error.code = "E_TASK_PHASE_INVALID";
10
12
  throw error;
11
13
  }
12
- const phaseFiltered = normalizedPhase ? tasks.filter((task) => task.phase === normalizedPhase) : tasks;
13
- const filtered = active
14
- ? phaseFiltered.filter((task) => task.claimState === "ACTIVE" && task.mutationAllowed === true)
15
- : phaseFiltered;
16
- const start = Number.isInteger(offset) && offset >= 0 ? offset : 0;
17
- const end = Number.isInteger(limit) && limit >= 0 ? start + limit : undefined;
18
- const projected = filtered.slice(start, end);
14
+ const end = pageLimit === null ? undefined : start + pageLimit;
19
15
  return {
20
- tasks: projected.map((task) => {
21
- if (task.healthy === false) {
22
- return {
23
- taskId: task.taskId ?? null,
24
- taskKey: task.taskKey,
25
- directory: task.directory,
26
- healthy: false,
27
- error: task.error,
28
- };
29
- }
30
- return {
31
- taskId: task.taskId,
32
- taskKey: task.taskKey,
33
- directory: task.directory,
34
- healthy: true,
35
- phase: task.phase,
36
- writeClaims: task.writeClaims ?? [],
37
- historicalWriteClaims: task.historicalWriteClaims ?? [],
38
- effectiveWriteClaims: task.effectiveWriteClaims ?? [],
39
- claimState: task.claimState,
40
- recovery: task.recovery,
41
- mutationAllowed: task.mutationAllowed,
42
- ownershipValid: task.ownershipValid,
43
- ownershipErrors: task.ownershipErrors ?? task.errors ?? [],
44
- reasonCodes: task.reasonCodes ?? [],
45
- locked: task.locked,
46
- hasContinuity: task.hasContinuity,
47
- hasReceipt: task.hasReceipt,
48
- createdAt: task.createdAt,
49
- updatedAt: task.updatedAt,
50
- };
51
- }),
16
+ tasks: projected,
52
17
  ...(normalizedPhase ? { phase: normalizedPhase } : {}),
53
18
  ...(active ? { active: true } : {}),
54
19
  offset: start,
55
20
  ...(Number.isInteger(limit) && limit >= 0 ? { limit } : {}),
56
- total: filtered.length,
57
- hasMore: end !== undefined ? end < filtered.length : false,
21
+ total,
22
+ hasMore: end !== undefined ? end < total : false,
58
23
  };
59
24
  }
60
25
 
@@ -0,0 +1,288 @@
1
+ import { canonicalFingerprint } from "../core/artifacts.js";
2
+ import { readContract } from "../core/contract.js";
3
+ import { appendProtocolEvent, validateEventLedger, validateStateLedgerCoherence } from "../core/events.js";
4
+ import { readPersistedRoute } from "../core/route-artifact.js";
5
+ import { resolveTaskContext } from "../core/task-context.js";
6
+ import { taskArtifactPath } from "../core/task-paths.js";
7
+ import {
8
+ classifyLockStaleness,
9
+ readLockInfo,
10
+ releaseStaleTaskLockIfUnchanged,
11
+ withProjectClaimsLock,
12
+ } from "../core/task-lock.js";
13
+ import { withTaskTransaction } from "../core/transaction.js";
14
+ import { resolveTaskClaimState } from "../core/task-claim-state.js";
15
+ import { readWorkState } from "../core/work-state.js";
16
+ import {
17
+ CONTRACT_BOOTSTRAP_REPAIR_AUTHORITY,
18
+ CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_DEFECT,
19
+ CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_EVENT,
20
+ CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_VERSION,
21
+ assertContractBootstrapRepairMigrationDetails,
22
+ isLegacyContractBootstrapRepairMigrationCandidate,
23
+ resolveEffectiveContractBootstrapRepairAnchor,
24
+ } from "../core/contract-bootstrap-recovery.js";
25
+ import {
26
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_AUTHORIZATION_REQUIRED,
27
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
28
+ E_TASK_LOCKED,
29
+ } from "../core/error-codes.js";
30
+
31
+ function migrationError(code, message, details = {}) {
32
+ const error = new Error(message);
33
+ error.code = code;
34
+ Object.assign(error, details);
35
+ return error;
36
+ }
37
+
38
+ function routePath(taskId) {
39
+ return taskArtifactPath(taskId, "route");
40
+ }
41
+
42
+ async function proveLegacyRepairState(target, packageRoot, taskId, marker) {
43
+ const contract = await readContract(target, packageRoot, { taskId });
44
+ if (contract.fingerprint !== marker.details.contractFingerprint) {
45
+ throw migrationError(
46
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
47
+ "Current contract does not match the legacy repair marker fingerprint",
48
+ { artifacts: [taskArtifactPath(taskId, "contract")] },
49
+ );
50
+ }
51
+
52
+ const state = await readWorkState(target, { packageRoot, taskId });
53
+ if (!state) {
54
+ throw migrationError(
55
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
56
+ "Current work-state is required to prove the legacy repair boundary",
57
+ { artifacts: [taskArtifactPath(taskId, "state")] },
58
+ );
59
+ }
60
+ if (state.taskId !== taskId
61
+ || state.phase !== marker.details.reconstructedPhase
62
+ || state.contractFingerprint !== marker.details.contractFingerprint
63
+ || canonicalFingerprint(state) !== marker.details.reconstructedStateFingerprint) {
64
+ throw migrationError(
65
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
66
+ "Current work-state does not exactly match the legacy repair marker reconstruction",
67
+ { artifacts: [taskArtifactPath(taskId, "state")] },
68
+ );
69
+ }
70
+ if (!Number.isInteger(state.revision) || state.revision < 0) {
71
+ throw migrationError(
72
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
73
+ "Current work-state revision is not a valid non-negative integer",
74
+ { artifacts: [taskArtifactPath(taskId, "state")] },
75
+ );
76
+ }
77
+
78
+ let route = null;
79
+ if (state.phase === "ROUTED") {
80
+ try {
81
+ route = await readPersistedRoute(target, packageRoot, { taskId });
82
+ } catch (error) {
83
+ throw migrationError(
84
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
85
+ `ROUTED legacy repair requires a readable persisted route: ${error.message}`,
86
+ { artifacts: [routePath(taskId)] },
87
+ );
88
+ }
89
+ if (marker.details.routeFingerprint === null
90
+ || state.routeFingerprint !== marker.details.routeFingerprint
91
+ || route.fingerprint !== marker.details.routeFingerprint
92
+ || route.value.contractFingerprint !== marker.details.contractFingerprint) {
93
+ throw migrationError(
94
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
95
+ "Persisted route does not exactly match the legacy repair marker reconstruction",
96
+ { artifacts: [routePath(taskId)] },
97
+ );
98
+ }
99
+ } else if (marker.details.routeFingerprint !== null || state.routeFingerprint !== undefined) {
100
+ throw migrationError(
101
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
102
+ "CONTRACT_READY legacy repair must not claim a route",
103
+ { artifacts: [taskArtifactPath(taskId, "state")] },
104
+ );
105
+ }
106
+
107
+ return { contract, state, route };
108
+ }
109
+
110
+ function migrationResult(taskId, candidate, migrationEvent, alreadyMigrated) {
111
+ return {
112
+ taskId,
113
+ migrated: !alreadyMigrated,
114
+ alreadyMigrated,
115
+ legacyMarkerSeq: candidate.marker.seq,
116
+ legacyRepairCommitSeq: candidate.repairCommit.seq,
117
+ migrationEventSeq: migrationEvent?.seq ?? null,
118
+ repairId: candidate.marker.details.repairId,
119
+ reconstructedPhase: candidate.marker.details.reconstructedPhase,
120
+ };
121
+ }
122
+
123
+ async function inspectAlreadyMigrated(target, packageRoot, taskId, ledger) {
124
+ const anchor = resolveEffectiveContractBootstrapRepairAnchor(ledger.events);
125
+ if (!ledger.valid || !anchor || anchor.kind !== "MIGRATED_LEGACY") {
126
+ throw migrationError(
127
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
128
+ "Existing contract bootstrap migration is invalid or the ledger is not strict",
129
+ );
130
+ }
131
+ const ownership = await resolveTaskClaimState(target, { taskId, packageRoot });
132
+ if (!ownership.ownershipValid) {
133
+ throw migrationError(
134
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
135
+ "Current task state no longer satisfies the migrated task ownership invariants",
136
+ { errors: ownership.errors },
137
+ );
138
+ }
139
+ return migrationResult(taskId, { marker: anchor.sourceMarker, repairCommit: ledger.events[anchor.sourceMarker.seq] }, anchor.migrationEvent, true);
140
+ }
141
+
142
+ export async function runTaskMigrateContractBootstrapRepair({ target, packageRoot, taskId, acknowledgeMigration } = {}) {
143
+ if (!acknowledgeMigration) {
144
+ throw migrationError(
145
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_AUTHORIZATION_REQUIRED,
146
+ "task-migrate-contract-bootstrap-repair requires fresh explicit --acknowledge-migration",
147
+ );
148
+ }
149
+
150
+ const context = await resolveTaskContext(target, { taskId, packageRoot, explicitRequired: true });
151
+ const effectiveTaskId = context.taskId;
152
+ const initial = await validateEventLedger(target, packageRoot, {
153
+ taskId: effectiveTaskId,
154
+ allowUnmigratedLegacyContractBootstrapRepairMarkers: true,
155
+ });
156
+ if (!initial.valid) {
157
+ const strict = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
158
+ if (strict.valid) return inspectAlreadyMigrated(target, packageRoot, effectiveTaskId, strict);
159
+ throw migrationError(
160
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
161
+ "Ledger is not structurally trustworthy for legacy contract bootstrap migration",
162
+ { errors: initial.errors },
163
+ );
164
+ }
165
+ const initialCandidate = isLegacyContractBootstrapRepairMigrationCandidate(initial.events, effectiveTaskId);
166
+ if (!initialCandidate) {
167
+ const strict = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
168
+ if (strict.valid) return inspectAlreadyMigrated(target, packageRoot, effectiveTaskId, strict);
169
+ throw migrationError(
170
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
171
+ "Ledger does not match the exact legacy contract bootstrap repair migration boundary",
172
+ { errors: initial.errors },
173
+ );
174
+ }
175
+
176
+ return withProjectClaimsLock(target, "task-migrate-contract-bootstrap-repair", async () => {
177
+ const lock = await readLockInfo(target, effectiveTaskId);
178
+ const status = classifyLockStaleness(lock);
179
+ if (status.status === "LIVE") throw migrationError(E_TASK_LOCKED, `Task ${effectiveTaskId} has a live mutation lock`);
180
+ if (status.status === "UNKNOWN" || status.status === "CORRUPT") {
181
+ throw migrationError(
182
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
183
+ `Task ${effectiveTaskId} lock state is ${status.status}; refusing unsafe migration`,
184
+ );
185
+ }
186
+ if (status.status === "STALE") {
187
+ const released = await releaseStaleTaskLockIfUnchanged(target, effectiveTaskId, lock);
188
+ if (!released.released && released.reason !== "LOCK_MISSING") {
189
+ throw migrationError(
190
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
191
+ `Task ${effectiveTaskId} stale lock could not be safely released: ${released.reason}`,
192
+ );
193
+ }
194
+ }
195
+
196
+ const result = await withTaskTransaction({
197
+ target,
198
+ taskId: effectiveTaskId,
199
+ operation: "task-migrate-contract-bootstrap-repair",
200
+ packageRoot,
201
+ recordCommitEvent: true,
202
+ }, async () => {
203
+ const locked = await validateEventLedger(target, packageRoot, {
204
+ taskId: effectiveTaskId,
205
+ allowUnmigratedLegacyContractBootstrapRepairMarkers: true,
206
+ });
207
+ if (!locked.valid) {
208
+ const strict = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
209
+ if (strict.valid) return inspectAlreadyMigrated(target, packageRoot, effectiveTaskId, strict);
210
+ throw migrationError(
211
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
212
+ "Pre-commit ledger revalidation failed inside the migration transaction",
213
+ { errors: locked.errors },
214
+ );
215
+ }
216
+ const candidate = isLegacyContractBootstrapRepairMigrationCandidate(locked.events, effectiveTaskId);
217
+ if (!candidate
218
+ || candidate.marker.seq !== initialCandidate.marker.seq
219
+ || candidate.marker.hash !== initialCandidate.marker.hash
220
+ || candidate.repairCommit.hash !== initialCandidate.repairCommit.hash) {
221
+ const strict = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
222
+ if (strict.valid) return inspectAlreadyMigrated(target, packageRoot, effectiveTaskId, strict);
223
+ throw migrationError(
224
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
225
+ "Ledger changed between inspection and migration lock; aborting",
226
+ );
227
+ }
228
+
229
+ const proof = await proveLegacyRepairState(target, packageRoot, effectiveTaskId, candidate.marker);
230
+ const coherenceErrors = validateStateLedgerCoherence(proof.state, locked.events);
231
+ if (coherenceErrors.length > 0) {
232
+ throw migrationError(
233
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
234
+ "Work-state and ledger are not coherent at the legacy repair boundary",
235
+ { errors: coherenceErrors },
236
+ );
237
+ }
238
+ const migratedAt = new Date().toISOString();
239
+ const details = {
240
+ migrationVersion: CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_VERSION,
241
+ defect: CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_DEFECT,
242
+ legacyMarkerSeq: candidate.marker.seq,
243
+ legacyMarkerHash: candidate.marker.hash,
244
+ legacyRepairId: candidate.marker.details.repairId,
245
+ legacyRepairCommitSeq: candidate.repairCommit.seq,
246
+ legacyRepairCommitHash: candidate.repairCommit.hash,
247
+ legacyRepairTransactionId: candidate.repairCommit.details.transactionId,
248
+ taskId: effectiveTaskId,
249
+ contractFingerprint: candidate.marker.details.contractFingerprint,
250
+ reconstructedPhase: candidate.marker.details.reconstructedPhase,
251
+ routeFingerprint: candidate.marker.details.routeFingerprint,
252
+ reconstructedStateFingerprint: candidate.marker.details.reconstructedStateFingerprint,
253
+ reconstructedStateRevision: proof.state.revision,
254
+ migratedAt,
255
+ authorityKind: CONTRACT_BOOTSTRAP_REPAIR_AUTHORITY,
256
+ };
257
+ assertContractBootstrapRepairMigrationDetails(details);
258
+ const migrationEvent = await appendProtocolEvent(target, {
259
+ taskId: effectiveTaskId,
260
+ event: CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_EVENT,
261
+ at: migratedAt,
262
+ details,
263
+ }, packageRoot, { taskId: effectiveTaskId });
264
+ return migrationResult(effectiveTaskId, candidate, migrationEvent, false);
265
+ });
266
+
267
+ const finalLedger = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
268
+ if (!finalLedger.valid) {
269
+ throw migrationError(
270
+ E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_INVALID,
271
+ "Post-migration ledger validation failed; the legacy repair remains unverified",
272
+ { errors: finalLedger.errors },
273
+ );
274
+ }
275
+ return result;
276
+ });
277
+ }
278
+
279
+ export function formatTaskMigrateContractBootstrapRepairResult(result) {
280
+ return [
281
+ `taskId: ${result.taskId}`,
282
+ `migration: ${result.alreadyMigrated ? "already migrated" : "migrated"}`,
283
+ `legacy marker seq: ${result.legacyMarkerSeq}`,
284
+ `migration event seq: ${result.migrationEventSeq}`,
285
+ `repairId: ${result.repairId}`,
286
+ "",
287
+ ].join("\n");
288
+ }
@@ -1,7 +1,7 @@
1
- import { migrateLegacyLayout } from "../core/task-migration.js";
1
+ import { migrateLegacyTaskStorage } from "../core/task-storage-migration.js";
2
2
 
3
- export async function runTaskMigrate({ target, packageRoot, dryRun = false } = {}) {
4
- const result = await migrateLegacyLayout(target, { packageRoot, dryRun });
3
+ export async function runTaskMigrate({ target, packageRoot, dryRun = false, destination, writersQuiesced = false } = {}) {
4
+ const result = await migrateLegacyTaskStorage(target, { packageRoot, dryRun, destination, writersQuiesced });
5
5
  return result;
6
6
  }
7
7