@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
@@ -1,16 +1,11 @@
1
- import { cp, mkdir, rename, rm } from "node:fs/promises";
2
- import path from "node:path";
3
- import { ensureWithin, fileExists, writeFileAtomic } from "./filesystem.js";
1
+ import { ensureWithin, fileExists } from "./filesystem.js";
4
2
  import { getPackageRoot } from "./templates.js";
5
3
  import {
6
4
  LEGACY_TASK_ARTIFACT_PATHS,
7
- TASK_STATE_ROOT,
8
- taskArtifactPath,
9
5
  taskDirectory,
10
6
  } from "./task-paths.js";
11
7
  import { assertTaskId, taskStorageKey } from "./task-identity.js";
12
- import { createTaskDescriptor, readTaskDescriptor, writeTaskDescriptor } from "./task-descriptor.js";
13
- import { readJsonArtifact } from "./artifacts.js";
8
+ import { readPortableJsonArtifact } from "./artifacts.js";
14
9
  import { validateMigrationSnapshot } from "./task-migration-validation.js";
15
10
  import {
16
11
  E_TASK_MIGRATION_IDENTITY_MISMATCH,
@@ -31,51 +26,11 @@ export async function detectLegacySingletonLayout(target) {
31
26
  };
32
27
  }
33
28
 
34
- async function removeLegacyArtifact(target, relativePath) {
35
- const fullPath = ensureWithin(target, relativePath);
36
- await rm(fullPath, { recursive: true, force: true });
37
- if (await fileExists(fullPath)) {
38
- const error = new Error(
39
- `Legacy artifact still exists after migration cleanup: ${relativePath}`,
40
- );
41
- error.code = E_TASK_MIGRATION_INVALID;
42
- throw error;
43
- }
44
- }
45
-
46
- function assertMigrationSnapshotsEqual(source, destination, label) {
47
- for (const [key, hash] of Object.entries(source.artifactFingerprints)) {
48
- if (destination.artifactFingerprints[key] !== hash) {
49
- const error = new Error(`Migration fingerprint mismatch in ${label}: ${key}`);
50
- error.code = E_TASK_MIGRATION_INVALID;
51
- throw error;
52
- }
53
- }
54
-
55
- for (const [key, hash] of Object.entries(source.directoryFingerprints)) {
56
- if (destination.directoryFingerprints[key] !== hash) {
57
- const error = new Error(`Migration directory fingerprint mismatch in ${label}: ${key}`);
58
- error.code = E_TASK_MIGRATION_INVALID;
59
- throw error;
60
- }
61
- }
62
-
63
- if (destination.eventCount !== source.eventCount) {
64
- const error = new Error(`Migration event count mismatch in ${label}`);
65
- error.code = E_TASK_MIGRATION_INVALID;
66
- throw error;
67
- }
68
- }
69
-
70
29
  export async function migrateLegacyLayout(
71
30
  target,
72
31
  {
73
32
  dryRun = false,
74
33
  packageRoot = getPackageRoot(),
75
- afterCopyForTest = null,
76
- afterPublishForTest = null,
77
- beforeLegacyCleanupForTest = null,
78
- removeLegacyArtifactForTest = null,
79
34
  } = {},
80
35
  ) {
81
36
  const detection = await detectLegacySingletonLayout(target);
@@ -90,60 +45,21 @@ export async function migrateLegacyLayout(
90
45
  let canonicalTaskId = null;
91
46
  const artifactIdentities = [];
92
47
 
93
- // Determine candidate canonicalTaskId from known legacy artifacts
48
+ const identitySchemas = { contract: "current-contract", state: "work-state", continuity: "continuity", receipt: "execution-receipt" };
94
49
  for (const item of detection.legacyFiles) {
95
- if (item.key === "contract") {
96
- try {
97
- const contract = await readJsonArtifact(target, item.path, "current-contract", packageRoot);
98
- if (contract.value?.taskId) {
99
- if (!canonicalTaskId) canonicalTaskId = contract.value.taskId;
100
- artifactIdentities.push({ artifact: "contract", taskId: contract.value.taskId });
101
- }
102
- } catch (err) {
103
- const error = new Error(`Legacy contract artifact is invalid: ${err.message}`);
104
- error.code = E_TASK_MIGRATION_INVALID;
105
- error.cause = err;
106
- throw error;
107
- }
108
- } else if (item.key === "state") {
109
- try {
110
- const state = await readJsonArtifact(target, item.path, "work-state", packageRoot);
111
- if (state.value?.taskId) {
112
- if (!canonicalTaskId) canonicalTaskId = state.value.taskId;
113
- artifactIdentities.push({ artifact: "state", taskId: state.value.taskId });
114
- }
115
- } catch (err) {
116
- const error = new Error(`Legacy state artifact is invalid: ${err.message}`);
117
- error.code = E_TASK_MIGRATION_INVALID;
118
- error.cause = err;
119
- throw error;
120
- }
121
- } else if (item.key === "continuity") {
122
- try {
123
- const continuity = await readJsonArtifact(target, item.path, "continuity", packageRoot);
124
- if (continuity.value?.taskId) {
125
- if (!canonicalTaskId) canonicalTaskId = continuity.value.taskId;
126
- artifactIdentities.push({ artifact: "continuity", taskId: continuity.value.taskId });
127
- }
128
- } catch (err) {
129
- const error = new Error(`Legacy continuity artifact is invalid: ${err.message}`);
130
- error.code = E_TASK_MIGRATION_INVALID;
131
- error.cause = err;
132
- throw error;
133
- }
134
- } else if (item.key === "receipt") {
135
- try {
136
- const receipt = await readJsonArtifact(target, item.path, "execution-receipt", packageRoot);
137
- if (receipt.value?.taskId) {
138
- if (!canonicalTaskId) canonicalTaskId = receipt.value.taskId;
139
- artifactIdentities.push({ artifact: "receipt", taskId: receipt.value.taskId });
140
- }
141
- } catch (err) {
142
- const error = new Error(`Legacy receipt artifact is invalid: ${err.message}`);
143
- error.code = E_TASK_MIGRATION_INVALID;
144
- error.cause = err;
145
- throw error;
50
+ const schema = identitySchemas[item.key];
51
+ if (!schema) continue;
52
+ try {
53
+ const artifact = await readPortableJsonArtifact(target, item.path, schema, packageRoot);
54
+ if (artifact.value?.taskId) {
55
+ canonicalTaskId ??= artifact.value.taskId;
56
+ artifactIdentities.push({ artifact: item.key, taskId: artifact.value.taskId });
146
57
  }
58
+ } catch (err) {
59
+ const error = new Error(`Legacy ${item.key} artifact is invalid: ${err.message}`);
60
+ error.code = E_TASK_MIGRATION_INVALID;
61
+ error.cause = err;
62
+ throw error;
147
63
  }
148
64
  }
149
65
 
@@ -167,7 +83,7 @@ export async function migrateLegacyLayout(
167
83
  }
168
84
 
169
85
  // 1. Validate complete legacy source snapshot fail-closed before creating any directories
170
- const sourceSnapshot = await validateMigrationSnapshot(target, {
86
+ await validateMigrationSnapshot(target, {
171
87
  taskId: canonicalTaskId,
172
88
  packageRoot,
173
89
  paths: LEGACY_TASK_ARTIFACT_PATHS,
@@ -176,8 +92,6 @@ export async function migrateLegacyLayout(
176
92
  const taskKey = taskStorageKey(canonicalTaskId);
177
93
  const finalDirRel = taskDirectory(canonicalTaskId);
178
94
  const finalDirAbs = ensureWithin(target, finalDirRel);
179
- const tempDirRel = `${TASK_STATE_ROOT}/.tmp-${taskKey}`;
180
- const tempDirAbs = ensureWithin(target, tempDirRel);
181
95
 
182
96
  if (await fileExists(finalDirAbs)) {
183
97
  const error = new Error(`Target task directory already exists: ${finalDirRel}`);
@@ -185,8 +99,6 @@ export async function migrateLegacyLayout(
185
99
  throw error;
186
100
  }
187
101
 
188
- const migratedArtifacts = [];
189
-
190
102
  if (dryRun) {
191
103
  return {
192
104
  migrated: false,
@@ -198,187 +110,5 @@ export async function migrateLegacyLayout(
198
110
  };
199
111
  }
200
112
 
201
- let published = false;
202
- let cleanupStarted = false;
203
-
204
- // 2. Create temporary migration directory
205
- await mkdir(tempDirAbs, { recursive: true });
206
-
207
- try {
208
- // 3. Create and write task.json descriptor
209
- const descriptor = createTaskDescriptor({
210
- taskId: canonicalTaskId,
211
- writeClaims: [],
212
- });
213
- await writeTaskDescriptor(target, descriptor, packageRoot, {
214
- relativePathOverride: `${tempDirRel}/task.json`,
215
- });
216
- migratedArtifacts.push("task.json");
217
-
218
- // 4. Copy and map files into temp namespace
219
- const fileMapping = [
220
- [LEGACY_TASK_ARTIFACT_PATHS.contract, "contract.json"],
221
- [LEGACY_TASK_ARTIFACT_PATHS.route, "routing-result.json"],
222
- [LEGACY_TASK_ARTIFACT_PATHS.preflight, "preflight.json"],
223
- [LEGACY_TASK_ARTIFACT_PATHS.state, "work-state.json"],
224
- [LEGACY_TASK_ARTIFACT_PATHS.continuity, "continuity.json"],
225
- [LEGACY_TASK_ARTIFACT_PATHS.receipt, "execution-receipt.json"],
226
- [LEGACY_TASK_ARTIFACT_PATHS.events, "events.ndjson"],
227
- ];
228
-
229
- for (const [legacyRel, destName] of fileMapping) {
230
- const srcAbs = ensureWithin(target, legacyRel);
231
- if (await fileExists(srcAbs)) {
232
- const destAbs = path.join(tempDirAbs, destName);
233
- await cp(srcAbs, destAbs);
234
- migratedArtifacts.push(destName);
235
- }
236
- }
237
-
238
- // 5. Copy directories: gates/ and executions/
239
- const dirMapping = [
240
- [LEGACY_TASK_ARTIFACT_PATHS.gates, "gates"],
241
- [LEGACY_TASK_ARTIFACT_PATHS.executions, "executions"],
242
- ];
243
-
244
- for (const [legacyRel, destDirName] of dirMapping) {
245
- const srcAbs = ensureWithin(target, legacyRel);
246
- if (await fileExists(srcAbs)) {
247
- const destAbs = path.join(tempDirAbs, destDirName);
248
- await cp(srcAbs, destAbs, { recursive: true });
249
- migratedArtifacts.push(`${destDirName}/`);
250
- }
251
- }
252
-
253
- // Test hook for corruption / failure testing
254
- if (typeof afterCopyForTest === "function") {
255
- await afterCopyForTest({ tempDirAbs, tempDirRel, target });
256
- }
257
-
258
- // 6. Validate temp snapshot
259
- const tempPaths = {
260
- contract: `${tempDirRel}/contract.json`,
261
- route: `${tempDirRel}/routing-result.json`,
262
- preflight: `${tempDirRel}/preflight.json`,
263
- state: `${tempDirRel}/work-state.json`,
264
- continuity: `${tempDirRel}/continuity.json`,
265
- receipt: `${tempDirRel}/execution-receipt.json`,
266
- events: `${tempDirRel}/events.ndjson`,
267
- gates: `${tempDirRel}/gates`,
268
- executions: `${tempDirRel}/executions`,
269
- };
270
-
271
- const tempSnapshot = await validateMigrationSnapshot(target, {
272
- taskId: canonicalTaskId,
273
- packageRoot,
274
- paths: tempPaths,
275
- });
276
-
277
- // 7. Compare copied fingerprints
278
- assertMigrationSnapshotsEqual(sourceSnapshot, tempSnapshot, "temporary namespace");
279
-
280
- // 8. Atomically publish
281
- await mkdir(ensureWithin(target, TASK_STATE_ROOT), { recursive: true });
282
- await rename(tempDirAbs, finalDirAbs);
283
- published = true;
284
-
285
- // Test hook for corruption / failure testing after publication
286
- if (typeof afterPublishForTest === "function") {
287
- await afterPublishForTest({ finalDirAbs, finalDirRel, target });
288
- }
289
-
290
- // 9. Post-publish validation: verify final namespace snapshot and task descriptor
291
- const finalPaths = {
292
- contract: taskArtifactPath(canonicalTaskId, "contract"),
293
- route: taskArtifactPath(canonicalTaskId, "route"),
294
- preflight: taskArtifactPath(canonicalTaskId, "preflight"),
295
- state: taskArtifactPath(canonicalTaskId, "state"),
296
- continuity: taskArtifactPath(canonicalTaskId, "continuity"),
297
- receipt: taskArtifactPath(canonicalTaskId, "receipt"),
298
- events: taskArtifactPath(canonicalTaskId, "events"),
299
- gates: taskArtifactPath(canonicalTaskId, "gates"),
300
- executions: taskArtifactPath(canonicalTaskId, "executions"),
301
- };
302
-
303
- const finalSnapshot = await validateMigrationSnapshot(target, {
304
- taskId: canonicalTaskId,
305
- packageRoot,
306
- paths: finalPaths,
307
- });
308
- assertMigrationSnapshotsEqual(sourceSnapshot, finalSnapshot, "published namespace");
309
- await readTaskDescriptor(target, canonicalTaskId, packageRoot);
310
-
311
- const migrationReceipt = {
312
- schemaVersion: 1,
313
- protocolVersion: 1,
314
- kind: "LEGACY_TASK_MIGRATION",
315
- taskId: canonicalTaskId,
316
- taskKey,
317
- migratedAt: new Date().toISOString(),
318
- source: sourceSnapshot,
319
- destination: finalSnapshot,
320
- cleanupStatus: "PENDING",
321
- };
322
- await writeFileAtomic(
323
- ensureWithin(target, `${finalDirRel}/migration-receipt.json`),
324
- `${JSON.stringify(migrationReceipt, null, 2)}\n`,
325
- );
326
- migratedArtifacts.push("migration-receipt.json");
327
-
328
- // Test hook for cleanup failure testing
329
- if (typeof beforeLegacyCleanupForTest === "function") {
330
- await beforeLegacyCleanupForTest({ finalDirAbs, finalDirRel, target });
331
- }
332
-
333
- // 10. Cleanup legacy task files (strictly after successful publish and validation)
334
- cleanupStarted = true;
335
- try {
336
- for (const item of detection.legacyFiles) {
337
- if (typeof removeLegacyArtifactForTest === "function") {
338
- await removeLegacyArtifactForTest(target, item.path);
339
- } else {
340
- await removeLegacyArtifact(target, item.path);
341
- }
342
- }
343
- migrationReceipt.cleanupStatus = "COMPLETED";
344
- migrationReceipt.cleanedAt = new Date().toISOString();
345
- await writeFileAtomic(
346
- ensureWithin(target, `${finalDirRel}/migration-receipt.json`),
347
- `${JSON.stringify(migrationReceipt, null, 2)}\n`,
348
- );
349
- } catch (cleanupErr) {
350
- const error = new Error(
351
- `Migration published successfully to ${finalDirRel}, but legacy cleanup failed: ${cleanupErr.message}. Manual cleanup required.`,
352
- );
353
- error.code = E_TASK_MIGRATION_INVALID;
354
- error.cause = cleanupErr;
355
- throw error;
356
- }
357
-
358
- return {
359
- migrated: true,
360
- taskId: canonicalTaskId,
361
- taskKey,
362
- targetDirectory: finalDirRel,
363
- migratedArtifacts,
364
- };
365
- } catch (error) {
366
- if (published && !cleanupStarted && (await fileExists(finalDirAbs))) {
367
- try {
368
- await rm(finalDirAbs, { recursive: true, force: true });
369
- } catch (rollbackErr) {
370
- error.rollbackError = rollbackErr;
371
- }
372
- }
373
-
374
- // Cleanup temp dir on failure if it still exists
375
- if (await fileExists(tempDirAbs)) {
376
- try {
377
- await rm(tempDirAbs, { recursive: true, force: true });
378
- } catch (tempErr) {
379
- error.tempCleanupError = tempErr;
380
- }
381
- }
382
- throw error;
383
- }
113
+ throw Object.assign(new Error("Filesystem task migration apply is retired; use explicit SQLite task migration with destination and excluded writers"), { code: "E_STORAGE_OPERATION_UNSUPPORTED" });
384
114
  }
@@ -27,6 +27,8 @@ export const TASK_ARTIFACT_FILES = Object.freeze({
27
27
  verificationScope: "verification-scope.json",
28
28
  attestations: "attestations",
29
29
  structuralQuality: "structural-quality",
30
+ decisions: "decisions",
31
+ testUtility: "test-utility.json",
30
32
  });
31
33
 
32
34
  export const POLICY_ROOT = ".forgeloop/policy";
@@ -106,6 +108,15 @@ export function taskEvaluationPath(taskId, evaluationId) {
106
108
  return `${taskDirectory(taskId)}/${TASK_ARTIFACT_FILES.evaluations}/${evaluationId}.json`;
107
109
  }
108
110
 
111
+ export function taskDecisionDirectory(taskId) {
112
+ return taskArtifactPath(taskId, "decisions");
113
+ }
114
+
115
+ export function taskDecisionPath(taskId, decisionId) {
116
+ assertArtifactId(decisionId, /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/, "decision ID");
117
+ return `${taskDecisionDirectory(taskId)}/${decisionId}.json`;
118
+ }
119
+
109
120
  function assertArtifactId(value, pattern, label) {
110
121
  if (typeof value !== "string" || !pattern.test(value)) {
111
122
  throw new Error(`Invalid ${label}: ${value}`);
@@ -217,6 +228,8 @@ export function buildTaskArtifactPaths(taskId) {
217
228
  actions: `${dir}/${TASK_ARTIFACT_FILES.actions}`,
218
229
  approvals: `${dir}/${TASK_ARTIFACT_FILES.approvals}`,
219
230
  evaluations: `${dir}/${TASK_ARTIFACT_FILES.evaluations}`,
231
+ decisions: `${dir}/${TASK_ARTIFACT_FILES.decisions}`,
232
+ testUtility: `${dir}/${TASK_ARTIFACT_FILES.testUtility}`,
220
233
  usage: `${dir}/${TASK_ARTIFACT_FILES.usage}`,
221
234
  lock: taskLockPath(taskId),
222
235
  policySnapshot: `${dir}/${TASK_ARTIFACT_FILES.policySnapshot}`,
@@ -12,6 +12,7 @@ export const TASK_RECOVERY_SCHEMA_VERSION = 1;
12
12
  export const TASK_RECOVERY_EVENT_TYPES = Object.freeze(new Set([
13
13
  "TASK_RECOVERY_RECORDED",
14
14
  "OPERATOR_RECOVERY_RECORDED",
15
+ "TASK_ABANDONED",
15
16
  LEGACY_RECOVERY_MIGRATION_EVENT,
16
17
  ]));
17
18
 
@@ -1,16 +1,21 @@
1
- import { readEvents, validateEventLedger } from "./events.js";
1
+ import { taskArtifactPath } from "./task-paths.js";
2
+ import { createHash } from "node:crypto";
3
+ import { getOperationalStore } from "../storage/operational-context.js";
4
+ import { needsExistingProjectScope, withExistingProjectScope } from "../storage/existing-project-scope.js";
5
+ import { iterateEvents, validateEventLedger, withEventLedgerAudit } from "./events.js";
2
6
  import { readWorkState } from "./work-state.js";
3
7
  import { canonicalFingerprint } from "./artifacts.js";
4
8
 
5
- function ledgerTail(events) {
6
- const last = events.at(-1) ?? null;
9
+ async function ledgerTail(target, packageRoot, options) {
10
+ let last = null;
11
+ for await (const event of iterateEvents(target, packageRoot, options)) last = event;
7
12
  return {
8
13
  sequence: last?.seq ?? 0,
9
14
  hash: last?.hash ?? null,
10
15
  };
11
16
  }
12
17
 
13
- export async function buildTaskSnapshot({
18
+ async function buildPortableTaskSnapshot({
14
19
  target,
15
20
  packageRoot,
16
21
  taskId = null,
@@ -19,19 +24,17 @@ export async function buildTaskSnapshot({
19
24
  } = {}) {
20
25
  const options = { packageRoot, taskId, ...(eventsPath ? { eventsPath } : {}) };
21
26
  const state = await readWorkState(target, { ...options, ...(stateFile ? { statePath: stateFile } : {}) });
22
- const events = await readEvents(target, packageRoot, options);
23
27
  const before = {
24
28
  stateRevision: state?.revision ?? null,
25
- ...ledgerTail(events),
29
+ ...await ledgerTail(target, packageRoot, options),
26
30
  };
27
31
 
28
32
  const validation = await validateEventLedger(target, packageRoot, options);
29
33
 
30
34
  const rereadState = await readWorkState(target, { ...options, ...(stateFile ? { statePath: stateFile } : {}) });
31
- const rereadEvents = await readEvents(target, packageRoot, options);
32
35
  const after = {
33
36
  stateRevision: rereadState?.revision ?? null,
34
- ...ledgerTail(rereadEvents),
37
+ ...await ledgerTail(target, packageRoot, options),
35
38
  };
36
39
 
37
40
  const consistent = JSON.stringify(before) === JSON.stringify(after);
@@ -51,3 +54,43 @@ export async function buildTaskSnapshot({
51
54
  events: validation.events,
52
55
  };
53
56
  }
57
+
58
+
59
+ function eventSummaryFingerprint(events) {
60
+ const digest = createHash("sha256");
61
+ digest.update("[");
62
+ let first = true;
63
+ for (const { seq, event, at } of events) {
64
+ if (!first) digest.update(",");
65
+ first = false;
66
+ // Same sorted keys/undefined omission as canonicalFingerprint({seq,event,at}).
67
+ digest.update(JSON.stringify({ at, event, seq }));
68
+ }
69
+ return digest.update("]").digest("hex");
70
+ }
71
+
72
+ /** Internal callback keeps event source owned until the projection finishes. */
73
+ export async function withTaskSnapshot(options, callback) {
74
+ const { target, packageRoot, taskId = null, eventsPath = null, stateFile = null } = options;
75
+ if (await needsExistingProjectScope(target)) {
76
+ return withExistingProjectScope(target, () => withTaskSnapshot(options, callback), { readOnly: true });
77
+ }
78
+ if (!getOperationalStore(target) || (stateFile && (!taskId || stateFile !== taskArtifactPath(taskId, "state")))) {
79
+ return callback(await buildPortableTaskSnapshot(options));
80
+ }
81
+ return withEventLedgerAudit(target, packageRoot, { taskId, ...(eventsPath ? { eventsPath } : {}) }, async validation => {
82
+ if (Array.isArray(validation.events)) return callback(await buildPortableTaskSnapshot(options));
83
+ const state = await readWorkState(target, { packageRoot, taskId, ...(stateFile ? { statePath: stateFile } : {}) });
84
+ const tail = validation.events.at(-1);
85
+ return callback({ consistent: true, taskId: taskId ?? state?.taskId ?? null,
86
+ anchors: { stateRevision: state?.revision ?? null, sequence: tail?.seq ?? 0, hash: tail?.hash ?? null },
87
+ capturedAt: new Date().toISOString(),
88
+ integrity: { valid: validation.valid, errors: validation.errors, eventCount: validation.events.length,
89
+ fingerprint: eventSummaryFingerprint(validation.events) },
90
+ state, events: validation.events });
91
+ });
92
+ }
93
+
94
+ export async function buildTaskSnapshot(options = {}) {
95
+ return withTaskSnapshot(options, snapshot => ({ ...snapshot, events: Array.isArray(snapshot.events) ? snapshot.events : [...snapshot.events] }));
96
+ }
@@ -0,0 +1,18 @@
1
+ import { detectLegacySingletonLayout, migrateLegacyLayout } from "./task-migration.js";
2
+ import { migrateProjectStorage } from "../storage/migration.js";
3
+
4
+ /** Explicit legacy conversion now publishes canonical SQLite storage. */
5
+ export async function migrateLegacyTaskStorage(target, { packageRoot, dryRun = false, destination, writersQuiesced = false } = {}) {
6
+ const detected = await detectLegacySingletonLayout(target);
7
+ if (!detected.hasLegacy) return migrateLegacyLayout(target, { packageRoot, dryRun });
8
+ if (dryRun) return { ...await migrateLegacyLayout(target, { packageRoot, dryRun }), targetDirectory: ".forgeloop/state.sqlite" };
9
+ if (typeof destination !== "string" || !destination || writersQuiesced !== true) throw Object.assign(new Error("Legacy task migration requires --destination and explicit --writers-quiesced for SQLite cutover"), { code: "E_CLI_INVOCATION_INVALID" });
10
+ const planned = await migrateLegacyLayout(target, { packageRoot, dryRun: true });
11
+ const storage = await migrateProjectStorage(target, { destination, writersQuiesced, packageRoot });
12
+ return {
13
+ migrated: true, taskId: planned.taskId, taskKey: planned.taskKey,
14
+ targetDirectory: ".forgeloop/state.sqlite", storage,
15
+ migrationReceipt: `${destination}/publication/publication-journal.json`,
16
+ migratedArtifacts: detected.legacyFiles.map(item => item.path),
17
+ };
18
+ }
@@ -12,8 +12,17 @@ import { GUIDE_TEMPLATE_PATHS } from "./guide-registry.js";
12
12
 
13
13
  const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
14
14
 
15
- const TEMPLATE_SOURCE_PATHS = Object.freeze({
16
- ".forgeloop/.gitignore": ".forgeloop/forgeloop.gitignore",
15
+ const INLINE_TEMPLATE_CONTENT = Object.freeze({
16
+ ".forgeloop/.gitignore": [
17
+ "# Local resumable task state is untrusted, target-specific data.",
18
+ "work-state.json",
19
+ "state.sqlite",
20
+ "state.sqlite-*",
21
+ "storage-version.json",
22
+ "executions/",
23
+ "repository-index/",
24
+ "",
25
+ ].join("\n"),
17
26
  });
18
27
 
19
28
  export const TEMPLATE_PATHS = [
@@ -36,8 +45,6 @@ export const TEMPLATE_PATHS = [
36
45
  "PROTOCOL_INTEGRATION.md",
37
46
  "AGENT_COMPATIBILITY.md",
38
47
  "THIRD_PARTY_NOTICES.md",
39
- "LICENSE",
40
- "LICENSE-DOCS.md",
41
48
  ...GUIDE_TEMPLATE_PATHS,
42
49
  "schemas/routing-input.schema.json",
43
50
  "schemas/routing-result.schema.json",
@@ -88,6 +95,9 @@ export const TEMPLATE_PATHS = [
88
95
  "schemas/execution-profile-benchmark-run.schema.json",
89
96
  "schemas/execution-profile-benchmark-aggregate.schema.json",
90
97
  "schemas/structural-quality.schema.json",
98
+ "schemas/semantic-decision.schema.json",
99
+ "schemas/context-plan.schema.json",
100
+ "schemas/test-utility.schema.json",
91
101
  ];
92
102
 
93
103
  export function getPackageRoot() {
@@ -97,14 +107,19 @@ export function getPackageRoot() {
97
107
  export async function readTemplateEntries(packageRoot = PACKAGE_ROOT) {
98
108
  return Promise.all(
99
109
  TEMPLATE_PATHS.map(async (relativePath) => {
100
- const sourcePath = TEMPLATE_SOURCE_PATHS[relativePath] ?? relativePath;
110
+ const sourcePath = relativePath;
111
+ const inlineContent = INLINE_TEMPLATE_CONTENT[relativePath];
101
112
  return {
102
113
  relativePath: targetPathForSource(relativePath),
103
114
  sourcePath,
104
115
  legacyRelativePath: legacyPathForSource(sourcePath),
105
- bytes: Buffer.from(isNativeAdapterPath(relativePath)
106
- ? nativeShim(relativePath)
107
- : await readFile(path.join(packageRoot, sourcePath))),
116
+ bytes: Buffer.from(
117
+ inlineContent !== undefined
118
+ ? inlineContent
119
+ : isNativeAdapterPath(relativePath)
120
+ ? nativeShim(relativePath)
121
+ : await readFile(path.join(packageRoot, sourcePath)),
122
+ ),
108
123
  };
109
124
  }),
110
125
  );
@@ -0,0 +1,68 @@
1
+ import { canonicalFingerprint } from "../artifacts.js";
2
+ import { buildTestUtilityArtifact } from "./utility.js";
3
+
4
+ const FIXTURE_TESTS = Object.freeze([
5
+ { testId: "protected-security", file: "tests/security.test.js", framework: "node:test", suite: "", name: "security", line: 1, signals: { security: true } },
6
+ { testId: "protected-public-api", file: "tests/api.test.js", framework: "node:test", suite: "", name: "api", line: 1, signals: { publicApi: true } },
7
+ { testId: "semantic-duplicate", file: "tests/duplicate.test.js", framework: "node:test", suite: "", name: "duplicate", line: 1, signals: {} },
8
+ { testId: "unique-branch", file: "tests/required.test.js", framework: "node:test", suite: "", name: "required", line: 1, signals: { requiredBehavior: true } },
9
+ { testId: "obsolete-test", file: "tests/obsolete.test.js", framework: "node:test", suite: "", name: "obsolete", line: 1, signals: {} },
10
+ { testId: "slow-required", file: "tests/critical.test.js", framework: "node:test", suite: "", name: "critical", line: 1, signals: { criticalPath: true } },
11
+ { testId: "flaky-low-signal", file: "tests/flaky.test.js", framework: "node:test", suite: "", name: "flaky", line: 1, signals: {} },
12
+ ]);
13
+
14
+ const CASES = Object.freeze([
15
+ { id: "protected-security", expected: "KEEP_RISK_GUARD", protected: true },
16
+ { id: "protected-public-api", expected: "KEEP_UNIQUE", protected: true },
17
+ { id: "semantic-duplicate", expected: "REDUNDANT_CANDIDATE", protected: false },
18
+ { id: "unique-branch", expected: "KEEP_REQUIRED", protected: true },
19
+ { id: "obsolete-test", expected: "OBSOLETE_CANDIDATE", protected: false },
20
+ { id: "slow-required", expected: "KEEP_RECOVERY_INVARIANT", protected: true },
21
+ { id: "flaky-low-signal", expected: "FLAKY_LOW_SIGNAL", protected: false },
22
+ ]);
23
+
24
+ function precision(cases, label) {
25
+ const predicted = cases.filter((item) => item.observed === label);
26
+ if (predicted.length === 0) return null;
27
+ return predicted.filter((item) => item.expected === label).length / predicted.length;
28
+ }
29
+
30
+ function recall(cases, label) {
31
+ const expected = cases.filter((item) => item.expected === label);
32
+ if (expected.length === 0) return null;
33
+ return expected.filter((item) => item.observed === label).length / expected.length;
34
+ }
35
+
36
+ export function runTestIntelligenceBenchmark() {
37
+ const candidateIds = ["semantic-duplicate", "obsolete-test", "flaky-low-signal"];
38
+ const decisions = { candidateIds, tests: {
39
+ "semantic-duplicate": { semantic_duplicate: true },
40
+ "obsolete-test": { likely_obsolete: true },
41
+ "flaky-low-signal": { likely_flaky_low_signal: true },
42
+ } };
43
+ const confidence = Object.fromEntries(
44
+ candidateIds.flatMap((id, index) => (
45
+ Object.keys(decisions.tests[id]).map((dimension) => [`test_${index}_${dimension}`, 0.95])
46
+ )),
47
+ );
48
+ const observedTests = buildTestUtilityArtifact({ taskId: "benchmark", inventory: { schemaVersion: 1, source: "FIXTURE", tests: FIXTURE_TESTS }, semanticDecision: { decisionId: "benchmark-decision", decision: decisions, confidence } }).tests;
49
+ const cases = CASES.map((item) => ({ ...item, observed: observedTests.find((test) => test.testId === item.id)?.classification ?? "UNKNOWN" }));
50
+ const protectedFalsePositives = cases.filter((item) => item.protected && ["REDUNDANT_CANDIDATE", "OBSOLETE_CANDIDATE", "SAFE_TO_REMOVE"].includes(item.observed)).length;
51
+ const summary = {
52
+ schemaVersion: 1,
53
+ status: "OFFLINE_DETERMINISTIC_FIXTURE",
54
+ caseCount: CASES.length,
55
+ metrics: {
56
+ redundantCandidatePrecision: precision(cases, "REDUNDANT_CANDIDATE"),
57
+ redundantCandidateRecall: recall(cases, "REDUNDANT_CANDIDATE"),
58
+ obsoleteCandidatePrecision: precision(cases, "OBSOLETE_CANDIDATE"),
59
+ obsoleteCandidateRecall: recall(cases, "OBSOLETE_CANDIDATE"),
60
+ protectedTestFalsePositiveRate: protectedFalsePositives / CASES.filter((item) => item.protected).length,
61
+ safeToRemoveFalsePositiveRate: precision(cases, "SAFE_TO_REMOVE") === null ? null : 1 - precision(cases, "SAFE_TO_REMOVE"),
62
+ rewriteRecommendationPrecision: null,
63
+ },
64
+ deletionAuthority: false,
65
+ cases,
66
+ };
67
+ return { ...summary, fingerprint: canonicalFingerprint(summary) };
68
+ }