@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,3 +1,4 @@
1
+ import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
1
2
  import { createHash } from "node:crypto";
2
3
 
3
4
  import { canonicalFingerprint } from "./artifacts.js";
@@ -8,7 +9,8 @@ import { assertRevisionProvider, resolveRevisionProvider } from "./revision/prov
8
9
  import { REVISION_PROVIDERS } from "./revision/registry.js";
9
10
  import { assertSigningProvider, resolveSigningProvider } from "./signing/provider.js";
10
11
  import { SIGNING_PROVIDERS } from "./signing/registry.js";
11
- import { taskAttestationBundlePath, taskAttestationStatementPath } from "./task-paths.js";
12
+ import { taskAttestationStatementPath } from "./task-paths.js";
13
+ import { resolveAttestationBundlePath } from "./attachment-paths.js";
12
14
  import { ensureWithin, fileExists } from "./filesystem.js";
13
15
  import { E_ATTESTATION_CONFIGURATION_INVALID } from "./error-codes.js";
14
16
 
@@ -130,6 +132,23 @@ export async function verifyAttestation({
130
132
  signingProvider = null,
131
133
  trustedRoot = null,
132
134
  requireSignature = false,
135
+ } = {}) {
136
+ const options = { target, packageRoot, taskId, revision, bundlePath, identity, issuer, revisionProvider, signingProvider, trustedRoot, requireSignature };
137
+ return withProjectReadSnapshot(target, () => verifySelectedAttestation(options));
138
+ }
139
+
140
+ async function verifySelectedAttestation({
141
+ target,
142
+ packageRoot = getPackageRoot(),
143
+ taskId,
144
+ revision = null,
145
+ bundlePath = null,
146
+ identity = null,
147
+ issuer = null,
148
+ revisionProvider = null,
149
+ signingProvider = null,
150
+ trustedRoot = null,
151
+ requireSignature = false,
133
152
  } = {}) {
134
153
  let effectiveRequireSignature = requireSignature === true;
135
154
  let effectiveSigningProvider = signingProvider;
@@ -146,7 +165,6 @@ export async function verifyAttestation({
146
165
  } catch (error) {
147
166
  if (error.code !== "ARTIFACT_MISSING") configurationError = error;
148
167
  }
149
- if (effectiveRequireSignature && !effectiveBundlePath) effectiveBundlePath = taskAttestationBundlePath(taskId);
150
168
  const result = {
151
169
  schemaVersion: 1,
152
170
  status: "INVALID",
@@ -166,6 +184,7 @@ export async function verifyAttestation({
166
184
  return result;
167
185
  }
168
186
  try {
187
+ if (effectiveBundlePath || effectiveRequireSignature) effectiveBundlePath = resolveAttestationBundlePath(target, taskId, effectiveBundlePath);
169
188
  const statementArtifact = await readAttestationStatement({ target, packageRoot, taskId });
170
189
  const manifestArtifact = await readCodeManifest({ target, packageRoot, taskId });
171
190
  await validateAttestationStatement(statementArtifact.value, packageRoot);
@@ -1,3 +1,5 @@
1
+ import { withTaskTransaction } from "./transaction.js";
2
+ import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
1
3
  import { canonicalFingerprint, readJsonArtifact, writeJsonArtifact } from "./artifacts.js";
2
4
  import { assertSchema, readSchema } from "./schema-validation.js";
3
5
  import { assertSecretFree } from "./receipt.js";
@@ -6,10 +8,11 @@ import { readCodeManifest, validateCodeManifestBindings } from "./code-manifest.
6
8
  import {
7
9
  taskAttestationStatementPath,
8
10
  taskAttestationStatementHistoryPath,
9
- taskAttestationBundlePath,
10
11
  } from "./task-paths.js";
11
12
  import { appendProtocolEvent } from "./events.js";
12
13
  import { ensureWithin, fileExists } from "./filesystem.js";
14
+ import { operationalArtifactExists } from "../storage/operational-context.js";
15
+ import { resolveAttestationBundlePath } from "./attachment-paths.js";
13
16
  import { E_ATTESTATION_CONFIGURATION_INVALID } from "./error-codes.js";
14
17
 
15
18
  const STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
@@ -136,8 +139,13 @@ export async function readAttestationStatement({ target, packageRoot = getPackag
136
139
 
137
140
  export async function writeAttestationStatement({ target, packageRoot = getPackageRoot(), taskId, statement } = {}) {
138
141
  await validateAttestationStatement(statement, packageRoot);
142
+ return withTaskTransaction({ target, taskId, packageRoot, operation: "attestation-create" }, () =>
143
+ writeValidatedAttestationStatement({ target, packageRoot, taskId, statement }));
144
+ }
145
+
146
+ async function writeValidatedAttestationStatement({ target, packageRoot, taskId, statement }) {
139
147
  const relativePath = taskAttestationStatementPath(taskId);
140
- if (await fileExists(ensureWithin(target, relativePath))) {
148
+ if (operationalArtifactExists(target, relativePath) ?? await fileExists(ensureWithin(target, relativePath))) {
141
149
  let previous;
142
150
  try {
143
151
  previous = await readAttestationStatement({ target, packageRoot, taskId });
@@ -150,7 +158,7 @@ export async function writeAttestationStatement({ target, packageRoot = getPacka
150
158
  throw attestationError("E_ATTESTATION_STATEMENT_INVALID", "Attestation statement is immutable within a verification cycle", [relativePath]);
151
159
  }
152
160
  const historyPath = taskAttestationStatementHistoryPath(taskId, previousCycle);
153
- if (!(await fileExists(ensureWithin(target, historyPath)))) {
161
+ if (!(operationalArtifactExists(target, historyPath) ?? await fileExists(ensureWithin(target, historyPath)))) {
154
162
  await writeJsonArtifact(target, historyPath, previous.value, "in-toto-statement", packageRoot, { taskId, operation: "attestation-statement-history" });
155
163
  }
156
164
  }
@@ -164,7 +172,11 @@ export async function writeAttestationStatement({ target, packageRoot = getPacka
164
172
  return { path: relativePath, fingerprint: artifact.fingerprint, statement };
165
173
  }
166
174
 
167
- export async function resolveAttestationStatus({ target, packageRoot = getPackageRoot(), taskId, revisionProvider = null, bundlePath = null, requireSignature = false, signingProvider = null, signerPolicy = {} } = {}) {
175
+ export async function resolveAttestationStatus(options = {}) {
176
+ return withProjectReadSnapshot(options.target, () => resolveSelectedAttestationStatus(options));
177
+ }
178
+
179
+ async function resolveSelectedAttestationStatus({ target, packageRoot = getPackageRoot(), taskId, revisionProvider = null, bundlePath = null, requireSignature = false, signingProvider = null, signerPolicy = {} } = {}) {
168
180
  const result = {
169
181
  taskId,
170
182
  status: "MISSING",
@@ -192,7 +204,6 @@ export async function resolveAttestationStatus({ target, packageRoot = getPackag
192
204
  ...(configuredSigning.policy ?? {}),
193
205
  ...signerPolicy,
194
206
  };
195
- if (effectiveRequireSignature && !effectiveBundlePath) effectiveBundlePath = taskAttestationBundlePath(taskId);
196
207
  result.signature = effectiveRequireSignature ? "REQUIRED" : "NOT_CHECKED";
197
208
  if ((config.attestation?.mode ?? "off") === "off" && !bundlePath && !effectiveRequireSignature) {
198
209
  result.status = "DISABLED";
@@ -208,8 +219,8 @@ export async function resolveAttestationStatus({ target, packageRoot = getPackag
208
219
  }
209
220
  }
210
221
  result.signature = effectiveRequireSignature ? "REQUIRED" : "NOT_CHECKED";
211
- if (effectiveRequireSignature && !effectiveBundlePath) effectiveBundlePath = taskAttestationBundlePath(taskId);
212
222
  try {
223
+ if (effectiveBundlePath || effectiveRequireSignature) effectiveBundlePath = resolveAttestationBundlePath(target, taskId, effectiveBundlePath);
213
224
  const statement = await readAttestationStatement({ target, packageRoot, taskId });
214
225
  const manifest = await readCodeManifest({ target, packageRoot, taskId });
215
226
  const provider = revisionProvider ?? manifest.value.capture.revisionProvider;
@@ -0,0 +1,514 @@
1
+ import { runNext } from "../commands/next.js";
2
+ import { runReport } from "../commands/report.js";
3
+ import { runStatus } from "../commands/status.js";
4
+ import { evaluateAudit } from "./audit.js";
5
+ import { listApprovals } from "./approvals.js";
6
+ import { buildTaskHistory } from "./history.js";
7
+ import { INTEGRATION_LIMITS } from "./integration-limits.js";
8
+ import { resolveTaskClaimState } from "./task-claim-state.js";
9
+ import { findTaskById } from "./task-discovery.js";
10
+ import { buildTaskTrace } from "./trace.js";
11
+
12
+ export const AUDIT_UX_SCHEMA_VERSION = 1;
13
+
14
+ export const AUDIT_UX_CATEGORIES = Object.freeze([
15
+ "LIFECYCLE",
16
+ "CONTRACT",
17
+ "ROUTING",
18
+ "GATE",
19
+ "CHECK",
20
+ "DIAGNOSTIC",
21
+ "ACTION",
22
+ "APPROVAL",
23
+ "OWNERSHIP",
24
+ "COMPLETION",
25
+ "RECOVERY",
26
+ "INTEGRITY",
27
+ ]);
28
+
29
+ export const AUDIT_UX_LIMITS = Object.freeze({
30
+ maxTimelineItems: 200,
31
+ maxChecks: 64,
32
+ maxAttemptsPerCheck: 32,
33
+ maxDiagnostics: 64,
34
+ maxActions: 64,
35
+ maxApprovals: 64,
36
+ maxStringLength: 512,
37
+ maxOutputBytes: 256 * 1024,
38
+ });
39
+
40
+ const PHASES = new Set([
41
+ "RECEIVED", "DISCOVERING", "CONTRACT_READY", "ROUTED", "DESIGNING", "PLANNED",
42
+ "EXECUTING", "VERIFYING", "DIAGNOSING", "CORRECTING", "REVIEWING", "COMPLETE",
43
+ ]);
44
+
45
+ const SAFE_STATUSES = new Set([
46
+ "PASSED", "FAILED", "BLOCKED", "PENDING", "CURRENT", "STALE", "VALID", "INVALID",
47
+ "COMPLETE", "INCOMPLETE", "NOT_VERIFIED", "NOT_APPLICABLE", "UNKNOWN",
48
+ ]);
49
+
50
+ const EVENT_TITLES = Object.freeze({
51
+ TASK_RECEIVED: "Task received",
52
+ CONTRACT_VALIDATED: "Contract validated",
53
+ ROUTE_VALIDATED: "Route validated",
54
+ GATE_SATISFIED: "Gate satisfied",
55
+ PREFLIGHT_READY: "Preflight READY",
56
+ PREFLIGHT_BLOCKED: "Preflight BLOCKED",
57
+ DESIGN_GATE_STARTED: "Design phase started",
58
+ PLAN_RECORDED: "Plan recorded",
59
+ EXECUTION_STARTED: "Execution started",
60
+ VERIFICATION_STARTED: "Verification started",
61
+ VERIFICATION_RECORDED: "Verification recorded",
62
+ CHECK_RECORDED: "Check recorded",
63
+ TERMINAL_RESULT_RECORDED: "Terminal result recorded",
64
+ REVIEW_STARTED: "Review started",
65
+ DIAGNOSIS_RECORDED: "Diagnosis recorded",
66
+ DIAGNOSTIC_CASE_RECORDED: "Diagnostic case recorded",
67
+ HYPOTHESIS_DISPOSITION_RECORDED: "Hypothesis disposition recorded",
68
+ INTERVENTION_RECORDED: "Intervention recorded",
69
+ CONTINUITY_RECORDED: "Continuity recorded",
70
+ CHECKPOINT_RECONCILED: "Checkpoint reconciled",
71
+ TASK_RECOVERY_RECORDED: "Recovery recorded",
72
+ TASK_RECOVERY_RESUMED: "Recovery resumed",
73
+ OPERATOR_RECOVERY_RECORDED: "Operator recovery recorded",
74
+ LEGACY_RECOVERY_MIGRATION_RECORDED: "Legacy recovery migration recorded",
75
+ COMPLETION_VALIDATED: "Completion validated",
76
+ COMPLETION_REJECTED: "Completion rejected",
77
+ TRANSACTION_COMMITTED: "Transaction committed",
78
+ });
79
+
80
+ function auditUxError(code, message) {
81
+ const error = new Error(message);
82
+ error.code = code;
83
+ return error;
84
+ }
85
+
86
+ function boundedText(value, fallback = null) {
87
+ if (typeof value !== "string") return fallback;
88
+ const normalized = value.replace(/\s+/gu, " ").trim();
89
+ if (!normalized) return fallback;
90
+ return normalized.length > AUDIT_UX_LIMITS.maxStringLength
91
+ ? `${normalized.slice(0, AUDIT_UX_LIMITS.maxStringLength - 1)}…`
92
+ : normalized;
93
+ }
94
+
95
+ const URL_USERINFO_PATTERN = /\b([A-Za-z][A-Za-z0-9+.-]*):\/\/[^/\s@?#]+@/gu;
96
+ const COOKIE_HEADER_PATTERN = /\b(Cookie|Set-Cookie)\s*:\s*[^\r\n]*/giu;
97
+ const AUTHORIZATION_HEADER_PATTERN = /\b(Authorization|Proxy-Authorization)\s*:\s*[^\r\n]*/giu;
98
+ const CREDENTIAL_ASSIGNMENT_PATTERN = /\b(password|passwd|token|access_token|refresh_token|secret|client_secret|api_key|apikey)\s*=\s*[^\s,;]+/giu;
99
+ const ENVIRONMENT_PATTERN = /\b(?!(?:PASSWORD|PASSWD|TOKEN|ACCESS_TOKEN|REFRESH_TOKEN|SECRET|CLIENT_SECRET|API_KEY|APIKEY)\b)[A-Z_][A-Z0-9_]{2,}=[^\s,;]+/gu;
100
+ const FILE_URL_PATTERN = /\bfile:\/\/[^\s,;)]*/giu;
101
+ const WINDOWS_PATH_PATTERN = /(?:\b[A-Za-z]:[\\/]|\\\\)[^\s,;)]*/gu;
102
+ const POSIX_PATH_PATTERN = /(^|[^\w])\/(?:[A-Za-z0-9._~-]+(?:\/[A-Za-z0-9._~-]+)*)/gu;
103
+ const URL_PATTERN = /\b[A-Za-z][A-Za-z0-9+.-]*:\/\/[^\s,;)]*/gu;
104
+
105
+ function safeText(value, fallback = null) {
106
+ if (typeof value !== "string") return fallback;
107
+ const bounded = value.length > AUDIT_UX_LIMITS.maxStringLength
108
+ ? `${value.slice(0, AUDIT_UX_LIMITS.maxStringLength - 1)}…`
109
+ : value;
110
+ const text = bounded.trim();
111
+ if (!text) return fallback;
112
+ let redacted = text
113
+ .replace(URL_USERINFO_PATTERN, "$1://<credential>@")
114
+ .replace(COOKIE_HEADER_PATTERN, "$1: <credential>")
115
+ .replace(AUTHORIZATION_HEADER_PATTERN, "$1: <credential>")
116
+ .replace(CREDENTIAL_ASSIGNMENT_PATTERN, "$1=<credential>")
117
+ .replace(ENVIRONMENT_PATTERN, "<environment>")
118
+ .replace(FILE_URL_PATTERN, "<path>")
119
+ .replace(WINDOWS_PATH_PATTERN, "<path>");
120
+
121
+ const urls = [];
122
+ redacted = redacted.replace(URL_PATTERN, (url) => {
123
+ const marker = `__AUDIT_UX_URL_${urls.length}__`;
124
+ urls.push(url);
125
+ return marker;
126
+ });
127
+ redacted = redacted.replace(POSIX_PATH_PATTERN, "$1<path>");
128
+ return boundedText(
129
+ redacted.replace(/__AUDIT_UX_URL_(\d+)__/gu, (_, index) => urls[Number(index)]),
130
+ fallback,
131
+ );
132
+ }
133
+
134
+ function safeIdentifier(value) {
135
+ const text = boundedText(value);
136
+ return text && /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/u.test(text) ? text : null;
137
+ }
138
+
139
+ function safeStatus(value) {
140
+ const normalized = typeof value === "string" ? value.toUpperCase() : "UNKNOWN";
141
+ return SAFE_STATUSES.has(normalized) ? normalized : "UNKNOWN";
142
+ }
143
+
144
+ function safePhase(value) {
145
+ return typeof value === "string" && PHASES.has(value) ? value : null;
146
+ }
147
+
148
+ function uniqueStrings(values) {
149
+ return [...new Set(values.filter((value) => typeof value === "string" && value))].sort();
150
+ }
151
+
152
+ function projectErrors(errors = []) {
153
+ return errors.map((error) => ({
154
+ code: safeIdentifier(error?.code) ?? "E_AUDIT_UX_INVALID_ERROR",
155
+ message: safeText(error?.message, "Canonical validation reported an error."),
156
+ }));
157
+ }
158
+
159
+ function categoryForEvent(event) {
160
+ const type = event?.type ?? "";
161
+ if (type.startsWith("ACTION_")) return "ACTION";
162
+ if (type.startsWith("APPROVAL_")) return "APPROVAL";
163
+ if (type.includes("RECOVERY") || type === "CHECKPOINT_RECONCILED") return "RECOVERY";
164
+ if (type.startsWith("DIAGNOSTIC") || type.startsWith("DIAGNOSIS")
165
+ || type === "INTERVENTION_RECORDED" || type === "HYPOTHESIS_DISPOSITION_RECORDED") return "DIAGNOSTIC";
166
+ if (type === "GATE_SATISFIED") return "GATE";
167
+ if (type === "VERIFICATION_RECORDED" || type === "CHECK_RECORDED" || type === "TERMINAL_RESULT_RECORDED") return "CHECK";
168
+ if (type === "COMPLETION_VALIDATED" || type === "COMPLETION_REJECTED") return "COMPLETION";
169
+ if (type === "CONTRACT_VALIDATED" || type === "PLAN_RECORDED" || type === "DECISION_CRITERION_RECORDED") return "CONTRACT";
170
+ if (type === "ROUTE_VALIDATED") return "ROUTING";
171
+ if (type === "TRANSACTION_COMMITTED") return "INTEGRITY";
172
+ return "LIFECYCLE";
173
+ }
174
+
175
+ function eventLabel(event) {
176
+ const data = event?.data ?? {};
177
+ const title = EVENT_TITLES[event?.type] ?? "Protocol event";
178
+ if (event?.type === "GATE_SATISFIED") {
179
+ const gate = safeIdentifier(data.gate);
180
+ return gate ? `${title}: ${gate}` : title;
181
+ }
182
+ if (["VERIFICATION_RECORDED", "CHECK_RECORDED"].includes(event?.type)) {
183
+ const check = safeIdentifier(data.id ?? data.checkId ?? data.requirement);
184
+ const status = safeStatus(data.status);
185
+ return check ? `${title}: ${check} ${status}` : `${title}: ${status}`;
186
+ }
187
+ if (event?.type?.startsWith("ACTION_") || event?.type?.startsWith("APPROVAL_")) {
188
+ const id = safeIdentifier(data.actionId ?? data.approvalId);
189
+ return id ? `${title}: ${id}` : title;
190
+ }
191
+ return title;
192
+ }
193
+
194
+ function timelineItem(event) {
195
+ const data = event?.data ?? {};
196
+ const category = categoryForEvent(event);
197
+ const status = typeof data.status === "string" ? safeStatus(data.status) : null;
198
+ const phase = safePhase(event?.phase);
199
+ return {
200
+ id: `event-${event.sequence}`,
201
+ sequence: event.sequence,
202
+ timestamp: typeof event.timestamp === "string" ? event.timestamp : null,
203
+ timestampQuality: event.timestampQuality === "authoritative" ? "authoritative" : "unknown",
204
+ kind: safeIdentifier(event.type) ?? "UNKNOWN_EVENT",
205
+ phase,
206
+ status,
207
+ title: eventLabel(event),
208
+ summary: safeText(event.summary, "Protocol event recorded."),
209
+ category,
210
+ ...(event.phaseQuality ? { phaseQuality: event.phaseQuality } : {}),
211
+ };
212
+ }
213
+
214
+ function normalizeOptions(options = {}) {
215
+ const limit = options.limit === undefined ? 100 : options.limit;
216
+ if (!Number.isInteger(limit) || limit < 1 || limit > AUDIT_UX_LIMITS.maxTimelineItems) {
217
+ throw auditUxError("E_AUDIT_UX_INPUT_INVALID", `limit must be an integer between 1 and ${AUDIT_UX_LIMITS.maxTimelineItems}`);
218
+ }
219
+ for (const key of ["beforeSequence", "afterSequence"]) {
220
+ if (options[key] !== undefined && (!Number.isInteger(options[key]) || options[key] < 0)) {
221
+ throw auditUxError("E_AUDIT_UX_INPUT_INVALID", `${key} must be a non-negative integer`);
222
+ }
223
+ }
224
+ if (options.beforeSequence !== undefined && options.afterSequence !== undefined) {
225
+ throw auditUxError("E_AUDIT_UX_INPUT_INVALID", "beforeSequence and afterSequence cannot be combined");
226
+ }
227
+ let categories = options.categories ?? [];
228
+ if (typeof categories === "string") categories = categories.split(",").map((value) => value.trim()).filter(Boolean);
229
+ if (!Array.isArray(categories) || categories.length > AUDIT_UX_CATEGORIES.length) {
230
+ throw auditUxError("E_AUDIT_UX_INPUT_INVALID", "categories must be a bounded array of category names");
231
+ }
232
+ const normalizedCategories = uniqueStrings(categories.map((value) => String(value).toUpperCase()));
233
+ if (normalizedCategories.some((category) => !AUDIT_UX_CATEGORIES.includes(category))) {
234
+ throw auditUxError("E_AUDIT_UX_INPUT_INVALID", "categories contains an unknown audit timeline category");
235
+ }
236
+ return {
237
+ limit,
238
+ beforeSequence: options.beforeSequence,
239
+ afterSequence: options.afterSequence,
240
+ categories: normalizedCategories,
241
+ };
242
+ }
243
+
244
+ function projectTimeline(events, options) {
245
+ const filtered = events
246
+ .filter((event) => options.beforeSequence === undefined || event.sequence < options.beforeSequence)
247
+ .filter((event) => options.afterSequence === undefined || event.sequence > options.afterSequence)
248
+ .filter((event) => options.categories.length === 0 || options.categories.includes(categoryForEvent(event)))
249
+ .sort((left, right) => left.sequence - right.sequence);
250
+ const hasMore = filtered.length > options.limit;
251
+ const page = hasMore && options.afterSequence === undefined
252
+ ? filtered.slice(-options.limit)
253
+ : filtered.slice(0, options.limit);
254
+ const items = page.map(timelineItem);
255
+ return {
256
+ items,
257
+ totalAvailable: filtered.length,
258
+ truncated: hasMore,
259
+ cursor: {
260
+ beforeSequence: options.beforeSequence ?? null,
261
+ afterSequence: options.afterSequence ?? null,
262
+ nextBeforeSequence: hasMore && options.afterSequence === undefined ? items[0]?.sequence ?? null : null,
263
+ nextAfterSequence: hasMore && options.afterSequence !== undefined ? items.at(-1)?.sequence ?? null : null,
264
+ },
265
+ };
266
+ }
267
+
268
+ function safeCountMap(value) {
269
+ return Object.fromEntries(Object.entries(value ?? {})
270
+ .map(([key, count]) => [safeIdentifier(key) ?? "UNKNOWN", Number.isInteger(count) ? count : 0]));
271
+ }
272
+
273
+ function projectActions(actions) {
274
+ return {
275
+ total: Number.isInteger(actions.total) ? actions.total : 0,
276
+ byState: safeCountMap(actions.byState),
277
+ byCapability: safeCountMap(actions.byCapability),
278
+ required: Number.isInteger(actions.required) ? actions.required : 0,
279
+ ambiguous: Number.isInteger(actions.ambiguous) ? actions.ambiguous : 0,
280
+ failed: Number.isInteger(actions.failed) ? actions.failed : 0,
281
+ verified: Number.isInteger(actions.verified) ? actions.verified : 0,
282
+ trustedSatisfied: Number.isInteger(actions.trustedSatisfied) ? actions.trustedSatisfied : 0,
283
+ untrustedRequired: Number.isInteger(actions.untrustedRequired) ? actions.untrustedRequired : 0,
284
+ repeatedIdempotencyAttempts: Number.isInteger(actions.repeatedIdempotencyAttempts) ? actions.repeatedIdempotencyAttempts : 0,
285
+ reconciliationCount: Number.isInteger(actions.reconciliationCount) ? actions.reconciliationCount : 0,
286
+ eventCount: Number.isInteger(actions.eventCount) ? actions.eventCount : 0,
287
+ };
288
+ }
289
+
290
+ function projectChecks(trace) {
291
+ return trace.checks.slice(0, AUDIT_UX_LIMITS.maxChecks).map((check) => ({
292
+ id: safeIdentifier(check.id) ?? "unknown-check",
293
+ requirement: safeText(check.requirement),
294
+ attemptCount: check.attemptCount,
295
+ failedAttempts: check.failedAttempts,
296
+ currentResult: check.currentResult ? safeStatus(check.currentResult) : null,
297
+ attempts: check.attempts.slice(-AUDIT_UX_LIMITS.maxAttemptsPerCheck).map((attempt) => ({
298
+ sequence: Number.isInteger(attempt.sequence) ? attempt.sequence : null,
299
+ timestamp: typeof attempt.at === "string" ? attempt.at : null,
300
+ status: safeStatus(attempt.status),
301
+ exitCode: Number.isInteger(attempt.exitCode) ? attempt.exitCode : null,
302
+ requirement: safeText(attempt.requirement),
303
+ verificationCycle: Number.isInteger(attempt.verificationCycle) ? attempt.verificationCycle : null,
304
+ provenance: safeIdentifier(attempt.provenance),
305
+ executionMode: safeIdentifier(attempt.executionMode),
306
+ })),
307
+ }));
308
+ }
309
+
310
+ function projectDiagnostics(trace) {
311
+ const cases = trace.diagnostics.cases.slice(0, AUDIT_UX_LIMITS.maxDiagnostics).map((entry) => ({
312
+ sequence: entry.sequence,
313
+ timestamp: typeof entry.at === "string" ? entry.at : null,
314
+ verificationCycle: Number.isInteger(entry.verificationCycle) ? entry.verificationCycle : null,
315
+ diagnosticRevision: Number.isInteger(entry.diagnosticRevision) ? entry.diagnosticRevision : null,
316
+ failureClass: safeIdentifier(entry.failureClass),
317
+ informationGain: safeIdentifier(entry.informationGain),
318
+ }));
319
+ return {
320
+ legacyDiagnosisCount: trace.diagnostics.legacyDiagnoses.length,
321
+ cases,
322
+ caseCount: trace.diagnostics.cases.length,
323
+ interventionCount: trace.diagnostics.interventions.length,
324
+ dispositionCount: trace.diagnostics.dispositions.length,
325
+ invalidRevisionCount: trace.diagnostics.invalidRevisions.length,
326
+ };
327
+ }
328
+
329
+ function projectApprovals(approvals) {
330
+ return approvals.slice(0, AUDIT_UX_LIMITS.maxApprovals).map((approval) => ({
331
+ approvalId: safeIdentifier(approval.approvalId) ?? "unknown-approval",
332
+ actionId: safeIdentifier(approval.actionId),
333
+ status: safeStatus(approval.status),
334
+ decision: safeIdentifier(approval.decision),
335
+ requestedAt: typeof approval.requestedAt === "string" ? approval.requestedAt : null,
336
+ resolvedAt: typeof approval.resolvedAt === "string" ? approval.resolvedAt : null,
337
+ authorityKind: safeIdentifier(approval.authorityKind),
338
+ }));
339
+ }
340
+
341
+ function projectCompletion(audit, ownership) {
342
+ const completion = audit.completion ?? {};
343
+ const coverage = Array.isArray(completion.coverage) ? completion.coverage : [];
344
+ const requirementsSatisfied = coverage.length > 0 && coverage.every((item) => item.status === "COVERED");
345
+ const valid = completion.status === "VALID";
346
+ return {
347
+ state: valid ? "COMPLETE" : "INCOMPLETE",
348
+ valid,
349
+ requirementsSatisfied,
350
+ claimsReleased: ownership.claimState === "RELEASED_BY_COMPLETION",
351
+ publicationStatus: safeIdentifier(audit.publicationStatus),
352
+ productionReadiness: safeIdentifier(audit.productionReadiness),
353
+ reasonCodes: uniqueStrings(projectErrors(audit.errors).map((error) => error.code)),
354
+ coverage: coverage.slice(0, AUDIT_UX_LIMITS.maxChecks).map((item) => ({
355
+ requirement: safeText(item.requirement),
356
+ status: safeStatus(item.status),
357
+ })),
358
+ };
359
+ }
360
+
361
+ function projectReasonCodes(...collections) {
362
+ return uniqueStrings(collections.flat().map((value) => {
363
+ if (typeof value === "string") return value;
364
+ return value?.code;
365
+ }).map((value) => safeIdentifier(value)).filter(Boolean));
366
+ }
367
+
368
+ function buildProjection({ status, audit, report, history, trace, ownership, next, approvals, timeline }) {
369
+ const integrityErrors = [
370
+ ...projectErrors(audit.errors),
371
+ ...projectErrors(trace.integrity.errors),
372
+ ...projectErrors(history.integrity.errors),
373
+ ...projectErrors(ownership.ownershipErrors),
374
+ ];
375
+ const checks = projectChecks(trace);
376
+ const completion = projectCompletion(audit, ownership);
377
+ const reasonCodes = projectReasonCodes(
378
+ status.reasonCodes,
379
+ audit.errors,
380
+ trace.integrity.errors,
381
+ ownership.reasonCodes,
382
+ next.reasonCodes,
383
+ );
384
+ const auditStatus = safeIdentifier(audit.status) ?? "INVALID";
385
+ const reportVerdict = safeIdentifier(report.verdict);
386
+ const traceValid = trace.integrity.valid === true;
387
+ const historyValid = history.integrity.valid === true;
388
+ return {
389
+ schemaVersion: AUDIT_UX_SCHEMA_VERSION,
390
+ protocolVersion: trace.protocolVersion,
391
+ taskId: trace.task.id,
392
+ readOnly: true,
393
+ authority: {
394
+ readOnly: true,
395
+ lifecycleAuthority: false,
396
+ evidenceAuthority: false,
397
+ completionAuthority: false,
398
+ mutationAuthority: false,
399
+ externalExecution: false,
400
+ },
401
+ health: {
402
+ phase: safePhase(status.phase ?? trace.task.phase),
403
+ protocolValid: status.protocol?.status === "VALID",
404
+ auditStatus,
405
+ reportVerdict,
406
+ completionStatus: completion.valid ? "VALID" : "INCOMPLETE",
407
+ ownershipValid: ownership.ownershipValid === true,
408
+ claimState: safeIdentifier(ownership.claimState),
409
+ mutationAllowed: ownership.mutationAllowed === true,
410
+ recoveryStatus: safeIdentifier(ownership.recoveryStatus),
411
+ currentNextAction: safeIdentifier(next.nextAction),
412
+ historyQuality: safeIdentifier(history.historyQuality.level),
413
+ traceIntegrity: traceValid,
414
+ reasonCodes,
415
+ },
416
+ lifecycle: {
417
+ phase: safePhase(trace.task.phase),
418
+ status: safeIdentifier(trace.task.status),
419
+ revision: Number.isInteger(trace.task.revision) ? trace.task.revision : null,
420
+ verificationCycle: Number.isInteger(trace.task.verificationCycle) ? trace.task.verificationCycle : null,
421
+ nextAction: safeIdentifier(next.nextAction),
422
+ terminal: next.terminal === true,
423
+ },
424
+ timeline,
425
+ verification: {
426
+ checks,
427
+ checkCount: trace.checks.length,
428
+ totalAttempts: trace.checks.reduce((total, check) => total + check.attemptCount, 0),
429
+ failedAttempts: trace.checks.reduce((total, check) => total + check.failedAttempts, 0),
430
+ coverage: completion.coverage,
431
+ },
432
+ ownership: {
433
+ claimState: safeIdentifier(ownership.claimState),
434
+ ownershipValid: ownership.ownershipValid === true,
435
+ mutationAllowed: ownership.mutationAllowed === true,
436
+ recoveryStatus: safeIdentifier(ownership.recoveryStatus),
437
+ historicalWriteClaims: ownership.historicalWriteClaims.slice(0, AUDIT_UX_LIMITS.maxActions).map(safeText).filter(Boolean),
438
+ effectiveWriteClaims: ownership.effectiveWriteClaims.slice(0, AUDIT_UX_LIMITS.maxActions).map(safeText).filter(Boolean),
439
+ reasonCodes: projectReasonCodes(ownership.reasonCodes, ownership.ownershipErrors),
440
+ },
441
+ diagnostics: projectDiagnostics(trace),
442
+ completion,
443
+ integrity: {
444
+ valid: auditStatus === "VALID" && traceValid && historyValid,
445
+ auditStatus,
446
+ traceValid,
447
+ historyValid,
448
+ reasonCodes: projectReasonCodes(integrityErrors),
449
+ },
450
+ history: {
451
+ quality: safeIdentifier(history.historyQuality.level),
452
+ totalEventCount: history.summary.totalEventCount,
453
+ returnedEventCount: timeline.items.length,
454
+ truncated: timeline.truncated,
455
+ },
456
+ actions: {
457
+ ...projectActions(trace.actions),
458
+ bounded: trace.actions.total > AUDIT_UX_LIMITS.maxActions,
459
+ },
460
+ approvals: projectApprovals(approvals),
461
+ recovery: {
462
+ active: ownership.recoveryStatus === "RELEASED_BY_RECOVERY",
463
+ status: safeIdentifier(ownership.recoveryStatus),
464
+ events: trace.recovery.slice(0, AUDIT_UX_LIMITS.maxDiagnostics).map((event) => ({
465
+ sequence: event.sequence,
466
+ timestamp: typeof event.at === "string" ? event.at : null,
467
+ kind: safeIdentifier(event.type),
468
+ })),
469
+ },
470
+ links: {
471
+ status: "task/status",
472
+ ownership: "task/ownership",
473
+ contract: "task/contract",
474
+ auditView: "task/audit-view",
475
+ },
476
+ };
477
+ }
478
+
479
+ export async function buildAuditUxView({
480
+ target,
481
+ packageRoot,
482
+ taskId,
483
+ limit,
484
+ beforeSequence,
485
+ afterSequence,
486
+ categories,
487
+ authorityContext,
488
+ runtimeContext,
489
+ } = {}) {
490
+ if (typeof taskId !== "string" || !taskId.trim()) {
491
+ throw auditUxError("E_TASK_REQUIRED", "Resource task/audit-view requires a taskId");
492
+ }
493
+ const options = normalizeOptions({ limit, beforeSequence, afterSequence, categories });
494
+ const task = await findTaskById(target, taskId, packageRoot);
495
+ if (!task) throw auditUxError("E_TASK_NOT_FOUND", `Task ${taskId} does not exist`);
496
+
497
+ const [status, audit, report, history, trace, ownership, next, approvals] = await Promise.all([
498
+ runStatus({ target, packageRoot, taskId }),
499
+ evaluateAudit({ target, packageRoot, taskId, authorityContext, runtimeContext }),
500
+ runReport({ target, packageRoot, taskId }),
501
+ buildTaskHistory({ target, packageRoot, taskId }),
502
+ buildTaskTrace({ target, packageRoot, taskId }),
503
+ resolveTaskClaimState(target, { packageRoot, taskId }),
504
+ runNext({ target, packageRoot, taskId, authorityContext, runtimeContext, compact: false }),
505
+ listApprovals(target, { packageRoot, taskId }),
506
+ ]);
507
+ const timeline = projectTimeline(trace.events, options);
508
+ const projection = buildProjection({ status, audit, report, history, trace, ownership, next, approvals, timeline });
509
+ const serializedBytes = Buffer.byteLength(JSON.stringify(projection), "utf8");
510
+ if (serializedBytes > AUDIT_UX_LIMITS.maxOutputBytes || serializedBytes > INTEGRATION_LIMITS.maxOutputBytes) {
511
+ throw auditUxError("E_AUDIT_UX_OUTPUT_LIMIT", "Audit UX projection exceeds its bounded output limit");
512
+ }
513
+ return projection;
514
+ }
package/src/core/audit.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
1
2
  import { ARTIFACT_PATHS } from "./artifacts.js";
2
3
  import { evaluateCompletion } from "./completion.js";
3
4
  import { readManifest } from "./manifest.js";
@@ -108,7 +109,11 @@ async function evaluateLocalAttestation({ target, packageRoot, taskId, completio
108
109
  return { ...base, status: "VALID" };
109
110
  }
110
111
 
111
- export async function evaluateAudit({
112
+ export async function evaluateAudit(options = {}) {
113
+ return withProjectReadSnapshot(options.target, () => evaluateSelectedAudit(options));
114
+ }
115
+
116
+ async function evaluateSelectedAudit({
112
117
  target,
113
118
  packageRoot,
114
119
  strict = false,