@kontourai/flow-agents 3.12.0 → 4.0.0

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 (1029) hide show
  1. package/.github/workflows/ci.yml +76 -0
  2. package/CHANGELOG.md +48 -0
  3. package/build/generated/capability-declarations.json +177 -0
  4. package/build/src/builder-flow-runtime.d.ts +37 -0
  5. package/build/src/builder-flow-runtime.js +71 -1
  6. package/build/src/builder-gate-action-envelope.d.ts +59 -7
  7. package/build/src/builder-gate-action-envelope.js +163 -30
  8. package/build/src/builder-lifecycle-authority.d.ts +26 -0
  9. package/build/src/builder-lifecycle-authority.js +38 -0
  10. package/build/src/cli/builder-run.js +71 -2
  11. package/build/src/cli/narrative-render.d.ts +95 -0
  12. package/build/src/cli/narrative-render.js +466 -0
  13. package/build/src/cli/narrative-sources.d.ts +2 -0
  14. package/build/src/cli/narrative-sources.js +282 -0
  15. package/build/src/cli/public-contracts.d.ts +257 -0
  16. package/build/src/cli/public-contracts.js +87 -0
  17. package/build/src/cli/workflow-sidecar.d.ts +28 -12
  18. package/build/src/cli/workflow-sidecar.js +599 -180
  19. package/build/src/cli/workflow.js +11 -1
  20. package/build/src/cli.js +8 -0
  21. package/build/src/continuation-driver.d.ts +12 -0
  22. package/build/src/continuation-driver.js +16 -2
  23. package/build/src/continuation-validation.js +349 -3
  24. package/build/src/flow-kit/action-metadata.js +21 -1
  25. package/build/src/index.d.ts +33 -2
  26. package/build/src/index.js +24 -0
  27. package/build/src/lib/capability-declarations.d.ts +83 -0
  28. package/build/src/lib/capability-declarations.js +209 -0
  29. package/build/src/narrative/envelope.d.ts +111 -0
  30. package/build/src/narrative/envelope.js +590 -0
  31. package/build/src/narrative/eval-result.d.ts +60 -0
  32. package/build/src/narrative/eval-result.js +158 -0
  33. package/build/src/narrative/grounding-validator.d.ts +104 -0
  34. package/build/src/narrative/grounding-validator.js +472 -0
  35. package/build/src/narrative/integrity.d.ts +17 -0
  36. package/build/src/narrative/integrity.js +39 -0
  37. package/build/src/narrative/intent-economics.d.ts +42 -0
  38. package/build/src/narrative/intent-economics.js +82 -0
  39. package/build/src/narrative/intent.d.ts +94 -0
  40. package/build/src/narrative/intent.js +132 -0
  41. package/build/src/narrative/policy-filter.d.ts +15 -0
  42. package/build/src/narrative/policy-filter.js +58 -0
  43. package/build/src/narrative/projection.d.ts +58 -0
  44. package/build/src/narrative/projection.js +502 -0
  45. package/build/src/narrative/readers.d.ts +84 -0
  46. package/build/src/narrative/readers.js +406 -0
  47. package/build/src/narrative/render.d.ts +3 -0
  48. package/build/src/narrative/render.js +83 -0
  49. package/build/src/narrative/resolver.d.ts +52 -0
  50. package/build/src/narrative/resolver.js +190 -0
  51. package/build/src/narrative/snapshot.d.ts +90 -0
  52. package/build/src/narrative/snapshot.js +325 -0
  53. package/build/src/narrative/source-ids.d.ts +121 -0
  54. package/build/src/narrative/source-ids.js +201 -0
  55. package/build/src/narrative/statements.d.ts +84 -0
  56. package/build/src/narrative/statements.js +347 -0
  57. package/build/src/narrative/turn-spine.d.ts +19 -0
  58. package/build/src/narrative/turn-spine.js +73 -0
  59. package/build/src/tools/build-universal-bundles.js +8 -0
  60. package/build/src/tools/generate-capability-matrix.d.ts +7 -0
  61. package/build/src/tools/generate-capability-matrix.js +207 -0
  62. package/build/src/tools/validate-source-tree.js +5 -1
  63. package/context/scripts/hooks/stop-goal-fit.js +182 -9
  64. package/context/scripts/hooks/workflow-steering.js +160 -19
  65. package/context/scripts/telemetry/lib/session.sh +3 -0
  66. package/context/scripts/telemetry/lib/transport.sh +12 -3
  67. package/context/scripts/telemetry/telemetry.sh +480 -59
  68. package/dist/base/build/generated/capability-declarations.json +177 -0
  69. package/dist/base/build/package.json +1 -1
  70. package/dist/base/build/src/builder-flow-runtime.d.ts +37 -0
  71. package/dist/base/build/src/builder-flow-runtime.js +71 -1
  72. package/dist/base/build/src/builder-gate-action-envelope.d.ts +59 -7
  73. package/dist/base/build/src/builder-gate-action-envelope.js +163 -30
  74. package/dist/base/build/src/builder-lifecycle-authority.d.ts +26 -0
  75. package/dist/base/build/src/builder-lifecycle-authority.js +38 -0
  76. package/dist/base/build/src/cli/builder-run.js +71 -2
  77. package/dist/base/build/src/cli/narrative-render.d.ts +95 -0
  78. package/dist/base/build/src/cli/narrative-render.js +466 -0
  79. package/dist/base/build/src/cli/narrative-sources.d.ts +2 -0
  80. package/dist/base/build/src/cli/narrative-sources.js +282 -0
  81. package/dist/base/build/src/cli/public-contracts.d.ts +257 -0
  82. package/dist/base/build/src/cli/public-contracts.js +87 -0
  83. package/dist/base/build/src/cli/workflow-sidecar.d.ts +28 -12
  84. package/dist/base/build/src/cli/workflow-sidecar.js +599 -180
  85. package/dist/base/build/src/cli/workflow.js +11 -1
  86. package/dist/base/build/src/cli.js +8 -0
  87. package/dist/base/build/src/continuation-driver.d.ts +12 -0
  88. package/dist/base/build/src/continuation-driver.js +16 -2
  89. package/dist/base/build/src/continuation-validation.js +349 -3
  90. package/dist/base/build/src/flow-kit/action-metadata.js +21 -1
  91. package/dist/base/build/src/index.d.ts +33 -2
  92. package/dist/base/build/src/index.js +24 -0
  93. package/dist/base/build/src/lib/capability-declarations.d.ts +83 -0
  94. package/dist/base/build/src/lib/capability-declarations.js +209 -0
  95. package/dist/base/build/src/narrative/envelope.d.ts +111 -0
  96. package/dist/base/build/src/narrative/envelope.js +590 -0
  97. package/dist/base/build/src/narrative/eval-result.d.ts +60 -0
  98. package/dist/base/build/src/narrative/eval-result.js +158 -0
  99. package/dist/base/build/src/narrative/grounding-validator.d.ts +104 -0
  100. package/dist/base/build/src/narrative/grounding-validator.js +472 -0
  101. package/dist/base/build/src/narrative/integrity.d.ts +17 -0
  102. package/dist/base/build/src/narrative/integrity.js +39 -0
  103. package/dist/base/build/src/narrative/intent-economics.d.ts +42 -0
  104. package/dist/base/build/src/narrative/intent-economics.js +82 -0
  105. package/dist/base/build/src/narrative/intent.d.ts +94 -0
  106. package/dist/base/build/src/narrative/intent.js +132 -0
  107. package/dist/base/build/src/narrative/policy-filter.d.ts +15 -0
  108. package/dist/base/build/src/narrative/policy-filter.js +58 -0
  109. package/dist/base/build/src/narrative/projection.d.ts +58 -0
  110. package/dist/base/build/src/narrative/projection.js +502 -0
  111. package/dist/base/build/src/narrative/readers.d.ts +84 -0
  112. package/dist/base/build/src/narrative/readers.js +406 -0
  113. package/dist/base/build/src/narrative/render.d.ts +3 -0
  114. package/dist/base/build/src/narrative/render.js +83 -0
  115. package/dist/base/build/src/narrative/resolver.d.ts +52 -0
  116. package/dist/base/build/src/narrative/resolver.js +190 -0
  117. package/dist/base/build/src/narrative/snapshot.d.ts +90 -0
  118. package/dist/base/build/src/narrative/snapshot.js +325 -0
  119. package/dist/base/build/src/narrative/source-ids.d.ts +121 -0
  120. package/dist/base/build/src/narrative/source-ids.js +201 -0
  121. package/dist/base/build/src/narrative/statements.d.ts +84 -0
  122. package/dist/base/build/src/narrative/statements.js +347 -0
  123. package/dist/base/build/src/narrative/turn-spine.d.ts +19 -0
  124. package/dist/base/build/src/narrative/turn-spine.js +73 -0
  125. package/dist/base/build/src/tools/build-universal-bundles.js +8 -0
  126. package/dist/base/build/src/tools/generate-capability-matrix.d.ts +7 -0
  127. package/dist/base/build/src/tools/generate-capability-matrix.js +207 -0
  128. package/dist/base/build/src/tools/validate-source-tree.js +5 -1
  129. package/dist/base/context/scripts/hooks/stop-goal-fit.js +182 -9
  130. package/dist/base/context/scripts/hooks/workflow-steering.js +160 -19
  131. package/dist/base/context/scripts/telemetry/lib/session.sh +3 -0
  132. package/dist/base/context/scripts/telemetry/lib/transport.sh +12 -3
  133. package/dist/base/context/scripts/telemetry/telemetry.sh +480 -59
  134. package/dist/base/docs/context-map.md +4 -0
  135. package/dist/base/docs/decisions/index.md +1 -0
  136. package/dist/base/docs/decisions/writer-observed-execution.md +51 -0
  137. package/dist/base/docs/fixture-ownership.md +5 -1
  138. package/dist/base/docs/migrations.md +20 -0
  139. package/dist/base/docs/public-workflow-cli.md +9 -0
  140. package/dist/base/docs/spec/builder-flow-runtime.md +49 -8
  141. package/dist/base/docs/spec/runtime-hook-surface.md +29 -1
  142. package/dist/base/docs/specs/economics-record-contract.md +1 -1
  143. package/dist/base/docs/specs/harness-capability-matrix.md +63 -21
  144. package/dist/base/docs/workflow-usage-guide.md +4 -1
  145. package/dist/base/evals/ci/run-baseline.sh +38 -0
  146. package/dist/base/evals/fixtures/narrative-evals/README.md +62 -0
  147. package/dist/base/evals/fixtures/narrative-evals/corpus.json +129 -0
  148. package/dist/base/evals/fixtures/narrative-evals/scorer.mjs +522 -0
  149. package/dist/base/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
  150. package/dist/base/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
  151. package/dist/base/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
  152. package/dist/base/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
  153. package/dist/base/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
  154. package/dist/base/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
  155. package/dist/base/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
  156. package/dist/base/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
  157. package/dist/base/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
  158. package/dist/base/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
  159. package/dist/base/evals/fixtures/narrative-sources/repo/created.txt +1 -0
  160. package/dist/base/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
  161. package/dist/base/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
  162. package/dist/base/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
  163. package/dist/base/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
  164. package/dist/base/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
  165. package/dist/base/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
  166. package/dist/base/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
  167. package/dist/base/evals/integration/test_capability_declarations.sh +183 -0
  168. package/dist/base/evals/integration/test_economics_record.sh +42 -0
  169. package/dist/base/evals/integration/test_fixture_retirement_audit.sh +2 -2
  170. package/dist/base/evals/integration/test_gate_review_inquiry_records.sh +42 -35
  171. package/dist/base/evals/integration/test_goal_fit_hook.sh +13 -5
  172. package/dist/base/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
  173. package/dist/base/evals/integration/test_narrative_evals.sh +174 -0
  174. package/dist/base/evals/integration/test_narrative_grounded_envelope.sh +280 -0
  175. package/dist/base/evals/integration/test_narrative_grounding_validator.sh +166 -0
  176. package/dist/base/evals/integration/test_narrative_intent_annotation.sh +205 -0
  177. package/dist/base/evals/integration/test_narrative_prose_renderer.sh +239 -0
  178. package/dist/base/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
  179. package/dist/base/evals/integration/test_narrative_runtime_projection.sh +146 -0
  180. package/dist/base/evals/integration/test_narrative_source_contract.sh +228 -0
  181. package/dist/base/evals/integration/test_narrative_trust_isolation.sh +408 -0
  182. package/dist/base/evals/integration/test_plain_stop_messaging.sh +91 -0
  183. package/dist/base/evals/integration/test_session_resume_roundtrip.sh +228 -0
  184. package/dist/base/evals/integration/test_telemetry_action_class.sh +265 -0
  185. package/dist/base/evals/integration/test_telemetry_delegation.sh +291 -0
  186. package/dist/base/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
  187. package/dist/base/evals/integration/test_telemetry_task_slug.sh +158 -0
  188. package/dist/base/evals/integration/test_telemetry_tool_outcome.sh +279 -0
  189. package/dist/base/evals/integration/test_workflow_sidecar_writer.sh +111 -2
  190. package/dist/base/evals/run.sh +12 -0
  191. package/dist/base/evals/static/test_library_exports.sh +5 -0
  192. package/dist/base/install.sh +1 -1
  193. package/dist/base/schemas/grounded-execution-narrative.schema.json +292 -0
  194. package/dist/base/schemas/narrative-eval-result.schema.json +205 -0
  195. package/dist/base/schemas/narrative-runtime-projection.schema.json +134 -0
  196. package/dist/base/schemas/narrative-source-manifest.schema.json +294 -0
  197. package/dist/base/scripts/check-hachure-boundary.mjs +81 -0
  198. package/dist/base/scripts/hooks/stop-goal-fit.js +182 -9
  199. package/dist/base/scripts/hooks/workflow-steering.js +160 -19
  200. package/dist/base/scripts/telemetry/economics-record.schema.json +1 -1
  201. package/dist/base/scripts/telemetry/economics-record.sh +47 -3
  202. package/dist/base/scripts/telemetry/lib/session.sh +3 -0
  203. package/dist/base/scripts/telemetry/lib/transport.sh +12 -3
  204. package/dist/base/scripts/telemetry/telemetry.sh +480 -59
  205. package/dist/claude-code/build/generated/capability-declarations.json +177 -0
  206. package/dist/claude-code/build/package.json +1 -1
  207. package/dist/claude-code/build/src/builder-flow-runtime.d.ts +37 -0
  208. package/dist/claude-code/build/src/builder-flow-runtime.js +71 -1
  209. package/dist/claude-code/build/src/builder-gate-action-envelope.d.ts +59 -7
  210. package/dist/claude-code/build/src/builder-gate-action-envelope.js +163 -30
  211. package/dist/claude-code/build/src/builder-lifecycle-authority.d.ts +26 -0
  212. package/dist/claude-code/build/src/builder-lifecycle-authority.js +38 -0
  213. package/dist/claude-code/build/src/cli/builder-run.js +71 -2
  214. package/dist/claude-code/build/src/cli/narrative-render.d.ts +95 -0
  215. package/dist/claude-code/build/src/cli/narrative-render.js +466 -0
  216. package/dist/claude-code/build/src/cli/narrative-sources.d.ts +2 -0
  217. package/dist/claude-code/build/src/cli/narrative-sources.js +282 -0
  218. package/dist/claude-code/build/src/cli/public-contracts.d.ts +257 -0
  219. package/dist/claude-code/build/src/cli/public-contracts.js +87 -0
  220. package/dist/claude-code/build/src/cli/workflow-sidecar.d.ts +28 -12
  221. package/dist/claude-code/build/src/cli/workflow-sidecar.js +599 -180
  222. package/dist/claude-code/build/src/cli/workflow.js +11 -1
  223. package/dist/claude-code/build/src/cli.js +8 -0
  224. package/dist/claude-code/build/src/continuation-driver.d.ts +12 -0
  225. package/dist/claude-code/build/src/continuation-driver.js +16 -2
  226. package/dist/claude-code/build/src/continuation-validation.js +349 -3
  227. package/dist/claude-code/build/src/flow-kit/action-metadata.js +21 -1
  228. package/dist/claude-code/build/src/index.d.ts +33 -2
  229. package/dist/claude-code/build/src/index.js +24 -0
  230. package/dist/claude-code/build/src/lib/capability-declarations.d.ts +83 -0
  231. package/dist/claude-code/build/src/lib/capability-declarations.js +209 -0
  232. package/dist/claude-code/build/src/narrative/envelope.d.ts +111 -0
  233. package/dist/claude-code/build/src/narrative/envelope.js +590 -0
  234. package/dist/claude-code/build/src/narrative/eval-result.d.ts +60 -0
  235. package/dist/claude-code/build/src/narrative/eval-result.js +158 -0
  236. package/dist/claude-code/build/src/narrative/grounding-validator.d.ts +104 -0
  237. package/dist/claude-code/build/src/narrative/grounding-validator.js +472 -0
  238. package/dist/claude-code/build/src/narrative/integrity.d.ts +17 -0
  239. package/dist/claude-code/build/src/narrative/integrity.js +39 -0
  240. package/dist/claude-code/build/src/narrative/intent-economics.d.ts +42 -0
  241. package/dist/claude-code/build/src/narrative/intent-economics.js +82 -0
  242. package/dist/claude-code/build/src/narrative/intent.d.ts +94 -0
  243. package/dist/claude-code/build/src/narrative/intent.js +132 -0
  244. package/dist/claude-code/build/src/narrative/policy-filter.d.ts +15 -0
  245. package/dist/claude-code/build/src/narrative/policy-filter.js +58 -0
  246. package/dist/claude-code/build/src/narrative/projection.d.ts +58 -0
  247. package/dist/claude-code/build/src/narrative/projection.js +502 -0
  248. package/dist/claude-code/build/src/narrative/readers.d.ts +84 -0
  249. package/dist/claude-code/build/src/narrative/readers.js +406 -0
  250. package/dist/claude-code/build/src/narrative/render.d.ts +3 -0
  251. package/dist/claude-code/build/src/narrative/render.js +83 -0
  252. package/dist/claude-code/build/src/narrative/resolver.d.ts +52 -0
  253. package/dist/claude-code/build/src/narrative/resolver.js +190 -0
  254. package/dist/claude-code/build/src/narrative/snapshot.d.ts +90 -0
  255. package/dist/claude-code/build/src/narrative/snapshot.js +325 -0
  256. package/dist/claude-code/build/src/narrative/source-ids.d.ts +121 -0
  257. package/dist/claude-code/build/src/narrative/source-ids.js +201 -0
  258. package/dist/claude-code/build/src/narrative/statements.d.ts +84 -0
  259. package/dist/claude-code/build/src/narrative/statements.js +347 -0
  260. package/dist/claude-code/build/src/narrative/turn-spine.d.ts +19 -0
  261. package/dist/claude-code/build/src/narrative/turn-spine.js +73 -0
  262. package/dist/claude-code/build/src/tools/build-universal-bundles.js +8 -0
  263. package/dist/claude-code/build/src/tools/generate-capability-matrix.d.ts +7 -0
  264. package/dist/claude-code/build/src/tools/generate-capability-matrix.js +207 -0
  265. package/dist/claude-code/build/src/tools/validate-source-tree.js +5 -1
  266. package/dist/claude-code/context/scripts/hooks/stop-goal-fit.js +182 -9
  267. package/dist/claude-code/context/scripts/hooks/workflow-steering.js +160 -19
  268. package/dist/claude-code/context/scripts/telemetry/lib/session.sh +3 -0
  269. package/dist/claude-code/context/scripts/telemetry/lib/transport.sh +12 -3
  270. package/dist/claude-code/context/scripts/telemetry/telemetry.sh +480 -59
  271. package/dist/claude-code/docs/context-map.md +4 -0
  272. package/dist/claude-code/docs/decisions/index.md +1 -0
  273. package/dist/claude-code/docs/decisions/writer-observed-execution.md +51 -0
  274. package/dist/claude-code/docs/fixture-ownership.md +5 -1
  275. package/dist/claude-code/docs/migrations.md +20 -0
  276. package/dist/claude-code/docs/public-workflow-cli.md +9 -0
  277. package/dist/claude-code/docs/spec/builder-flow-runtime.md +49 -8
  278. package/dist/claude-code/docs/spec/runtime-hook-surface.md +29 -1
  279. package/dist/claude-code/docs/specs/economics-record-contract.md +1 -1
  280. package/dist/claude-code/docs/specs/harness-capability-matrix.md +63 -21
  281. package/dist/claude-code/docs/workflow-usage-guide.md +4 -1
  282. package/dist/claude-code/evals/ci/run-baseline.sh +38 -0
  283. package/dist/claude-code/evals/fixtures/narrative-evals/README.md +62 -0
  284. package/dist/claude-code/evals/fixtures/narrative-evals/corpus.json +129 -0
  285. package/dist/claude-code/evals/fixtures/narrative-evals/scorer.mjs +522 -0
  286. package/dist/claude-code/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
  287. package/dist/claude-code/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
  288. package/dist/claude-code/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
  289. package/dist/claude-code/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
  290. package/dist/claude-code/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
  291. package/dist/claude-code/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
  292. package/dist/claude-code/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
  293. package/dist/claude-code/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
  294. package/dist/claude-code/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
  295. package/dist/claude-code/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
  296. package/dist/claude-code/evals/fixtures/narrative-sources/repo/created.txt +1 -0
  297. package/dist/claude-code/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
  298. package/dist/claude-code/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
  299. package/dist/claude-code/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
  300. package/dist/claude-code/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
  301. package/dist/claude-code/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
  302. package/dist/claude-code/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
  303. package/dist/claude-code/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
  304. package/dist/claude-code/evals/integration/test_capability_declarations.sh +183 -0
  305. package/dist/claude-code/evals/integration/test_economics_record.sh +42 -0
  306. package/dist/claude-code/evals/integration/test_fixture_retirement_audit.sh +2 -2
  307. package/dist/claude-code/evals/integration/test_gate_review_inquiry_records.sh +42 -35
  308. package/dist/claude-code/evals/integration/test_goal_fit_hook.sh +13 -5
  309. package/dist/claude-code/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
  310. package/dist/claude-code/evals/integration/test_narrative_evals.sh +174 -0
  311. package/dist/claude-code/evals/integration/test_narrative_grounded_envelope.sh +280 -0
  312. package/dist/claude-code/evals/integration/test_narrative_grounding_validator.sh +166 -0
  313. package/dist/claude-code/evals/integration/test_narrative_intent_annotation.sh +205 -0
  314. package/dist/claude-code/evals/integration/test_narrative_prose_renderer.sh +239 -0
  315. package/dist/claude-code/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
  316. package/dist/claude-code/evals/integration/test_narrative_runtime_projection.sh +146 -0
  317. package/dist/claude-code/evals/integration/test_narrative_source_contract.sh +228 -0
  318. package/dist/claude-code/evals/integration/test_narrative_trust_isolation.sh +408 -0
  319. package/dist/claude-code/evals/integration/test_plain_stop_messaging.sh +91 -0
  320. package/dist/claude-code/evals/integration/test_session_resume_roundtrip.sh +228 -0
  321. package/dist/claude-code/evals/integration/test_telemetry_action_class.sh +265 -0
  322. package/dist/claude-code/evals/integration/test_telemetry_delegation.sh +291 -0
  323. package/dist/claude-code/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
  324. package/dist/claude-code/evals/integration/test_telemetry_task_slug.sh +158 -0
  325. package/dist/claude-code/evals/integration/test_telemetry_tool_outcome.sh +279 -0
  326. package/dist/claude-code/evals/integration/test_workflow_sidecar_writer.sh +111 -2
  327. package/dist/claude-code/evals/run.sh +12 -0
  328. package/dist/claude-code/evals/static/test_library_exports.sh +5 -0
  329. package/dist/claude-code/install.sh +1 -1
  330. package/dist/claude-code/schemas/grounded-execution-narrative.schema.json +292 -0
  331. package/dist/claude-code/schemas/narrative-eval-result.schema.json +205 -0
  332. package/dist/claude-code/schemas/narrative-runtime-projection.schema.json +134 -0
  333. package/dist/claude-code/schemas/narrative-source-manifest.schema.json +294 -0
  334. package/dist/claude-code/scripts/check-hachure-boundary.mjs +81 -0
  335. package/dist/claude-code/scripts/hooks/stop-goal-fit.js +182 -9
  336. package/dist/claude-code/scripts/hooks/workflow-steering.js +160 -19
  337. package/dist/claude-code/scripts/telemetry/economics-record.schema.json +1 -1
  338. package/dist/claude-code/scripts/telemetry/economics-record.sh +47 -3
  339. package/dist/claude-code/scripts/telemetry/lib/session.sh +3 -0
  340. package/dist/claude-code/scripts/telemetry/lib/transport.sh +12 -3
  341. package/dist/claude-code/scripts/telemetry/telemetry.sh +480 -59
  342. package/dist/codex/build/generated/capability-declarations.json +177 -0
  343. package/dist/codex/build/package.json +1 -1
  344. package/dist/codex/build/src/builder-flow-runtime.d.ts +37 -0
  345. package/dist/codex/build/src/builder-flow-runtime.js +71 -1
  346. package/dist/codex/build/src/builder-gate-action-envelope.d.ts +59 -7
  347. package/dist/codex/build/src/builder-gate-action-envelope.js +163 -30
  348. package/dist/codex/build/src/builder-lifecycle-authority.d.ts +26 -0
  349. package/dist/codex/build/src/builder-lifecycle-authority.js +38 -0
  350. package/dist/codex/build/src/cli/builder-run.js +71 -2
  351. package/dist/codex/build/src/cli/narrative-render.d.ts +95 -0
  352. package/dist/codex/build/src/cli/narrative-render.js +466 -0
  353. package/dist/codex/build/src/cli/narrative-sources.d.ts +2 -0
  354. package/dist/codex/build/src/cli/narrative-sources.js +282 -0
  355. package/dist/codex/build/src/cli/public-contracts.d.ts +257 -0
  356. package/dist/codex/build/src/cli/public-contracts.js +87 -0
  357. package/dist/codex/build/src/cli/workflow-sidecar.d.ts +28 -12
  358. package/dist/codex/build/src/cli/workflow-sidecar.js +599 -180
  359. package/dist/codex/build/src/cli/workflow.js +11 -1
  360. package/dist/codex/build/src/cli.js +8 -0
  361. package/dist/codex/build/src/continuation-driver.d.ts +12 -0
  362. package/dist/codex/build/src/continuation-driver.js +16 -2
  363. package/dist/codex/build/src/continuation-validation.js +349 -3
  364. package/dist/codex/build/src/flow-kit/action-metadata.js +21 -1
  365. package/dist/codex/build/src/index.d.ts +33 -2
  366. package/dist/codex/build/src/index.js +24 -0
  367. package/dist/codex/build/src/lib/capability-declarations.d.ts +83 -0
  368. package/dist/codex/build/src/lib/capability-declarations.js +209 -0
  369. package/dist/codex/build/src/narrative/envelope.d.ts +111 -0
  370. package/dist/codex/build/src/narrative/envelope.js +590 -0
  371. package/dist/codex/build/src/narrative/eval-result.d.ts +60 -0
  372. package/dist/codex/build/src/narrative/eval-result.js +158 -0
  373. package/dist/codex/build/src/narrative/grounding-validator.d.ts +104 -0
  374. package/dist/codex/build/src/narrative/grounding-validator.js +472 -0
  375. package/dist/codex/build/src/narrative/integrity.d.ts +17 -0
  376. package/dist/codex/build/src/narrative/integrity.js +39 -0
  377. package/dist/codex/build/src/narrative/intent-economics.d.ts +42 -0
  378. package/dist/codex/build/src/narrative/intent-economics.js +82 -0
  379. package/dist/codex/build/src/narrative/intent.d.ts +94 -0
  380. package/dist/codex/build/src/narrative/intent.js +132 -0
  381. package/dist/codex/build/src/narrative/policy-filter.d.ts +15 -0
  382. package/dist/codex/build/src/narrative/policy-filter.js +58 -0
  383. package/dist/codex/build/src/narrative/projection.d.ts +58 -0
  384. package/dist/codex/build/src/narrative/projection.js +502 -0
  385. package/dist/codex/build/src/narrative/readers.d.ts +84 -0
  386. package/dist/codex/build/src/narrative/readers.js +406 -0
  387. package/dist/codex/build/src/narrative/render.d.ts +3 -0
  388. package/dist/codex/build/src/narrative/render.js +83 -0
  389. package/dist/codex/build/src/narrative/resolver.d.ts +52 -0
  390. package/dist/codex/build/src/narrative/resolver.js +190 -0
  391. package/dist/codex/build/src/narrative/snapshot.d.ts +90 -0
  392. package/dist/codex/build/src/narrative/snapshot.js +325 -0
  393. package/dist/codex/build/src/narrative/source-ids.d.ts +121 -0
  394. package/dist/codex/build/src/narrative/source-ids.js +201 -0
  395. package/dist/codex/build/src/narrative/statements.d.ts +84 -0
  396. package/dist/codex/build/src/narrative/statements.js +347 -0
  397. package/dist/codex/build/src/narrative/turn-spine.d.ts +19 -0
  398. package/dist/codex/build/src/narrative/turn-spine.js +73 -0
  399. package/dist/codex/build/src/tools/build-universal-bundles.js +8 -0
  400. package/dist/codex/build/src/tools/generate-capability-matrix.d.ts +7 -0
  401. package/dist/codex/build/src/tools/generate-capability-matrix.js +207 -0
  402. package/dist/codex/build/src/tools/validate-source-tree.js +5 -1
  403. package/dist/codex/context/scripts/hooks/stop-goal-fit.js +182 -9
  404. package/dist/codex/context/scripts/hooks/workflow-steering.js +160 -19
  405. package/dist/codex/context/scripts/telemetry/lib/session.sh +3 -0
  406. package/dist/codex/context/scripts/telemetry/lib/transport.sh +12 -3
  407. package/dist/codex/context/scripts/telemetry/telemetry.sh +480 -59
  408. package/dist/codex/docs/context-map.md +4 -0
  409. package/dist/codex/docs/decisions/index.md +1 -0
  410. package/dist/codex/docs/decisions/writer-observed-execution.md +51 -0
  411. package/dist/codex/docs/fixture-ownership.md +5 -1
  412. package/dist/codex/docs/migrations.md +20 -0
  413. package/dist/codex/docs/public-workflow-cli.md +9 -0
  414. package/dist/codex/docs/spec/builder-flow-runtime.md +49 -8
  415. package/dist/codex/docs/spec/runtime-hook-surface.md +29 -1
  416. package/dist/codex/docs/specs/economics-record-contract.md +1 -1
  417. package/dist/codex/docs/specs/harness-capability-matrix.md +63 -21
  418. package/dist/codex/docs/workflow-usage-guide.md +4 -1
  419. package/dist/codex/evals/ci/run-baseline.sh +38 -0
  420. package/dist/codex/evals/fixtures/narrative-evals/README.md +62 -0
  421. package/dist/codex/evals/fixtures/narrative-evals/corpus.json +129 -0
  422. package/dist/codex/evals/fixtures/narrative-evals/scorer.mjs +522 -0
  423. package/dist/codex/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
  424. package/dist/codex/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
  425. package/dist/codex/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
  426. package/dist/codex/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
  427. package/dist/codex/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
  428. package/dist/codex/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
  429. package/dist/codex/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
  430. package/dist/codex/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
  431. package/dist/codex/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
  432. package/dist/codex/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
  433. package/dist/codex/evals/fixtures/narrative-sources/repo/created.txt +1 -0
  434. package/dist/codex/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
  435. package/dist/codex/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
  436. package/dist/codex/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
  437. package/dist/codex/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
  438. package/dist/codex/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
  439. package/dist/codex/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
  440. package/dist/codex/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
  441. package/dist/codex/evals/integration/test_capability_declarations.sh +183 -0
  442. package/dist/codex/evals/integration/test_economics_record.sh +42 -0
  443. package/dist/codex/evals/integration/test_fixture_retirement_audit.sh +2 -2
  444. package/dist/codex/evals/integration/test_gate_review_inquiry_records.sh +42 -35
  445. package/dist/codex/evals/integration/test_goal_fit_hook.sh +13 -5
  446. package/dist/codex/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
  447. package/dist/codex/evals/integration/test_narrative_evals.sh +174 -0
  448. package/dist/codex/evals/integration/test_narrative_grounded_envelope.sh +280 -0
  449. package/dist/codex/evals/integration/test_narrative_grounding_validator.sh +166 -0
  450. package/dist/codex/evals/integration/test_narrative_intent_annotation.sh +205 -0
  451. package/dist/codex/evals/integration/test_narrative_prose_renderer.sh +239 -0
  452. package/dist/codex/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
  453. package/dist/codex/evals/integration/test_narrative_runtime_projection.sh +146 -0
  454. package/dist/codex/evals/integration/test_narrative_source_contract.sh +228 -0
  455. package/dist/codex/evals/integration/test_narrative_trust_isolation.sh +408 -0
  456. package/dist/codex/evals/integration/test_plain_stop_messaging.sh +91 -0
  457. package/dist/codex/evals/integration/test_session_resume_roundtrip.sh +228 -0
  458. package/dist/codex/evals/integration/test_telemetry_action_class.sh +265 -0
  459. package/dist/codex/evals/integration/test_telemetry_delegation.sh +291 -0
  460. package/dist/codex/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
  461. package/dist/codex/evals/integration/test_telemetry_task_slug.sh +158 -0
  462. package/dist/codex/evals/integration/test_telemetry_tool_outcome.sh +279 -0
  463. package/dist/codex/evals/integration/test_workflow_sidecar_writer.sh +111 -2
  464. package/dist/codex/evals/run.sh +12 -0
  465. package/dist/codex/evals/static/test_library_exports.sh +5 -0
  466. package/dist/codex/install.sh +1 -1
  467. package/dist/codex/schemas/grounded-execution-narrative.schema.json +292 -0
  468. package/dist/codex/schemas/narrative-eval-result.schema.json +205 -0
  469. package/dist/codex/schemas/narrative-runtime-projection.schema.json +134 -0
  470. package/dist/codex/schemas/narrative-source-manifest.schema.json +294 -0
  471. package/dist/codex/scripts/check-hachure-boundary.mjs +81 -0
  472. package/dist/codex/scripts/hooks/stop-goal-fit.js +182 -9
  473. package/dist/codex/scripts/hooks/workflow-steering.js +160 -19
  474. package/dist/codex/scripts/telemetry/economics-record.schema.json +1 -1
  475. package/dist/codex/scripts/telemetry/economics-record.sh +47 -3
  476. package/dist/codex/scripts/telemetry/lib/session.sh +3 -0
  477. package/dist/codex/scripts/telemetry/lib/transport.sh +12 -3
  478. package/dist/codex/scripts/telemetry/telemetry.sh +480 -59
  479. package/dist/kiro/build/generated/capability-declarations.json +177 -0
  480. package/dist/kiro/build/package.json +1 -1
  481. package/dist/kiro/build/src/builder-flow-runtime.d.ts +37 -0
  482. package/dist/kiro/build/src/builder-flow-runtime.js +71 -1
  483. package/dist/kiro/build/src/builder-gate-action-envelope.d.ts +59 -7
  484. package/dist/kiro/build/src/builder-gate-action-envelope.js +163 -30
  485. package/dist/kiro/build/src/builder-lifecycle-authority.d.ts +26 -0
  486. package/dist/kiro/build/src/builder-lifecycle-authority.js +38 -0
  487. package/dist/kiro/build/src/cli/builder-run.js +71 -2
  488. package/dist/kiro/build/src/cli/narrative-render.d.ts +95 -0
  489. package/dist/kiro/build/src/cli/narrative-render.js +466 -0
  490. package/dist/kiro/build/src/cli/narrative-sources.d.ts +2 -0
  491. package/dist/kiro/build/src/cli/narrative-sources.js +282 -0
  492. package/dist/kiro/build/src/cli/public-contracts.d.ts +257 -0
  493. package/dist/kiro/build/src/cli/public-contracts.js +87 -0
  494. package/dist/kiro/build/src/cli/workflow-sidecar.d.ts +28 -12
  495. package/dist/kiro/build/src/cli/workflow-sidecar.js +599 -180
  496. package/dist/kiro/build/src/cli/workflow.js +11 -1
  497. package/dist/kiro/build/src/cli.js +8 -0
  498. package/dist/kiro/build/src/continuation-driver.d.ts +12 -0
  499. package/dist/kiro/build/src/continuation-driver.js +16 -2
  500. package/dist/kiro/build/src/continuation-validation.js +349 -3
  501. package/dist/kiro/build/src/flow-kit/action-metadata.js +21 -1
  502. package/dist/kiro/build/src/index.d.ts +33 -2
  503. package/dist/kiro/build/src/index.js +24 -0
  504. package/dist/kiro/build/src/lib/capability-declarations.d.ts +83 -0
  505. package/dist/kiro/build/src/lib/capability-declarations.js +209 -0
  506. package/dist/kiro/build/src/narrative/envelope.d.ts +111 -0
  507. package/dist/kiro/build/src/narrative/envelope.js +590 -0
  508. package/dist/kiro/build/src/narrative/eval-result.d.ts +60 -0
  509. package/dist/kiro/build/src/narrative/eval-result.js +158 -0
  510. package/dist/kiro/build/src/narrative/grounding-validator.d.ts +104 -0
  511. package/dist/kiro/build/src/narrative/grounding-validator.js +472 -0
  512. package/dist/kiro/build/src/narrative/integrity.d.ts +17 -0
  513. package/dist/kiro/build/src/narrative/integrity.js +39 -0
  514. package/dist/kiro/build/src/narrative/intent-economics.d.ts +42 -0
  515. package/dist/kiro/build/src/narrative/intent-economics.js +82 -0
  516. package/dist/kiro/build/src/narrative/intent.d.ts +94 -0
  517. package/dist/kiro/build/src/narrative/intent.js +132 -0
  518. package/dist/kiro/build/src/narrative/policy-filter.d.ts +15 -0
  519. package/dist/kiro/build/src/narrative/policy-filter.js +58 -0
  520. package/dist/kiro/build/src/narrative/projection.d.ts +58 -0
  521. package/dist/kiro/build/src/narrative/projection.js +502 -0
  522. package/dist/kiro/build/src/narrative/readers.d.ts +84 -0
  523. package/dist/kiro/build/src/narrative/readers.js +406 -0
  524. package/dist/kiro/build/src/narrative/render.d.ts +3 -0
  525. package/dist/kiro/build/src/narrative/render.js +83 -0
  526. package/dist/kiro/build/src/narrative/resolver.d.ts +52 -0
  527. package/dist/kiro/build/src/narrative/resolver.js +190 -0
  528. package/dist/kiro/build/src/narrative/snapshot.d.ts +90 -0
  529. package/dist/kiro/build/src/narrative/snapshot.js +325 -0
  530. package/dist/kiro/build/src/narrative/source-ids.d.ts +121 -0
  531. package/dist/kiro/build/src/narrative/source-ids.js +201 -0
  532. package/dist/kiro/build/src/narrative/statements.d.ts +84 -0
  533. package/dist/kiro/build/src/narrative/statements.js +347 -0
  534. package/dist/kiro/build/src/narrative/turn-spine.d.ts +19 -0
  535. package/dist/kiro/build/src/narrative/turn-spine.js +73 -0
  536. package/dist/kiro/build/src/tools/build-universal-bundles.js +8 -0
  537. package/dist/kiro/build/src/tools/generate-capability-matrix.d.ts +7 -0
  538. package/dist/kiro/build/src/tools/generate-capability-matrix.js +207 -0
  539. package/dist/kiro/build/src/tools/validate-source-tree.js +5 -1
  540. package/dist/kiro/context/scripts/hooks/stop-goal-fit.js +182 -9
  541. package/dist/kiro/context/scripts/hooks/workflow-steering.js +160 -19
  542. package/dist/kiro/context/scripts/telemetry/lib/session.sh +3 -0
  543. package/dist/kiro/context/scripts/telemetry/lib/transport.sh +12 -3
  544. package/dist/kiro/context/scripts/telemetry/telemetry.sh +480 -59
  545. package/dist/kiro/docs/context-map.md +4 -0
  546. package/dist/kiro/docs/decisions/index.md +1 -0
  547. package/dist/kiro/docs/decisions/writer-observed-execution.md +51 -0
  548. package/dist/kiro/docs/fixture-ownership.md +5 -1
  549. package/dist/kiro/docs/migrations.md +20 -0
  550. package/dist/kiro/docs/public-workflow-cli.md +9 -0
  551. package/dist/kiro/docs/spec/builder-flow-runtime.md +49 -8
  552. package/dist/kiro/docs/spec/runtime-hook-surface.md +29 -1
  553. package/dist/kiro/docs/specs/economics-record-contract.md +1 -1
  554. package/dist/kiro/docs/specs/harness-capability-matrix.md +63 -21
  555. package/dist/kiro/docs/workflow-usage-guide.md +4 -1
  556. package/dist/kiro/evals/ci/run-baseline.sh +38 -0
  557. package/dist/kiro/evals/fixtures/narrative-evals/README.md +62 -0
  558. package/dist/kiro/evals/fixtures/narrative-evals/corpus.json +129 -0
  559. package/dist/kiro/evals/fixtures/narrative-evals/scorer.mjs +522 -0
  560. package/dist/kiro/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
  561. package/dist/kiro/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
  562. package/dist/kiro/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
  563. package/dist/kiro/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
  564. package/dist/kiro/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
  565. package/dist/kiro/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
  566. package/dist/kiro/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
  567. package/dist/kiro/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
  568. package/dist/kiro/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
  569. package/dist/kiro/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
  570. package/dist/kiro/evals/fixtures/narrative-sources/repo/created.txt +1 -0
  571. package/dist/kiro/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
  572. package/dist/kiro/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
  573. package/dist/kiro/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
  574. package/dist/kiro/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
  575. package/dist/kiro/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
  576. package/dist/kiro/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
  577. package/dist/kiro/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
  578. package/dist/kiro/evals/integration/test_capability_declarations.sh +183 -0
  579. package/dist/kiro/evals/integration/test_economics_record.sh +42 -0
  580. package/dist/kiro/evals/integration/test_fixture_retirement_audit.sh +2 -2
  581. package/dist/kiro/evals/integration/test_gate_review_inquiry_records.sh +42 -35
  582. package/dist/kiro/evals/integration/test_goal_fit_hook.sh +13 -5
  583. package/dist/kiro/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
  584. package/dist/kiro/evals/integration/test_narrative_evals.sh +174 -0
  585. package/dist/kiro/evals/integration/test_narrative_grounded_envelope.sh +280 -0
  586. package/dist/kiro/evals/integration/test_narrative_grounding_validator.sh +166 -0
  587. package/dist/kiro/evals/integration/test_narrative_intent_annotation.sh +205 -0
  588. package/dist/kiro/evals/integration/test_narrative_prose_renderer.sh +239 -0
  589. package/dist/kiro/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
  590. package/dist/kiro/evals/integration/test_narrative_runtime_projection.sh +146 -0
  591. package/dist/kiro/evals/integration/test_narrative_source_contract.sh +228 -0
  592. package/dist/kiro/evals/integration/test_narrative_trust_isolation.sh +408 -0
  593. package/dist/kiro/evals/integration/test_plain_stop_messaging.sh +91 -0
  594. package/dist/kiro/evals/integration/test_session_resume_roundtrip.sh +228 -0
  595. package/dist/kiro/evals/integration/test_telemetry_action_class.sh +265 -0
  596. package/dist/kiro/evals/integration/test_telemetry_delegation.sh +291 -0
  597. package/dist/kiro/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
  598. package/dist/kiro/evals/integration/test_telemetry_task_slug.sh +158 -0
  599. package/dist/kiro/evals/integration/test_telemetry_tool_outcome.sh +279 -0
  600. package/dist/kiro/evals/integration/test_workflow_sidecar_writer.sh +111 -2
  601. package/dist/kiro/evals/run.sh +12 -0
  602. package/dist/kiro/evals/static/test_library_exports.sh +5 -0
  603. package/dist/kiro/install.sh +1 -1
  604. package/dist/kiro/schemas/grounded-execution-narrative.schema.json +292 -0
  605. package/dist/kiro/schemas/narrative-eval-result.schema.json +205 -0
  606. package/dist/kiro/schemas/narrative-runtime-projection.schema.json +134 -0
  607. package/dist/kiro/schemas/narrative-source-manifest.schema.json +294 -0
  608. package/dist/kiro/scripts/check-hachure-boundary.mjs +81 -0
  609. package/dist/kiro/scripts/hooks/stop-goal-fit.js +182 -9
  610. package/dist/kiro/scripts/hooks/workflow-steering.js +160 -19
  611. package/dist/kiro/scripts/telemetry/economics-record.schema.json +1 -1
  612. package/dist/kiro/scripts/telemetry/economics-record.sh +47 -3
  613. package/dist/kiro/scripts/telemetry/lib/session.sh +3 -0
  614. package/dist/kiro/scripts/telemetry/lib/transport.sh +12 -3
  615. package/dist/kiro/scripts/telemetry/telemetry.sh +480 -59
  616. package/dist/opencode/build/generated/capability-declarations.json +177 -0
  617. package/dist/opencode/build/package.json +1 -1
  618. package/dist/opencode/build/src/builder-flow-runtime.d.ts +37 -0
  619. package/dist/opencode/build/src/builder-flow-runtime.js +71 -1
  620. package/dist/opencode/build/src/builder-gate-action-envelope.d.ts +59 -7
  621. package/dist/opencode/build/src/builder-gate-action-envelope.js +163 -30
  622. package/dist/opencode/build/src/builder-lifecycle-authority.d.ts +26 -0
  623. package/dist/opencode/build/src/builder-lifecycle-authority.js +38 -0
  624. package/dist/opencode/build/src/cli/builder-run.js +71 -2
  625. package/dist/opencode/build/src/cli/narrative-render.d.ts +95 -0
  626. package/dist/opencode/build/src/cli/narrative-render.js +466 -0
  627. package/dist/opencode/build/src/cli/narrative-sources.d.ts +2 -0
  628. package/dist/opencode/build/src/cli/narrative-sources.js +282 -0
  629. package/dist/opencode/build/src/cli/public-contracts.d.ts +257 -0
  630. package/dist/opencode/build/src/cli/public-contracts.js +87 -0
  631. package/dist/opencode/build/src/cli/workflow-sidecar.d.ts +28 -12
  632. package/dist/opencode/build/src/cli/workflow-sidecar.js +599 -180
  633. package/dist/opencode/build/src/cli/workflow.js +11 -1
  634. package/dist/opencode/build/src/cli.js +8 -0
  635. package/dist/opencode/build/src/continuation-driver.d.ts +12 -0
  636. package/dist/opencode/build/src/continuation-driver.js +16 -2
  637. package/dist/opencode/build/src/continuation-validation.js +349 -3
  638. package/dist/opencode/build/src/flow-kit/action-metadata.js +21 -1
  639. package/dist/opencode/build/src/index.d.ts +33 -2
  640. package/dist/opencode/build/src/index.js +24 -0
  641. package/dist/opencode/build/src/lib/capability-declarations.d.ts +83 -0
  642. package/dist/opencode/build/src/lib/capability-declarations.js +209 -0
  643. package/dist/opencode/build/src/narrative/envelope.d.ts +111 -0
  644. package/dist/opencode/build/src/narrative/envelope.js +590 -0
  645. package/dist/opencode/build/src/narrative/eval-result.d.ts +60 -0
  646. package/dist/opencode/build/src/narrative/eval-result.js +158 -0
  647. package/dist/opencode/build/src/narrative/grounding-validator.d.ts +104 -0
  648. package/dist/opencode/build/src/narrative/grounding-validator.js +472 -0
  649. package/dist/opencode/build/src/narrative/integrity.d.ts +17 -0
  650. package/dist/opencode/build/src/narrative/integrity.js +39 -0
  651. package/dist/opencode/build/src/narrative/intent-economics.d.ts +42 -0
  652. package/dist/opencode/build/src/narrative/intent-economics.js +82 -0
  653. package/dist/opencode/build/src/narrative/intent.d.ts +94 -0
  654. package/dist/opencode/build/src/narrative/intent.js +132 -0
  655. package/dist/opencode/build/src/narrative/policy-filter.d.ts +15 -0
  656. package/dist/opencode/build/src/narrative/policy-filter.js +58 -0
  657. package/dist/opencode/build/src/narrative/projection.d.ts +58 -0
  658. package/dist/opencode/build/src/narrative/projection.js +502 -0
  659. package/dist/opencode/build/src/narrative/readers.d.ts +84 -0
  660. package/dist/opencode/build/src/narrative/readers.js +406 -0
  661. package/dist/opencode/build/src/narrative/render.d.ts +3 -0
  662. package/dist/opencode/build/src/narrative/render.js +83 -0
  663. package/dist/opencode/build/src/narrative/resolver.d.ts +52 -0
  664. package/dist/opencode/build/src/narrative/resolver.js +190 -0
  665. package/dist/opencode/build/src/narrative/snapshot.d.ts +90 -0
  666. package/dist/opencode/build/src/narrative/snapshot.js +325 -0
  667. package/dist/opencode/build/src/narrative/source-ids.d.ts +121 -0
  668. package/dist/opencode/build/src/narrative/source-ids.js +201 -0
  669. package/dist/opencode/build/src/narrative/statements.d.ts +84 -0
  670. package/dist/opencode/build/src/narrative/statements.js +347 -0
  671. package/dist/opencode/build/src/narrative/turn-spine.d.ts +19 -0
  672. package/dist/opencode/build/src/narrative/turn-spine.js +73 -0
  673. package/dist/opencode/build/src/tools/build-universal-bundles.js +8 -0
  674. package/dist/opencode/build/src/tools/generate-capability-matrix.d.ts +7 -0
  675. package/dist/opencode/build/src/tools/generate-capability-matrix.js +207 -0
  676. package/dist/opencode/build/src/tools/validate-source-tree.js +5 -1
  677. package/dist/opencode/context/scripts/hooks/stop-goal-fit.js +182 -9
  678. package/dist/opencode/context/scripts/hooks/workflow-steering.js +160 -19
  679. package/dist/opencode/context/scripts/telemetry/lib/session.sh +3 -0
  680. package/dist/opencode/context/scripts/telemetry/lib/transport.sh +12 -3
  681. package/dist/opencode/context/scripts/telemetry/telemetry.sh +480 -59
  682. package/dist/opencode/docs/context-map.md +4 -0
  683. package/dist/opencode/docs/decisions/index.md +1 -0
  684. package/dist/opencode/docs/decisions/writer-observed-execution.md +51 -0
  685. package/dist/opencode/docs/fixture-ownership.md +5 -1
  686. package/dist/opencode/docs/migrations.md +20 -0
  687. package/dist/opencode/docs/public-workflow-cli.md +9 -0
  688. package/dist/opencode/docs/spec/builder-flow-runtime.md +49 -8
  689. package/dist/opencode/docs/spec/runtime-hook-surface.md +29 -1
  690. package/dist/opencode/docs/specs/economics-record-contract.md +1 -1
  691. package/dist/opencode/docs/specs/harness-capability-matrix.md +63 -21
  692. package/dist/opencode/docs/workflow-usage-guide.md +4 -1
  693. package/dist/opencode/evals/ci/run-baseline.sh +38 -0
  694. package/dist/opencode/evals/fixtures/narrative-evals/README.md +62 -0
  695. package/dist/opencode/evals/fixtures/narrative-evals/corpus.json +129 -0
  696. package/dist/opencode/evals/fixtures/narrative-evals/scorer.mjs +522 -0
  697. package/dist/opencode/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
  698. package/dist/opencode/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
  699. package/dist/opencode/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
  700. package/dist/opencode/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
  701. package/dist/opencode/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
  702. package/dist/opencode/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
  703. package/dist/opencode/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
  704. package/dist/opencode/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
  705. package/dist/opencode/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
  706. package/dist/opencode/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
  707. package/dist/opencode/evals/fixtures/narrative-sources/repo/created.txt +1 -0
  708. package/dist/opencode/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
  709. package/dist/opencode/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
  710. package/dist/opencode/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
  711. package/dist/opencode/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
  712. package/dist/opencode/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
  713. package/dist/opencode/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
  714. package/dist/opencode/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
  715. package/dist/opencode/evals/integration/test_capability_declarations.sh +183 -0
  716. package/dist/opencode/evals/integration/test_economics_record.sh +42 -0
  717. package/dist/opencode/evals/integration/test_fixture_retirement_audit.sh +2 -2
  718. package/dist/opencode/evals/integration/test_gate_review_inquiry_records.sh +42 -35
  719. package/dist/opencode/evals/integration/test_goal_fit_hook.sh +13 -5
  720. package/dist/opencode/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
  721. package/dist/opencode/evals/integration/test_narrative_evals.sh +174 -0
  722. package/dist/opencode/evals/integration/test_narrative_grounded_envelope.sh +280 -0
  723. package/dist/opencode/evals/integration/test_narrative_grounding_validator.sh +166 -0
  724. package/dist/opencode/evals/integration/test_narrative_intent_annotation.sh +205 -0
  725. package/dist/opencode/evals/integration/test_narrative_prose_renderer.sh +239 -0
  726. package/dist/opencode/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
  727. package/dist/opencode/evals/integration/test_narrative_runtime_projection.sh +146 -0
  728. package/dist/opencode/evals/integration/test_narrative_source_contract.sh +228 -0
  729. package/dist/opencode/evals/integration/test_narrative_trust_isolation.sh +408 -0
  730. package/dist/opencode/evals/integration/test_plain_stop_messaging.sh +91 -0
  731. package/dist/opencode/evals/integration/test_session_resume_roundtrip.sh +228 -0
  732. package/dist/opencode/evals/integration/test_telemetry_action_class.sh +265 -0
  733. package/dist/opencode/evals/integration/test_telemetry_delegation.sh +291 -0
  734. package/dist/opencode/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
  735. package/dist/opencode/evals/integration/test_telemetry_task_slug.sh +158 -0
  736. package/dist/opencode/evals/integration/test_telemetry_tool_outcome.sh +279 -0
  737. package/dist/opencode/evals/integration/test_workflow_sidecar_writer.sh +111 -2
  738. package/dist/opencode/evals/run.sh +12 -0
  739. package/dist/opencode/evals/static/test_library_exports.sh +5 -0
  740. package/dist/opencode/install.sh +1 -1
  741. package/dist/opencode/schemas/grounded-execution-narrative.schema.json +292 -0
  742. package/dist/opencode/schemas/narrative-eval-result.schema.json +205 -0
  743. package/dist/opencode/schemas/narrative-runtime-projection.schema.json +134 -0
  744. package/dist/opencode/schemas/narrative-source-manifest.schema.json +294 -0
  745. package/dist/opencode/scripts/check-hachure-boundary.mjs +81 -0
  746. package/dist/opencode/scripts/hooks/stop-goal-fit.js +182 -9
  747. package/dist/opencode/scripts/hooks/workflow-steering.js +160 -19
  748. package/dist/opencode/scripts/telemetry/economics-record.schema.json +1 -1
  749. package/dist/opencode/scripts/telemetry/economics-record.sh +47 -3
  750. package/dist/opencode/scripts/telemetry/lib/session.sh +3 -0
  751. package/dist/opencode/scripts/telemetry/lib/transport.sh +12 -3
  752. package/dist/opencode/scripts/telemetry/telemetry.sh +480 -59
  753. package/dist/pi/build/generated/capability-declarations.json +177 -0
  754. package/dist/pi/build/package.json +1 -1
  755. package/dist/pi/build/src/builder-flow-runtime.d.ts +37 -0
  756. package/dist/pi/build/src/builder-flow-runtime.js +71 -1
  757. package/dist/pi/build/src/builder-gate-action-envelope.d.ts +59 -7
  758. package/dist/pi/build/src/builder-gate-action-envelope.js +163 -30
  759. package/dist/pi/build/src/builder-lifecycle-authority.d.ts +26 -0
  760. package/dist/pi/build/src/builder-lifecycle-authority.js +38 -0
  761. package/dist/pi/build/src/cli/builder-run.js +71 -2
  762. package/dist/pi/build/src/cli/narrative-render.d.ts +95 -0
  763. package/dist/pi/build/src/cli/narrative-render.js +466 -0
  764. package/dist/pi/build/src/cli/narrative-sources.d.ts +2 -0
  765. package/dist/pi/build/src/cli/narrative-sources.js +282 -0
  766. package/dist/pi/build/src/cli/public-contracts.d.ts +257 -0
  767. package/dist/pi/build/src/cli/public-contracts.js +87 -0
  768. package/dist/pi/build/src/cli/workflow-sidecar.d.ts +28 -12
  769. package/dist/pi/build/src/cli/workflow-sidecar.js +599 -180
  770. package/dist/pi/build/src/cli/workflow.js +11 -1
  771. package/dist/pi/build/src/cli.js +8 -0
  772. package/dist/pi/build/src/continuation-driver.d.ts +12 -0
  773. package/dist/pi/build/src/continuation-driver.js +16 -2
  774. package/dist/pi/build/src/continuation-validation.js +349 -3
  775. package/dist/pi/build/src/flow-kit/action-metadata.js +21 -1
  776. package/dist/pi/build/src/index.d.ts +33 -2
  777. package/dist/pi/build/src/index.js +24 -0
  778. package/dist/pi/build/src/lib/capability-declarations.d.ts +83 -0
  779. package/dist/pi/build/src/lib/capability-declarations.js +209 -0
  780. package/dist/pi/build/src/narrative/envelope.d.ts +111 -0
  781. package/dist/pi/build/src/narrative/envelope.js +590 -0
  782. package/dist/pi/build/src/narrative/eval-result.d.ts +60 -0
  783. package/dist/pi/build/src/narrative/eval-result.js +158 -0
  784. package/dist/pi/build/src/narrative/grounding-validator.d.ts +104 -0
  785. package/dist/pi/build/src/narrative/grounding-validator.js +472 -0
  786. package/dist/pi/build/src/narrative/integrity.d.ts +17 -0
  787. package/dist/pi/build/src/narrative/integrity.js +39 -0
  788. package/dist/pi/build/src/narrative/intent-economics.d.ts +42 -0
  789. package/dist/pi/build/src/narrative/intent-economics.js +82 -0
  790. package/dist/pi/build/src/narrative/intent.d.ts +94 -0
  791. package/dist/pi/build/src/narrative/intent.js +132 -0
  792. package/dist/pi/build/src/narrative/policy-filter.d.ts +15 -0
  793. package/dist/pi/build/src/narrative/policy-filter.js +58 -0
  794. package/dist/pi/build/src/narrative/projection.d.ts +58 -0
  795. package/dist/pi/build/src/narrative/projection.js +502 -0
  796. package/dist/pi/build/src/narrative/readers.d.ts +84 -0
  797. package/dist/pi/build/src/narrative/readers.js +406 -0
  798. package/dist/pi/build/src/narrative/render.d.ts +3 -0
  799. package/dist/pi/build/src/narrative/render.js +83 -0
  800. package/dist/pi/build/src/narrative/resolver.d.ts +52 -0
  801. package/dist/pi/build/src/narrative/resolver.js +190 -0
  802. package/dist/pi/build/src/narrative/snapshot.d.ts +90 -0
  803. package/dist/pi/build/src/narrative/snapshot.js +325 -0
  804. package/dist/pi/build/src/narrative/source-ids.d.ts +121 -0
  805. package/dist/pi/build/src/narrative/source-ids.js +201 -0
  806. package/dist/pi/build/src/narrative/statements.d.ts +84 -0
  807. package/dist/pi/build/src/narrative/statements.js +347 -0
  808. package/dist/pi/build/src/narrative/turn-spine.d.ts +19 -0
  809. package/dist/pi/build/src/narrative/turn-spine.js +73 -0
  810. package/dist/pi/build/src/tools/build-universal-bundles.js +8 -0
  811. package/dist/pi/build/src/tools/generate-capability-matrix.d.ts +7 -0
  812. package/dist/pi/build/src/tools/generate-capability-matrix.js +207 -0
  813. package/dist/pi/build/src/tools/validate-source-tree.js +5 -1
  814. package/dist/pi/context/scripts/hooks/stop-goal-fit.js +182 -9
  815. package/dist/pi/context/scripts/hooks/workflow-steering.js +160 -19
  816. package/dist/pi/context/scripts/telemetry/lib/session.sh +3 -0
  817. package/dist/pi/context/scripts/telemetry/lib/transport.sh +12 -3
  818. package/dist/pi/context/scripts/telemetry/telemetry.sh +480 -59
  819. package/dist/pi/docs/context-map.md +4 -0
  820. package/dist/pi/docs/decisions/index.md +1 -0
  821. package/dist/pi/docs/decisions/writer-observed-execution.md +51 -0
  822. package/dist/pi/docs/fixture-ownership.md +5 -1
  823. package/dist/pi/docs/migrations.md +20 -0
  824. package/dist/pi/docs/public-workflow-cli.md +9 -0
  825. package/dist/pi/docs/spec/builder-flow-runtime.md +49 -8
  826. package/dist/pi/docs/spec/runtime-hook-surface.md +29 -1
  827. package/dist/pi/docs/specs/economics-record-contract.md +1 -1
  828. package/dist/pi/docs/specs/harness-capability-matrix.md +63 -21
  829. package/dist/pi/docs/workflow-usage-guide.md +4 -1
  830. package/dist/pi/evals/ci/run-baseline.sh +38 -0
  831. package/dist/pi/evals/fixtures/narrative-evals/README.md +62 -0
  832. package/dist/pi/evals/fixtures/narrative-evals/corpus.json +129 -0
  833. package/dist/pi/evals/fixtures/narrative-evals/scorer.mjs +522 -0
  834. package/dist/pi/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
  835. package/dist/pi/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
  836. package/dist/pi/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
  837. package/dist/pi/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
  838. package/dist/pi/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
  839. package/dist/pi/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
  840. package/dist/pi/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
  841. package/dist/pi/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
  842. package/dist/pi/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
  843. package/dist/pi/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
  844. package/dist/pi/evals/fixtures/narrative-sources/repo/created.txt +1 -0
  845. package/dist/pi/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
  846. package/dist/pi/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
  847. package/dist/pi/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
  848. package/dist/pi/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
  849. package/dist/pi/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
  850. package/dist/pi/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
  851. package/dist/pi/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
  852. package/dist/pi/evals/integration/test_capability_declarations.sh +183 -0
  853. package/dist/pi/evals/integration/test_economics_record.sh +42 -0
  854. package/dist/pi/evals/integration/test_fixture_retirement_audit.sh +2 -2
  855. package/dist/pi/evals/integration/test_gate_review_inquiry_records.sh +42 -35
  856. package/dist/pi/evals/integration/test_goal_fit_hook.sh +13 -5
  857. package/dist/pi/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
  858. package/dist/pi/evals/integration/test_narrative_evals.sh +174 -0
  859. package/dist/pi/evals/integration/test_narrative_grounded_envelope.sh +280 -0
  860. package/dist/pi/evals/integration/test_narrative_grounding_validator.sh +166 -0
  861. package/dist/pi/evals/integration/test_narrative_intent_annotation.sh +205 -0
  862. package/dist/pi/evals/integration/test_narrative_prose_renderer.sh +239 -0
  863. package/dist/pi/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
  864. package/dist/pi/evals/integration/test_narrative_runtime_projection.sh +146 -0
  865. package/dist/pi/evals/integration/test_narrative_source_contract.sh +228 -0
  866. package/dist/pi/evals/integration/test_narrative_trust_isolation.sh +408 -0
  867. package/dist/pi/evals/integration/test_plain_stop_messaging.sh +91 -0
  868. package/dist/pi/evals/integration/test_session_resume_roundtrip.sh +228 -0
  869. package/dist/pi/evals/integration/test_telemetry_action_class.sh +265 -0
  870. package/dist/pi/evals/integration/test_telemetry_delegation.sh +291 -0
  871. package/dist/pi/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
  872. package/dist/pi/evals/integration/test_telemetry_task_slug.sh +158 -0
  873. package/dist/pi/evals/integration/test_telemetry_tool_outcome.sh +279 -0
  874. package/dist/pi/evals/integration/test_workflow_sidecar_writer.sh +111 -2
  875. package/dist/pi/evals/run.sh +12 -0
  876. package/dist/pi/evals/static/test_library_exports.sh +5 -0
  877. package/dist/pi/install.sh +1 -1
  878. package/dist/pi/schemas/grounded-execution-narrative.schema.json +292 -0
  879. package/dist/pi/schemas/narrative-eval-result.schema.json +205 -0
  880. package/dist/pi/schemas/narrative-runtime-projection.schema.json +134 -0
  881. package/dist/pi/schemas/narrative-source-manifest.schema.json +294 -0
  882. package/dist/pi/scripts/check-hachure-boundary.mjs +81 -0
  883. package/dist/pi/scripts/hooks/stop-goal-fit.js +182 -9
  884. package/dist/pi/scripts/hooks/workflow-steering.js +160 -19
  885. package/dist/pi/scripts/telemetry/economics-record.schema.json +1 -1
  886. package/dist/pi/scripts/telemetry/economics-record.sh +47 -3
  887. package/dist/pi/scripts/telemetry/lib/session.sh +3 -0
  888. package/dist/pi/scripts/telemetry/lib/transport.sh +12 -3
  889. package/dist/pi/scripts/telemetry/telemetry.sh +480 -59
  890. package/docs/context-map.md +4 -0
  891. package/docs/decisions/index.md +1 -0
  892. package/docs/decisions/writer-observed-execution.md +51 -0
  893. package/docs/fixture-ownership.md +5 -1
  894. package/docs/migrations.md +20 -0
  895. package/docs/public-workflow-cli.md +9 -0
  896. package/docs/spec/builder-flow-runtime.md +49 -8
  897. package/docs/spec/runtime-hook-surface.md +29 -1
  898. package/docs/specs/economics-record-contract.md +1 -1
  899. package/docs/specs/harness-capability-matrix.md +63 -21
  900. package/docs/workflow-usage-guide.md +4 -1
  901. package/evals/ci/run-baseline.sh +38 -0
  902. package/evals/fixtures/narrative-evals/README.md +62 -0
  903. package/evals/fixtures/narrative-evals/corpus.json +129 -0
  904. package/evals/fixtures/narrative-evals/scorer.mjs +522 -0
  905. package/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
  906. package/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
  907. package/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
  908. package/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
  909. package/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
  910. package/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
  911. package/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
  912. package/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
  913. package/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
  914. package/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
  915. package/evals/fixtures/narrative-sources/repo/created.txt +1 -0
  916. package/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
  917. package/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
  918. package/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
  919. package/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
  920. package/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
  921. package/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
  922. package/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
  923. package/evals/integration/test_capability_declarations.sh +183 -0
  924. package/evals/integration/test_economics_record.sh +42 -0
  925. package/evals/integration/test_fixture_retirement_audit.sh +2 -2
  926. package/evals/integration/test_gate_review_inquiry_records.sh +42 -35
  927. package/evals/integration/test_goal_fit_hook.sh +13 -5
  928. package/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
  929. package/evals/integration/test_narrative_evals.sh +174 -0
  930. package/evals/integration/test_narrative_grounded_envelope.sh +280 -0
  931. package/evals/integration/test_narrative_grounding_validator.sh +166 -0
  932. package/evals/integration/test_narrative_intent_annotation.sh +205 -0
  933. package/evals/integration/test_narrative_prose_renderer.sh +239 -0
  934. package/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
  935. package/evals/integration/test_narrative_runtime_projection.sh +146 -0
  936. package/evals/integration/test_narrative_source_contract.sh +228 -0
  937. package/evals/integration/test_narrative_trust_isolation.sh +408 -0
  938. package/evals/integration/test_plain_stop_messaging.sh +91 -0
  939. package/evals/integration/test_session_resume_roundtrip.sh +228 -0
  940. package/evals/integration/test_telemetry_action_class.sh +265 -0
  941. package/evals/integration/test_telemetry_delegation.sh +291 -0
  942. package/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
  943. package/evals/integration/test_telemetry_task_slug.sh +158 -0
  944. package/evals/integration/test_telemetry_tool_outcome.sh +279 -0
  945. package/evals/integration/test_workflow_sidecar_writer.sh +111 -2
  946. package/evals/run.sh +12 -0
  947. package/evals/static/test_library_exports.sh +5 -0
  948. package/package.json +9 -5
  949. package/schemas/grounded-execution-narrative.schema.json +292 -0
  950. package/schemas/narrative-eval-result.schema.json +205 -0
  951. package/schemas/narrative-runtime-projection.schema.json +134 -0
  952. package/schemas/narrative-source-manifest.schema.json +294 -0
  953. package/scripts/check-hachure-boundary.mjs +81 -0
  954. package/scripts/hooks/stop-goal-fit.js +182 -9
  955. package/scripts/hooks/workflow-steering.js +160 -19
  956. package/scripts/telemetry/economics-record.schema.json +1 -1
  957. package/scripts/telemetry/economics-record.sh +47 -3
  958. package/scripts/telemetry/lib/session.sh +3 -0
  959. package/scripts/telemetry/lib/transport.sh +12 -3
  960. package/scripts/telemetry/telemetry.sh +480 -59
  961. package/src/builder-flow-runtime.ts +102 -1
  962. package/src/builder-gate-action-envelope.ts +216 -37
  963. package/src/builder-lifecycle-authority.ts +48 -0
  964. package/src/cli/builder-flow-runtime.test.mjs +220 -7
  965. package/src/cli/builder-run.ts +71 -1
  966. package/src/cli/capability-declarations.test.mjs +117 -0
  967. package/src/cli/continuation-driver.test.mjs +332 -10
  968. package/src/cli/kit-metadata-security.test.mjs +90 -2
  969. package/src/cli/narrative-arch-isolation.test.mjs +198 -0
  970. package/src/cli/narrative-envelope.test.mjs +367 -0
  971. package/src/cli/narrative-grounding-validator.test.mjs +322 -0
  972. package/src/cli/narrative-intent.test.mjs +177 -0
  973. package/src/cli/narrative-policy-filter.test.mjs +66 -0
  974. package/src/cli/narrative-projection.test.mjs +236 -0
  975. package/src/cli/narrative-readers.test.mjs +196 -0
  976. package/src/cli/narrative-render.test.mjs +221 -0
  977. package/src/cli/narrative-render.ts +589 -0
  978. package/src/cli/narrative-resolver.test.mjs +114 -0
  979. package/src/cli/narrative-snapshot.test.mjs +149 -0
  980. package/src/cli/narrative-source-ids.test.mjs +84 -0
  981. package/src/cli/narrative-sources.ts +276 -0
  982. package/src/cli/narrative-statements.test.mjs +369 -0
  983. package/src/cli/narrative-turn-spine.test.mjs +74 -0
  984. package/src/cli/public-api.test.mjs +17 -0
  985. package/src/cli/public-contracts.ts +95 -0
  986. package/src/cli/sidecar-pure-helpers.test.mjs +216 -24
  987. package/src/cli/trust-bundle-policy-order.test.mjs +134 -1
  988. package/src/cli/workflow-sidecar.ts +563 -167
  989. package/src/cli/workflow.ts +15 -1
  990. package/src/cli/writer-observed-execution.test.mjs +102 -0
  991. package/src/cli.ts +8 -0
  992. package/src/continuation-driver.ts +32 -3
  993. package/src/continuation-validation.ts +362 -4
  994. package/src/flow-kit/action-metadata.ts +20 -1
  995. package/src/index.ts +241 -0
  996. package/src/lib/capability-declarations.ts +261 -0
  997. package/src/narrative/envelope.ts +624 -0
  998. package/src/narrative/eval-result.ts +175 -0
  999. package/src/narrative/grounding-validator.ts +610 -0
  1000. package/src/narrative/integrity.ts +53 -0
  1001. package/src/narrative/intent-economics.ts +119 -0
  1002. package/src/narrative/intent.ts +216 -0
  1003. package/src/narrative/policy-filter.ts +66 -0
  1004. package/src/narrative/projection.ts +530 -0
  1005. package/src/narrative/readers.ts +427 -0
  1006. package/src/narrative/render.ts +82 -0
  1007. package/src/narrative/resolver.ts +195 -0
  1008. package/src/narrative/snapshot.ts +365 -0
  1009. package/src/narrative/source-ids.ts +294 -0
  1010. package/src/narrative/statements.ts +423 -0
  1011. package/src/narrative/turn-spine.ts +99 -0
  1012. package/src/tools/build-universal-bundles.ts +8 -0
  1013. package/src/tools/generate-capability-matrix.ts +215 -0
  1014. package/src/tools/validate-source-tree.ts +5 -1
  1015. package/build/src/cli/sidecar-claim-explain.d.ts +0 -45
  1016. package/build/src/cli/sidecar-claim-explain.js +0 -87
  1017. package/dist/base/build/src/cli/sidecar-claim-explain.d.ts +0 -45
  1018. package/dist/base/build/src/cli/sidecar-claim-explain.js +0 -87
  1019. package/dist/claude-code/build/src/cli/sidecar-claim-explain.d.ts +0 -45
  1020. package/dist/claude-code/build/src/cli/sidecar-claim-explain.js +0 -87
  1021. package/dist/codex/build/src/cli/sidecar-claim-explain.d.ts +0 -45
  1022. package/dist/codex/build/src/cli/sidecar-claim-explain.js +0 -87
  1023. package/dist/kiro/build/src/cli/sidecar-claim-explain.d.ts +0 -45
  1024. package/dist/kiro/build/src/cli/sidecar-claim-explain.js +0 -87
  1025. package/dist/opencode/build/src/cli/sidecar-claim-explain.d.ts +0 -45
  1026. package/dist/opencode/build/src/cli/sidecar-claim-explain.js +0 -87
  1027. package/dist/pi/build/src/cli/sidecar-claim-explain.d.ts +0 -45
  1028. package/dist/pi/build/src/cli/sidecar-claim-explain.js +0 -87
  1029. package/src/cli/sidecar-claim-explain.ts +0 -130
@@ -14,7 +14,8 @@ import { flowAgentsPackageRoot, flowAgentsPackageVersion } from "../lib/package-
14
14
  import { pinnedFlowAgentsCommand } from "../lib/pinned-cli-command.js";
15
15
  import { runObservedCommand } from "../lib/observed-command.js";
16
16
  import { captureReviewWorkspaceSnapshot, startBuilderFlowSession, syncBuilderFlowSession } from "../builder-flow-runtime.js";
17
- import { WORKFLOW_CRITIQUE_STATUSES } from "./public-contracts.js";
17
+ import { NARRATIVE_NAMESPACE_ROOT } from "./narrative-sources.js";
18
+ import { EVIDENCE_REF_FIELD_SCHEMAS, EVIDENCE_REF_KINDS, EVIDENCE_REF_RULES, NARRATIVE_PROMOTE_OPERATION, WORKFLOW_ACCEPTANCE_STATUSES, WORKFLOW_CRITIQUE_STATUSES, } from "./public-contracts.js";
18
19
  // #291 Wave 1 Task 1.1 exports: ensure-session's ownership guard reuses the EXACT same
19
20
  // assignment ⋈ liveness join / claim / supersede logic #290 already ships for the
20
21
  // `assignment-provider` CLI, rather than reimplementing a second, parallel join (static ESM
@@ -391,62 +392,23 @@ export async function validateTrustBundle(bundle) {
391
392
  return { valid: false, errors: [message], available: true };
392
393
  }
393
394
  }
394
- // Validate a single InquiryRecord against the hachure inquiry-record.schema.json.
395
- // Uses a separate AJV instance compiled against that schema (not the trust-bundle schema).
396
- let _hachureInquiryRecordValidator;
397
- function getHachureInquiryRecordValidator() {
398
- if (_hachureInquiryRecordValidator !== undefined)
399
- return _hachureInquiryRecordValidator;
400
- try {
401
- const _require = createRequire(import.meta.url);
402
- const hachureDir = path.dirname(_require.resolve("hachure"));
403
- const schemasDir = path.join(hachureDir, "schemas");
404
- const Ajv = _require("ajv/dist/2020");
405
- const schemas = {};
406
- for (const file of fs.readdirSync(schemasDir)) {
407
- if (!file.endsWith(".schema.json"))
408
- continue;
409
- schemas[file] = JSON.parse(fs.readFileSync(path.join(schemasDir, file), "utf8"));
410
- }
411
- const inquiryRecordSchema = schemas["inquiry-record.schema.json"];
412
- if (!inquiryRecordSchema) {
413
- _hachureInquiryRecordValidator = null;
414
- return null;
415
- }
416
- const ajv = new Ajv({ strict: false, allErrors: true });
417
- for (const [filename, schema] of Object.entries(schemas)) {
418
- if (filename === "inquiry-record.schema.json")
419
- continue;
420
- ajv.addSchema(schema, filename);
421
- }
422
- const validate = ajv.compile(inquiryRecordSchema);
423
- _hachureInquiryRecordValidator = (record) => {
424
- const valid = validate(record);
425
- if (valid)
426
- return { valid: true, errors: [] };
427
- const errors = (validate.errors ?? []).map((err) => {
428
- const loc = err.instancePath || err.schemaPath || "";
429
- return `${loc} ${err.message ?? "invalid"}`.trim();
430
- });
431
- return { valid: false, errors };
432
- };
433
- return _hachureInquiryRecordValidator;
434
- }
435
- catch {
436
- _hachureInquiryRecordValidator = null;
437
- return null;
438
- }
439
- }
440
395
  /**
441
- * Validate a record against the canonical hachure inquiry-record.schema.json
442
- * (https://kontourai.io/schemas/surface/inquiry-record.schema.json).
443
- * Returns `{ valid, errors, available }`. Fail-open when hachure is not installed.
396
+ * Validate a record against the canonical InquiryRecord schema via
397
+ * @kontourai/surface's `validateInquiryRecord` (symmetric to `validateTrustBundle`).
398
+ * Returns `{ valid, errors, available }`. Fail-open when Surface is unavailable.
444
399
  */
445
- export function validateInquiryRecord(record) {
446
- const validate = getHachureInquiryRecordValidator();
447
- if (!validate)
400
+ export async function validateInquiryRecord(record) {
401
+ const m = await tryLoadSurface();
402
+ if (!m || typeof m.validateInquiryRecord !== "function") {
448
403
  return { valid: true, errors: [], available: false };
449
- return { ...validate(record), available: true };
404
+ }
405
+ try {
406
+ m.validateInquiryRecord(record);
407
+ return { valid: true, errors: [], available: true };
408
+ }
409
+ catch (err) {
410
+ return { valid: false, errors: [err instanceof Error ? err.message : String(err)], available: true };
411
+ }
450
412
  }
451
413
  let _surfaceModule; // undefined = not tried yet; null = unavailable
452
414
  async function tryLoadSurface() {
@@ -488,6 +450,15 @@ function criterionStatusToEventStatus(status) {
488
450
  return "assumed";
489
451
  return null; // pending / not_verified → no event → Surface returns "unknown"
490
452
  }
453
+ const ACCEPTANCE_CRITERION_STATUSES = new Set(WORKFLOW_ACCEPTANCE_STATUSES);
454
+ const CANONICALLY_OBSERVED_ACCEPTANCE_CRITERIA = new WeakSet();
455
+ function markCanonicallyObservedCriterion(criterion) {
456
+ CANONICALLY_OBSERVED_ACCEPTANCE_CRITERIA.add(criterion);
457
+ return criterion;
458
+ }
459
+ function normalizeCriterionStatus(status) {
460
+ return typeof status === "string" && ACCEPTANCE_CRITERION_STATUSES.has(status) ? status : "pending";
461
+ }
491
462
  /**
492
463
  * WS8 (ADR 0020): Derive Surface evidence classification (evidenceType + method)
493
464
  * from a workflow check's kind, replacing the previous hardcoded `test_output`.
@@ -599,7 +570,23 @@ export function reduceCaptureLogByCommand(commandLog) {
599
570
  else
600
571
  merged = "ambiguous";
601
572
  }
602
- const mergedExitCode = exitCode !== null ? exitCode : (prev ? prev.exitCode : null);
573
+ // #634 review finding: the exit code must travel with the WINNING status, never a
574
+ // losing entry's — [hook fail exit 1, writer pass exit 0] must fold to fail/1, not
575
+ // fail/0 (contradictory isError:true, exitCode:0 evidence). When both entries carry
576
+ // the winning status, prefer the newer non-null code.
577
+ let mergedExitCode;
578
+ if (!prev) {
579
+ mergedExitCode = exitCode;
580
+ }
581
+ else if (merged === result && merged === prev.observedResult) {
582
+ mergedExitCode = exitCode !== null ? exitCode : prev.exitCode;
583
+ }
584
+ else if (merged === result) {
585
+ mergedExitCode = exitCode;
586
+ }
587
+ else {
588
+ mergedExitCode = prev.exitCode;
589
+ }
603
590
  captureByCommand.set(key, { observedResult: merged, exitCode: mergedExitCode });
604
591
  }
605
592
  return captureByCommand;
@@ -790,8 +777,6 @@ export async function buildTrustBundle(slug, timestamp, checks, criteria, critiq
790
777
  // "flow-step". Fallback: first non-decision, non-acceptance entry.
791
778
  // check (kind=policy) → expects[] entry whose claimType contains
792
779
  // "compliance" or "policy". Fallback: same as non-policy.
793
- // acceptance criterion → expects[] entry whose subjectType is "flow-step"
794
- // OR claimType contains "tests" OR "compliance". Fallback: first entry.
795
780
  // critique → expects[] entry whose claimType contains "policy"
796
781
  // OR "compliance" AND subjectType is "artifact". Fallback: last entry.
797
782
  //
@@ -801,7 +786,7 @@ export async function buildTrustBundle(slug, timestamp, checks, criteria, critiq
801
786
  // Surface from that evidence (never hand-set).
802
787
  //
803
788
  // Per-gate producibility (ADR 0016 P-d):
804
- // (a) Already handled via subjectType=flow-step preference:
789
+ // (a) Already handled via subjectType=flow-step preference for checks:
805
790
  // builder.verify.tests (verify-gate, subjectType=flow-step)
806
791
  // builder.verify.policy-compliance (verify-gate, kind=policy match)
807
792
  // (b) Producible via fallback (non-decision, non-acceptance, first match):
@@ -818,6 +803,9 @@ export async function buildTrustBundle(slug, timestamp, checks, criteria, critiq
818
803
  // builder.learn.evidence (learn-gate, subjectType=release)
819
804
  // For category (c): record-gate-claim subcommand allows skills to target a specific
820
805
  // expects[] entry by --expectation <id>, bypassing this semantic match entirely.
806
+ //
807
+ // Acceptance criteria do not participate in this matching table. They are durable Work Item
808
+ // facts with invariant workflow.acceptance.criterion/flow-step identity across every gate.
821
809
  function matchExpectsEntry(kind, checkKindVal, expectationId) {
822
810
  if (!activeStep || activeStep.gateExpects.length === 0)
823
811
  return null;
@@ -856,15 +844,6 @@ export async function buildTrustBundle(slug, timestamp, checks, criteria, critiq
856
844
  return { claimType: fallback.bundle_claim.claimType, subjectType: fallback.bundle_claim.subjectType };
857
845
  return null;
858
846
  }
859
- if (kind === "acceptance") {
860
- const match = expects.find((e) => {
861
- const ct = e.bundle_claim.claimType.toLowerCase();
862
- return e.bundle_claim.subjectType === "flow-step" || ct.includes("tests") || ct.includes("compliance");
863
- });
864
- if (match)
865
- return { claimType: match.bundle_claim.claimType, subjectType: match.bundle_claim.subjectType };
866
- return { claimType: expects[0].bundle_claim.claimType, subjectType: expects[0].bundle_claim.subjectType };
867
- }
868
847
  if (kind === "critique") {
869
848
  const match = expects.find((e) => {
870
849
  const ct = e.bundle_claim.claimType.toLowerCase();
@@ -1059,33 +1038,71 @@ export async function buildTrustBundle(slug, timestamp, checks, criteria, critiq
1059
1038
  continue;
1060
1039
  const subjectId = `${slug}/${criterion.id}`;
1061
1040
  const fieldOrBehavior = String(criterion.description ?? criterion.id);
1041
+ const normalizedCriterionStatus = normalizeCriterionStatus(criterion.status);
1062
1042
  const criterionIdentityVersion = criterion.identity_version === 2 ? 2 : 1;
1063
1043
  const criterionVerifiedAt = criterionIdentityVersion === 2 && typeof criterion.verified_at === "string" ? criterion.verified_at : null;
1044
+ const criterionEvidenceRefs = Array.isArray(criterion.evidence_refs) ? criterion.evidence_refs : [];
1045
+ const commandRefs = criterionEvidenceRefs
1046
+ .filter((ref) => ref?.kind === "command")
1047
+ .map((ref) => commandFromEvidenceRef(ref))
1048
+ .filter((command) => command.length > 0);
1049
+ const rawObservedCommands = Array.isArray(criterion._observed_commands) ? criterion._observed_commands : [];
1050
+ const observedCommands = rawObservedCommands.filter((observation) => typeof observation?.command === "string"
1051
+ && commandRefs.includes(observation.command)
1052
+ && observation.exit_code === 0
1053
+ && typeof observation.output_sha256 === "string"
1054
+ && /^[a-f0-9]{64}$/i.test(observation.output_sha256)
1055
+ && Number.isSafeInteger(observation.test_count)
1056
+ && observation.test_count > 0
1057
+ && observation.execution_proof?.kind === "local-process-exit");
1058
+ const hasObservedCommandProvenance = CANONICALLY_OBSERVED_ACCEPTANCE_CRITERIA.has(criterion)
1059
+ && criterionIdentityVersion === 2
1060
+ && criterionVerifiedAt !== null
1061
+ && Number.isFinite(Date.parse(criterionVerifiedAt))
1062
+ && observedCommands.length === rawObservedCommands.length
1063
+ && observedCommands.length > 0
1064
+ && commandRefs.every((command) => observedCommands.some((observation) => observation.command === command));
1065
+ const criterionStatus = normalizedCriterionStatus === "pass" && !hasObservedCommandProvenance
1066
+ ? "pending"
1067
+ : normalizedCriterionStatus;
1068
+ const criterionClaimTimestamp = criterionStatus === "pass" && criterionVerifiedAt ? criterionVerifiedAt : ts;
1064
1069
  const claimId = generateClaimId(subjectId, "flow-agents.workflow", criterionVerifiedAt ? `${fieldOrBehavior}::verified::${criterionVerifiedAt}` : fieldOrBehavior);
1065
- const legacyClaimType = "workflow.acceptance.criterion";
1066
- const policy = ensurePolicy(legacyClaimType, "high", []);
1067
- const evStatus = criterionStatusToEventStatus(String(criterion.status ?? ""));
1070
+ const claimType = "workflow.acceptance.criterion";
1071
+ const criterionEvidenceIds = [];
1072
+ if (criterionStatus === "pass") {
1073
+ for (const observation of observedCommands) {
1074
+ const evidenceId = `ev:${claimId}:${createHash("sha256").update(observation.command).digest("hex").slice(0, 16)}`;
1075
+ criterionEvidenceIds.push(evidenceId);
1076
+ evidenceItems.push({
1077
+ id: evidenceId,
1078
+ claimId,
1079
+ evidenceType: "test_output",
1080
+ method: "validation",
1081
+ sourceRef: `${slug}/observed-command`,
1082
+ excerptOrSummary: observation.command,
1083
+ observedAt: criterionVerifiedAt,
1084
+ collectedBy: "flow-agents/workflow-sidecar",
1085
+ passing: true,
1086
+ execution: { runner: "bash", label: observation.command, isError: false, exitCode: 0 },
1087
+ });
1088
+ }
1089
+ }
1090
+ const policy = ensurePolicy(claimType, "high", criterionEvidenceIds.length > 0 ? ["test_output"] : []);
1091
+ const evStatus = criterionStatusToEventStatus(criterionStatus);
1068
1092
  const claimEvents = [];
1069
1093
  if (evStatus) {
1070
- const evt = { id: `evt:${claimId}`, claimId, status: evStatus, actor: "flow-agents/workflow-sidecar", method: "validation", evidenceIds: [], createdAt: ts, verifiedAt: ts };
1094
+ const evt = { id: `evt:${claimId}`, claimId, status: evStatus, actor: "flow-agents/workflow-sidecar", method: "validation", evidenceIds: criterionEvidenceIds, createdAt: criterionClaimTimestamp, verifiedAt: criterionClaimTimestamp };
1071
1095
  events.push(evt);
1072
1096
  claimEvents.push(evt);
1073
1097
  }
1074
- // P-d: declared-only when active flow/step present (shadow retired); no-flow path unchanged.
1075
- const declared = matchExpectsEntry("acceptance");
1076
- if (declared) {
1077
- // Declared kit-typed claim only — no legacy shadow (ADR 0016 P-d).
1078
- const declaredPolicy = ensurePolicy(declared.claimType, "high", []);
1079
- const declaredClaimObj = { id: claimId, subjectType: declared.subjectType, subjectId, facet: "flow-agents.workflow", claimType: declared.claimType, fieldOrBehavior, value: criterion.status, createdAt: ts, updatedAt: ts, impactLevel: "high", verificationPolicyId: declaredPolicy.id, metadata: { origin: "acceptance", criterion: { id: criterion.id, description: criterion.description ?? criterion.id, status: criterion.status, evidence_refs: Array.isArray(criterion.evidence_refs) ? criterion.evidence_refs : [], ...(criterionIdentityVersion === 2 ? { identity_version: 2 } : {}), ...(criterionVerifiedAt ? { verified_at: criterionVerifiedAt } : {}) }, ...(workflowSubjectRef ? { workflow_subject_ref: workflowSubjectRef } : {}) } };
1080
- const { status: declaredStatus } = deriveClaimStatus({ claim: declaredClaimObj, evidence: [], events: claimEvents, policies: [declaredPolicy] });
1081
- claims.push({ ...declaredClaimObj, status: declaredStatus });
1082
- }
1083
- else {
1084
- // No active flow step — only the workflow.* primary claim (legitimate no-flow fallback path).
1085
- const claimObj = { id: claimId, subjectType: "workflow-acceptance-criterion", subjectId, facet: "flow-agents.workflow", claimType: legacyClaimType, fieldOrBehavior, value: criterion.status, createdAt: ts, updatedAt: ts, impactLevel: "high", verificationPolicyId: policy.id, metadata: { origin: "acceptance", criterion: { id: criterion.id, description: criterion.description ?? criterion.id, status: criterion.status, evidence_refs: Array.isArray(criterion.evidence_refs) ? criterion.evidence_refs : [] } } };
1086
- const { status: derivedStatus } = deriveClaimStatus({ claim: claimObj, evidence: [], events: claimEvents, policies: [policy] });
1087
- claims.push({ ...claimObj, status: derivedStatus });
1088
- }
1098
+ // Acceptance criteria are durable Work Item facts. Their identity must not change when a
1099
+ // bundle is rebuilt at a different active gate. Missing or invalid status is conservatively
1100
+ // projected as pending so direct artifact edits can never manufacture a pass or invalidate
1101
+ // the Hachure claim shape.
1102
+ const claimObj = { id: claimId, subjectType: "flow-step", subjectId, facet: "flow-agents.workflow", claimType, fieldOrBehavior, value: criterionStatus, createdAt: criterionClaimTimestamp, updatedAt: criterionClaimTimestamp, impactLevel: "high", verificationPolicyId: policy.id, metadata: { origin: "acceptance", criterion: { id: criterion.id, description: criterion.description ?? criterion.id, status: criterionStatus, evidence_refs: criterionEvidenceRefs, ...(observedCommands.length > 0 ? { observed_commands: observedCommands } : {}), ...(criterionIdentityVersion === 2 ? { identity_version: 2 } : {}), ...(criterionVerifiedAt ? { verified_at: criterionVerifiedAt } : {}) }, ...(workflowSubjectRef ? { workflow_subject_ref: workflowSubjectRef } : {}) } };
1103
+ const criterionEvidence = evidenceItems.filter((evidence) => evidence.claimId === claimId);
1104
+ const { status: derivedStatus } = deriveClaimStatus({ claim: claimObj, evidence: criterionEvidence, events: claimEvents, policies: [policy] });
1105
+ claims.push({ ...claimObj, status: derivedStatus });
1089
1106
  }
1090
1107
  // Critique entries → claims + events
1091
1108
  for (const c of Array.isArray(critiques) ? critiques : []) {
@@ -2432,11 +2449,11 @@ function hasNonEmptyString(value) {
2432
2449
  function hasPositiveInteger(value) {
2433
2450
  return Number.isInteger(value) && Number(value) >= 1;
2434
2451
  }
2435
- export function validateEvidenceRef(ref, label) {
2436
- if (!["source", "command", "artifact", "provider", "external"].includes(ref.kind))
2437
- die(`${label} entry kind must be one of: source, command, artifact, provider, external`);
2452
+ export function validateEvidenceRef(ref, label, projectRoot = process.cwd()) {
2453
+ if (!EVIDENCE_REF_KINDS.includes(ref.kind))
2454
+ die(`${label} entry kind must be one of: ${EVIDENCE_REF_KINDS.join(", ")}`);
2438
2455
  for (const key of Object.keys(ref))
2439
- if (!["kind", "url", "file", "line_start", "line_end", "excerpt", "summary"].includes(key))
2456
+ if (!Object.hasOwn(EVIDENCE_REF_FIELD_SCHEMAS, key))
2440
2457
  die(`${label} entries contain unsupported field: ${key}`);
2441
2458
  if (ref.url !== undefined && !hasNonEmptyString(ref.url))
2442
2459
  die(`${label} entry url must be a non-empty string`);
@@ -2450,19 +2467,26 @@ export function validateEvidenceRef(ref, label) {
2450
2467
  die(`${label} entry line_start must be a positive integer`);
2451
2468
  if (ref.line_end !== undefined && !hasPositiveInteger(ref.line_end))
2452
2469
  die(`${label} entry line_end must be a positive integer`);
2453
- if (ref.kind === "source" && (!hasNonEmptyString(ref.file) || !hasPositiveInteger(ref.line_start) || !hasPositiveInteger(ref.line_end) || !hasNonEmptyString(ref.excerpt)))
2454
- die(`${label} source refs require file, line_start, line_end, and excerpt`);
2455
- if (ref.kind === "artifact" && (!hasNonEmptyString(ref.file) && !hasNonEmptyString(ref.url)))
2456
- die(`${label} artifact refs require file or url`);
2457
- if (ref.kind === "artifact" && (!hasNonEmptyString(ref.summary) && !hasNonEmptyString(ref.excerpt)))
2458
- die(`${label} artifact refs require summary or excerpt`);
2459
- if (ref.kind === "command" && (!hasNonEmptyString(ref.summary) && !hasNonEmptyString(ref.excerpt) && !hasNonEmptyString(ref.url)))
2460
- die(`${label} command refs require summary, excerpt, or url`);
2461
- if ((ref.kind === "provider" || ref.kind === "external") && !hasNonEmptyString(ref.url))
2462
- die(`${label} ${ref.kind} refs require url`);
2470
+ rejectNarrativeReference(projectRoot, ref.file, `${label} entry file`);
2471
+ rejectNarrativeReference(projectRoot, ref.url, `${label} entry url`);
2472
+ rejectNarrativeReference(projectRoot, ref.excerpt, `${label} entry excerpt`);
2473
+ const rules = EVIDENCE_REF_RULES[ref.kind];
2474
+ for (const rule of rules) {
2475
+ const present = (field) => field === "line_start" || field === "line_end"
2476
+ ? hasPositiveInteger(ref[field])
2477
+ : hasNonEmptyString(ref[field]);
2478
+ const valid = rule.mode === "all" ? rule.fields.every(present) : rule.fields.some(present);
2479
+ if (!valid) {
2480
+ const separator = rule.fields.length > 2 ? ", " : " ";
2481
+ const joined = rule.fields.length > 1
2482
+ ? `${rule.fields.slice(0, -1).join(separator)}${rule.fields.length > 2 ? "," : ""} or ${rule.fields.at(-1)}`
2483
+ : rule.fields[0];
2484
+ die(`${label} ${ref.kind} refs require ${rule.mode === "all" && rule.fields.length > 1 ? joined.replace(/ or ([^,]+)$/, " and $1") : joined}`);
2485
+ }
2486
+ }
2463
2487
  return ref;
2464
2488
  }
2465
- export function normalizeEvidenceRefs(raw, label) {
2489
+ export function normalizeEvidenceRefs(raw, label, projectRoot = process.cwd()) {
2466
2490
  if (!Array.isArray(raw))
2467
2491
  die(`${label} must be an array`);
2468
2492
  return raw.map((ref) => {
@@ -2470,7 +2494,7 @@ export function normalizeEvidenceRefs(raw, label) {
2470
2494
  die(`${label} entries must be structured evidence reference objects; legacy string refs are not supported`);
2471
2495
  if (!ref || typeof ref !== "object" || Array.isArray(ref))
2472
2496
  die(`${label} entries must be objects`);
2473
- return validateEvidenceRef({ ...ref }, label);
2497
+ return validateEvidenceRef({ ...ref }, label, projectRoot);
2474
2498
  });
2475
2499
  }
2476
2500
  function canonicalProjectRootForSession(dir) {
@@ -2484,14 +2508,180 @@ function canonicalProjectRootForSession(dir) {
2484
2508
  die("gate evidence project root must be a non-symlink directory");
2485
2509
  return projectRoot;
2486
2510
  }
2511
+ // #619: the narrative isolation guard must run for EVERY session layout (canonical,
2512
+ // tmp, or legacy .flow-agents/) without imposing the canonical-session requirement that
2513
+ // canonicalProjectRootForSession enforces. It only needs a best-effort project root to
2514
+ // resolve relative evidence paths; the namespace regex and content-shape checks are
2515
+ // location-independent. It mirrors canonicalProjectRootForSession's path computation for a
2516
+ // canonical .kontourai/flow-agents/<slug> session (project root is three levels up), handles
2517
+ // the legacy .flow-agents/<slug> layout (two levels up), and never dies on any layout.
2518
+ function narrativeGuardRoot(dir) {
2519
+ const artifactRoot = path.dirname(dir);
2520
+ const parentRoot = path.dirname(artifactRoot);
2521
+ if (path.basename(artifactRoot) === "flow-agents" && path.basename(parentRoot) === ".kontourai") {
2522
+ return path.dirname(parentRoot);
2523
+ }
2524
+ if (path.basename(artifactRoot) === ".flow-agents") {
2525
+ return parentRoot;
2526
+ }
2527
+ // Unknown/tmp layout: best-effort. Relative-path resolution may be imperfect, but the
2528
+ // namespace regex and content-shape checks are location-independent, so the isolation
2529
+ // guarantee holds regardless.
2530
+ return artifactRoot;
2531
+ }
2487
2532
  function pathIsWithinRoot(candidate, root) {
2488
2533
  const relative = path.relative(root, candidate);
2489
2534
  return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative));
2490
2535
  }
2536
+ export const NARRATIVE_TRUST_ISOLATION_DIAGNOSTIC = "narrative trust isolation (#619): narrative namespace artifacts cannot be used as workflow evidence";
2537
+ function decodeNarrativeReference(value) {
2538
+ try {
2539
+ return decodeURIComponent(value);
2540
+ }
2541
+ catch {
2542
+ return value;
2543
+ }
2544
+ }
2545
+ function realpathWithExistingPrefix(candidate) {
2546
+ let cursor = path.resolve(candidate);
2547
+ const suffix = [];
2548
+ while (!fs.existsSync(cursor)) {
2549
+ const parent = path.dirname(cursor);
2550
+ if (parent === cursor)
2551
+ break;
2552
+ suffix.unshift(path.basename(cursor));
2553
+ cursor = parent;
2554
+ }
2555
+ let canonical = cursor;
2556
+ try {
2557
+ canonical = (fs.realpathSync.native ?? fs.realpathSync)(cursor);
2558
+ }
2559
+ catch { /* resolved lexical prefix is the fallback */ }
2560
+ return path.resolve(canonical, ...suffix);
2561
+ }
2562
+ export function isNarrativeNamespacePath(projectRoot, resolvedPath) {
2563
+ const decoded = decodeNarrativeReference(resolvedPath);
2564
+ const normalized = decoded.replaceAll("\\", "/").toLowerCase();
2565
+ if (/(?:^|[^a-z0-9._-])\.kontourai(?:\/[^/\s"'`]+)*\/narrative(?:$|[/\s"'`])/.test(normalized))
2566
+ return true;
2567
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(decoded) && !decoded.toLowerCase().startsWith("file://"))
2568
+ return false;
2569
+ let local = decoded;
2570
+ if (decoded.toLowerCase().startsWith("file://")) {
2571
+ try {
2572
+ local = fileURLToPath(decoded);
2573
+ }
2574
+ catch {
2575
+ return false;
2576
+ }
2577
+ }
2578
+ const canonicalRoot = realpathWithExistingPrefix(path.resolve(projectRoot, NARRATIVE_NAMESPACE_ROOT)).toLowerCase();
2579
+ const candidate = realpathWithExistingPrefix(path.resolve(projectRoot, local)).toLowerCase();
2580
+ return pathIsWithinRoot(candidate, canonicalRoot);
2581
+ }
2582
+ export function isNarrativeArtifactContent(fileBytes) {
2583
+ const text = Buffer.isBuffer(fileBytes) ? fileBytes.toString("utf8") : fileBytes;
2584
+ try {
2585
+ const parsed = JSON.parse(text);
2586
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
2587
+ return false;
2588
+ const value = parsed;
2589
+ if (value.schema_version === "grounded-execution-narrative/v1")
2590
+ return true;
2591
+ if (value.schema_version === "grounded-runtime-projection/v1")
2592
+ return true;
2593
+ if (value.schema_version === "1.0"
2594
+ && hasNonEmptyString(value.narrative_id)
2595
+ && hasNonEmptyString(value.captured_at)
2596
+ && value.compiler && typeof value.compiler === "object"
2597
+ && value.capture_completeness && typeof value.capture_completeness === "object"
2598
+ && Array.isArray(value.sources))
2599
+ return true;
2600
+ return false;
2601
+ }
2602
+ catch {
2603
+ return text.includes("flow-agents-narrative-composer")
2604
+ && text.includes("# Grounded Execution Narrative")
2605
+ && text.includes("## Authority provenance");
2606
+ }
2607
+ }
2608
+ function localPathTokens(value) {
2609
+ const decoded = decodeNarrativeReference(value);
2610
+ const tokens = decoded.match(/(?:file:\/\/[^\s"'`]+)|(?:[A-Za-z]:[\\/][^\s"'`]+)|(?:\.{0,2}[\\/][^\s"'`]+)|(?:[^\s"'`]+[\\/][^\s"'`]+)/g) ?? [];
2611
+ return tokens.map((token) => token.replace(/^[([{<]+/, "").replace(/[\])}>;,]+$/, ""));
2612
+ }
2613
+ function referencesComposedNarrativePath(projectRoot, value) {
2614
+ const decoded = decodeNarrativeReference(value);
2615
+ const folded = decoded.replaceAll("\\", "/").toLowerCase();
2616
+ if (folded.includes(".kontourai/narrative"))
2617
+ return true;
2618
+ // Static shell inspection cannot decide arbitrary runtime variable composition such as
2619
+ // `base=.kontourai; test -f "$base/narrative/..."`. The compensating invariant is that a
2620
+ // command check persists only its command plus exit/observed-output digest; it never reads file
2621
+ // bytes into trust.bundle. Every channel that DOES materialize a file is independently guarded
2622
+ // by canonical path and narrative content shape. Keep that invariant covered end-to-end.
2623
+ const cdPattern = /(?:^|[;&|]\s*)cd\s+(?:"([^"]+)"|'([^']+)'|([^\s;&|]+))\s*&&([\s\S]*)/gi;
2624
+ for (const match of decoded.matchAll(cdPattern)) {
2625
+ const cdTarget = match[1] ?? match[2] ?? match[3] ?? "";
2626
+ const remainder = (match[4] ?? "").replaceAll("\\", "/").toLowerCase();
2627
+ if (!/(?:^|[^a-z0-9._-])narrative\//.test(remainder))
2628
+ continue;
2629
+ const canonicalTarget = realpathWithExistingPrefix(path.resolve(projectRoot, decodeNarrativeReference(cdTarget)))
2630
+ .replaceAll("\\", "/")
2631
+ .toLowerCase();
2632
+ const canonicalKontourai = realpathWithExistingPrefix(path.resolve(projectRoot, ".kontourai"))
2633
+ .replaceAll("\\", "/")
2634
+ .toLowerCase();
2635
+ if (canonicalTarget === canonicalKontourai
2636
+ || canonicalTarget.startsWith(`${canonicalKontourai}/`)
2637
+ || /(?:^|\/)\.kontourai(?:\/|$)/.test(canonicalTarget))
2638
+ return true;
2639
+ }
2640
+ return false;
2641
+ }
2642
+ function narrativeCandidateFile(projectRoot, raw) {
2643
+ let decoded = decodeNarrativeReference(raw);
2644
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(decoded)) {
2645
+ if (!decoded.toLowerCase().startsWith("file://"))
2646
+ return null;
2647
+ try {
2648
+ decoded = fileURLToPath(decoded);
2649
+ }
2650
+ catch {
2651
+ return null;
2652
+ }
2653
+ }
2654
+ const candidate = path.isAbsolute(decoded) ? decoded : path.resolve(projectRoot, decoded);
2655
+ try {
2656
+ const canonical = (fs.realpathSync.native ?? fs.realpathSync)(candidate);
2657
+ const stat = fs.statSync(canonical);
2658
+ return stat.isFile() ? canonical : null;
2659
+ }
2660
+ catch {
2661
+ return null;
2662
+ }
2663
+ }
2664
+ function rejectNarrativeReference(projectRoot, value, label) {
2665
+ if (typeof value !== "string")
2666
+ return;
2667
+ const decoded = decodeNarrativeReference(value);
2668
+ if (referencesComposedNarrativePath(projectRoot, decoded))
2669
+ die(`${label}: ${NARRATIVE_TRUST_ISOLATION_DIAGNOSTIC}`);
2670
+ const candidates = [decoded, ...localPathTokens(decoded)];
2671
+ for (const candidate of new Set(candidates)) {
2672
+ if (isNarrativeNamespacePath(projectRoot, candidate))
2673
+ die(`${label}: ${NARRATIVE_TRUST_ISOLATION_DIAGNOSTIC}`);
2674
+ const file = narrativeCandidateFile(projectRoot, candidate);
2675
+ if (file && isNarrativeArtifactContent(fs.readFileSync(file))) {
2676
+ die(`${label}: ${NARRATIVE_TRUST_ISOLATION_DIAGNOSTIC}`);
2677
+ }
2678
+ }
2679
+ }
2491
2680
  function validateLocalEvidenceFile(projectRoot, raw, label) {
2492
2681
  const candidate = path.resolve(projectRoot, raw);
2493
2682
  if (!pathIsWithinRoot(candidate, projectRoot))
2494
2683
  die(`${label} must remain inside the canonical project root`);
2684
+ rejectNarrativeReference(projectRoot, raw, label);
2495
2685
  let stat;
2496
2686
  try {
2497
2687
  stat = fs.lstatSync(candidate);
@@ -2509,6 +2699,12 @@ function globMatches(pattern, relative) {
2509
2699
  const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replaceAll("**", "::GLOBSTAR::").replaceAll("*", "[^/]*").replaceAll("::GLOBSTAR::", ".*");
2510
2700
  return new RegExp(`^${escaped}$`).test(relative);
2511
2701
  }
2702
+ export function rejectOperationBoundExpectation(expectationId, operation) {
2703
+ const completion = operation === NARRATIVE_PROMOTE_OPERATION
2704
+ ? "authenticated external narrative provider completion"
2705
+ : "authenticated external ChangeProvider completion";
2706
+ die(`record-gate-claim cannot satisfy operation-bound expectation ${expectationId}; ${operation} requires ${completion}`);
2707
+ }
2512
2708
  function expectedGateProducer(flowId, stepId, expectationId) {
2513
2709
  const manifest = loadJson(path.join(flowAgentsPackageRoot(), "kits", "builder", "kit.json"));
2514
2710
  const actions = Array.isArray(manifest.flow_step_actions) ? manifest.flow_step_actions : [];
@@ -2520,7 +2716,7 @@ function expectedGateProducer(flowId, stepId, expectationId) {
2520
2716
  : undefined;
2521
2717
  if (binding?.interface === "operation") {
2522
2718
  const operation = typeof binding.operation === "string" ? binding.operation : "the declared external operation";
2523
- die(`record-gate-claim cannot satisfy operation-bound expectation ${expectationId}; ${operation} requires authenticated external ChangeProvider completion`);
2719
+ rejectOperationBoundExpectation(expectationId, operation);
2524
2720
  }
2525
2721
  const skills = Array.isArray(action.skills) ? action.skills.filter((value) => typeof value === "string") : [];
2526
2722
  const roles = Array.isArray(manifest.skill_roles) ? manifest.skill_roles : [];
@@ -2775,6 +2971,131 @@ async function normalizeObservedCommands(commands, projectRoot, requireTestInten
2775
2971
  die("every --command requires a corresponding --observed-command-json");
2776
2972
  return commands.map((command) => byCommand.get(command));
2777
2973
  }
2974
+ // #634: the canonical writer's own execution is a first-class observation. On hosts whose
2975
+ // PostToolUse capture never surfaces exit codes, every hook-captured entry is "ambiguous",
2976
+ // the independent capture can never confirm a pass, and the verify gate dead-ends. The writer
2977
+ // just ran the command itself — a real process exit, not an inference — so it appends that
2978
+ // observation to the SAME hash-chained command-log, under the SAME lock protocol the capture
2979
+ // hook uses, visibly attributed via source: "canonical-writer-execution". Precedence stays
2980
+ // honest in the fold (reduceCaptureLogByCommand): any observed fail beats any pass, so a
2981
+ // writer pass can lift ambiguity but can never bury a hook-observed failure. The append is
2982
+ // fail-open with a stderr note: losing the supplementary observation must never fail the
2983
+ // gate-claim write itself. Decision record: docs/decisions/writer-observed-execution.md.
2984
+ export const WRITER_OBSERVATION_SOURCE = "canonical-writer-execution";
2985
+ const WRITER_LOCK_RETRY_MS = 5;
2986
+ const WRITER_LOCK_MAX_TRIES = 200;
2987
+ const WRITER_LOCK_STALE_MS = 10000;
2988
+ function loadCommandLogChain() {
2989
+ const _req = createRequire(import.meta.url);
2990
+ const chainPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../scripts/lib/command-log-chain.js");
2991
+ return _req(chainPath);
2992
+ }
2993
+ function writerSleepSync(ms) {
2994
+ try {
2995
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
2996
+ }
2997
+ catch { /* SharedArrayBuffer/Atomics unavailable — skip the backoff */ }
2998
+ }
2999
+ function writerAcquireLock(lockFile) {
3000
+ for (let i = 0; i < WRITER_LOCK_MAX_TRIES; i++) {
3001
+ try {
3002
+ const fd = fs.openSync(lockFile, "wx");
3003
+ try {
3004
+ fs.writeSync(fd, String(process.pid));
3005
+ }
3006
+ catch { /* pid is advisory only */ }
3007
+ return fd;
3008
+ }
3009
+ catch (err) {
3010
+ if (!err || err.code !== "EEXIST")
3011
+ return null;
3012
+ try {
3013
+ const st = fs.statSync(lockFile);
3014
+ if (Date.now() - st.mtimeMs > WRITER_LOCK_STALE_MS) {
3015
+ fs.unlinkSync(lockFile);
3016
+ continue;
3017
+ }
3018
+ }
3019
+ catch {
3020
+ continue;
3021
+ }
3022
+ writerSleepSync(WRITER_LOCK_RETRY_MS);
3023
+ }
3024
+ }
3025
+ return null;
3026
+ }
3027
+ function writerReadLastChainState(logFile, genesis) {
3028
+ let raw = "";
3029
+ try {
3030
+ raw = fs.readFileSync(logFile, "utf8");
3031
+ }
3032
+ catch {
3033
+ return { seq: -1, hash: genesis };
3034
+ }
3035
+ const lines = raw.split("\n").filter((l) => l.trim());
3036
+ for (let i = lines.length - 1; i >= 0; i--) {
3037
+ let entry;
3038
+ try {
3039
+ entry = JSON.parse(lines[i]);
3040
+ }
3041
+ catch {
3042
+ continue;
3043
+ }
3044
+ if (entry && entry._chain && typeof entry._chain.hash === "string" && typeof entry._chain.seq === "number") {
3045
+ return { seq: entry._chain.seq, hash: entry._chain.hash };
3046
+ }
3047
+ }
3048
+ return { seq: -1, hash: genesis };
3049
+ }
3050
+ export function appendWriterObservedCommands(dir, observed, timestamp) {
3051
+ if (observed.length === 0)
3052
+ return;
3053
+ try {
3054
+ const chain = loadCommandLogChain();
3055
+ const logFile = path.join(dir, "command-log.jsonl");
3056
+ const lockFile = `${logFile}.lock`;
3057
+ const fd = writerAcquireLock(lockFile);
3058
+ try {
3059
+ let { seq, hash: prevHash } = writerReadLastChainState(logFile, chain.CHAIN_GENESIS);
3060
+ const lines = [];
3061
+ for (const entry of observed) {
3062
+ const record = {
3063
+ command: entry.command,
3064
+ observedResult: entry.exit_code === 0 ? "pass" : "fail",
3065
+ exitCode: entry.exit_code,
3066
+ capturedAt: timestamp,
3067
+ source: WRITER_OBSERVATION_SOURCE,
3068
+ writer: {
3069
+ output_sha256: entry.output_sha256,
3070
+ ...(Number.isSafeInteger(entry.test_count) ? { test_count: entry.test_count } : {}),
3071
+ ...(entry.execution_proof ? { execution_proof: entry.execution_proof } : {}),
3072
+ },
3073
+ };
3074
+ seq += 1;
3075
+ const hashValue = chain.computeChainHash(prevHash, record);
3076
+ record._chain = { seq, prevHash, hash: hashValue };
3077
+ prevHash = hashValue;
3078
+ lines.push(JSON.stringify(record));
3079
+ }
3080
+ fs.appendFileSync(logFile, `${lines.join("\n")}\n`);
3081
+ }
3082
+ finally {
3083
+ if (fd !== null) {
3084
+ try {
3085
+ fs.closeSync(fd);
3086
+ }
3087
+ catch { /* closed */ }
3088
+ try {
3089
+ fs.unlinkSync(lockFile);
3090
+ }
3091
+ catch { /* removed */ }
3092
+ }
3093
+ }
3094
+ }
3095
+ catch (error) {
3096
+ process.stderr.write(`[record-gate-claim] writer observation append failed (fail-open, capture unaffected): ${error instanceof Error ? error.message : String(error)}\n`);
3097
+ }
3098
+ }
2778
3099
  function requireObservedCommandRefs(refs, observedCommands, label, requireAll = false) {
2779
3100
  const commandRefs = refs.filter((ref) => ref.kind === "command");
2780
3101
  if (commandRefs.length === 0)
@@ -2789,7 +3110,7 @@ function requireObservedCommandRefs(refs, observedCommands, label, requireAll =
2789
3110
  die(`${label} requires a top-level command evidence ref for every successful observed command`);
2790
3111
  }
2791
3112
  }
2792
- function completePassingCriteria(existing, raw, observedCommands, verifiedAt) {
3113
+ function completePassingCriteria(existing, raw, observedCommands, verifiedAt, projectRoot) {
2793
3114
  if (raw.length === 0)
2794
3115
  die("record-gate-claim requires --criterion-json for a passing tests-evidence claim");
2795
3116
  const incoming = raw.map((value) => parseJson(value, "--criterion-json"));
@@ -2804,21 +3125,24 @@ function completePassingCriteria(existing, raw, observedCommands, verifiedAt) {
2804
3125
  if (new Set(ids).size !== ids.length || ids.length !== expectedIds.length || ids.some((id) => !expectedIds.includes(id))) {
2805
3126
  die(`--criterion-json must cover every declared acceptance criterion exactly once (expected: ${expectedIds.join(", ") || "none"}; received: ${ids.join(", ") || "none"})`);
2806
3127
  }
3128
+ const observedCommandNames = new Set(observedCommands.map((observation) => observation.command));
2807
3129
  return incoming.map((criterion, index) => {
2808
3130
  if (Object.keys(criterion).some((key) => !["id", "status", "evidence_refs"].includes(key)))
2809
3131
  die(`criterion ${ids[index]} may update only id, status, and evidence_refs`);
2810
3132
  if (criterion.status !== "pass")
2811
3133
  die(`criterion ${ids[index]} must have status pass for a passing tests-evidence claim`);
2812
- const refs = normalizeEvidenceRefs(criterion.evidence_refs, `criterion ${ids[index]} evidence_refs`);
3134
+ const refs = normalizeEvidenceRefs(criterion.evidence_refs, `criterion ${ids[index]} evidence_refs`, projectRoot);
2813
3135
  if (refs.length === 0)
2814
3136
  die(`criterion ${ids[index]} requires reviewable evidence_refs`);
2815
- requireObservedCommandRefs(refs, observedCommands, `criterion ${ids[index]}`);
2816
- return { ...expectedById.get(ids[index]), status: "pass", evidence_refs: refs, identity_version: 2, verified_at: verifiedAt };
3137
+ requireObservedCommandRefs(refs, observedCommandNames, `criterion ${ids[index]}`);
3138
+ const referencedCommands = new Set(refs.filter((ref) => ref.kind === "command").map(commandFromEvidenceRef));
3139
+ const criterionObservedCommands = observedCommands.filter((observation) => referencedCommands.has(observation.command));
3140
+ return markCanonicallyObservedCriterion({ ...expectedById.get(ids[index]), status: "pass", evidence_refs: refs, identity_version: 2, verified_at: verifiedAt, _observed_commands: criterionObservedCommands });
2817
3141
  });
2818
3142
  }
2819
3143
  const critiqueStatuses = new Set(WORKFLOW_CRITIQUE_STATUSES);
2820
3144
  const safeCritiqueId = /^[a-z][a-z0-9_-]*$/;
2821
- function normalizeCritiqueLanes(raw) {
3145
+ function normalizeCritiqueLanes(raw, projectRoot) {
2822
3146
  if (raw.length === 0)
2823
3147
  die("record-critique requires at least one --lane-json");
2824
3148
  const lanes = raw.map((value, index) => {
@@ -2832,7 +3156,7 @@ function normalizeCritiqueLanes(raw) {
2832
3156
  die(`--lane-json ${index} status must be one of: pass, fail, not_verified`);
2833
3157
  if (!hasNonEmptyString(lane.summary))
2834
3158
  die(`--lane-json ${index} summary must be non-empty`);
2835
- const evidenceRefs = normalizeEvidenceRefs(lane.evidence_refs, `--lane-json ${index} evidence_refs`);
3159
+ const evidenceRefs = normalizeEvidenceRefs(lane.evidence_refs, `--lane-json ${index} evidence_refs`, projectRoot);
2836
3160
  if (evidenceRefs.length === 0)
2837
3161
  die(`--lane-json ${index} requires structured reviewable evidence_refs`);
2838
3162
  return { id: lane.id, status: lane.status, summary: lane.summary, evidence_refs: evidenceRefs };
@@ -2851,6 +3175,13 @@ function reviewTargetArtifacts(dir, rawPaths, label) {
2851
3175
  return files.map((file) => ({ file, sha256: createHash("sha256").update(fs.readFileSync(path.join(projectRoot, file))).digest("hex") }));
2852
3176
  }
2853
3177
  function reviewTargetArtifactsMatch(dir, reviewTarget) {
3178
+ if (reviewTarget && typeof reviewTarget === "object" && !Array.isArray(reviewTarget)) {
3179
+ const projectRoot = narrativeGuardRoot(dir);
3180
+ const artifacts = reviewTarget.artifacts;
3181
+ if (Array.isArray(artifacts)) {
3182
+ artifacts.forEach((artifact, index) => rejectNarrativeReference(projectRoot, artifact?.file, `critique review_target artifact ${index}`));
3183
+ }
3184
+ }
2854
3185
  try {
2855
3186
  if (!reviewTarget || typeof reviewTarget !== "object" || Array.isArray(reviewTarget))
2856
3187
  return false;
@@ -2925,7 +3256,7 @@ function critiqueIsCleanAndCurrent(dir, critique) {
2925
3256
  // BEFORE normalizeCheck runs — see applyGateClaimStamp). This does not weaken new-mint
2926
3257
  // enforcement: a NOVEL gate-claim-* id (not already present at all) is still rejected exactly as
2927
3258
  // before, and superseding a STAMPED existing id is now rejected too.
2928
- export function normalizeCheck(raw, allowGateClaimPrefix = false, existingCheckStampById) {
3259
+ export function normalizeCheck(raw, allowGateClaimPrefix = false, existingCheckStampById, projectRoot = process.cwd()) {
2929
3260
  const check = { ...raw };
2930
3261
  if (!check.id || !check.kind || !check.status || !check.summary)
2931
3262
  die("check requires id, kind, status, and summary");
@@ -2940,18 +3271,21 @@ export function normalizeCheck(raw, allowGateClaimPrefix = false, existingCheckS
2940
3271
  die("kind must be one of: build, types, lint, test, command, security, diff, browser, runtime, policy, external");
2941
3272
  if (!checkStatuses.has(check.status))
2942
3273
  die("status must be one of: pass, fail, not_verified, skip");
3274
+ rejectNarrativeReference(projectRoot, check.command, `check ${String(check.id)} command`);
2943
3275
  validateRunnableCheckCommand(check, `check ${String(check.id)}`);
2944
3276
  if (Array.isArray(check.standard_refs))
2945
- for (const ref of check.standard_refs)
3277
+ for (const ref of check.standard_refs) {
2946
3278
  if (!["junit", "sarif", "coverage", "veritas"].includes(ref.standard))
2947
3279
  die("standard must be one of");
3280
+ rejectNarrativeReference(projectRoot, ref.ref, `check ${String(check.id)} standard_ref`);
3281
+ }
2948
3282
  if (check.artifact_refs)
2949
- check.artifact_refs = normalizeEvidenceRefs(check.artifact_refs, "artifact_refs");
3283
+ check.artifact_refs = normalizeEvidenceRefs(check.artifact_refs, "artifact_refs", projectRoot);
2950
3284
  if (check.surface_trust_refs)
2951
- check.surface_trust_refs = normalizeSurfaceRefs(check.surface_trust_refs);
3285
+ check.surface_trust_refs = normalizeSurfaceRefs(check.surface_trust_refs, projectRoot);
2952
3286
  return check;
2953
3287
  }
2954
- function normalizeSurfaceRefs(refs) {
3288
+ function normalizeSurfaceRefs(refs, projectRoot) {
2955
3289
  if (!Array.isArray(refs))
2956
3290
  die("surface_trust_refs must be an array");
2957
3291
  // Use the cached @kontourai/surface module for advisory inline validation of referenced
@@ -2964,6 +3298,7 @@ function normalizeSurfaceRefs(refs) {
2964
3298
  if (key.toLowerCase().includes("veritas"))
2965
3299
  die(`unsupported field in Surface trust ref: ${key}`);
2966
3300
  const out = { ...ref };
3301
+ rejectNarrativeReference(projectRoot, out.artifact_ref, "surface trust artifact_ref");
2967
3302
  // trust.bundle is the canonical Hachure-aligned artifact kind; TrustReport/Trust Snapshot are legacy aliases
2968
3303
  if (!["trust.bundle", "TrustReport", "Trust Snapshot"].includes(out.artifact_kind))
2969
3304
  die("artifact_kind must be one of: trust.bundle, TrustReport, Trust Snapshot");
@@ -3048,17 +3383,19 @@ export function kitIdentityFromBundle(raw, bundleFile) {
3048
3383
  // 3. Genuinely unknown — never fallback to "builder".
3049
3384
  return { claimType: "unknown.trust.bundle", kitId: "unknown", subject: "unknown-kit", gateId: "unknown.trust.bundle" };
3050
3385
  }
3051
- function surfaceCheckFromArtifact(file, index) {
3052
- const raw = JSON.parse(read(file));
3386
+ function surfaceCheckFromArtifact(file, index, projectRoot = process.cwd()) {
3387
+ rejectNarrativeReference(projectRoot, file, `--surface-trust-json ${index}`);
3388
+ const resolvedFile = path.isAbsolute(file) ? file : path.resolve(projectRoot, decodeNarrativeReference(file));
3389
+ const raw = JSON.parse(read(resolvedFile));
3053
3390
  const lower = JSON.stringify(raw).toLowerCase();
3054
3391
  // Structurally read kit identity from the bundle — never hardcode "builder".
3055
- const { claimType: bundleClaimType, subject: bundleSubject, gateId: bundleGateId } = kitIdentityFromBundle(raw, file);
3392
+ const { claimType: bundleClaimType, subject: bundleSubject, gateId: bundleGateId } = kitIdentityFromBundle(raw, resolvedFile);
3056
3393
  let ref;
3057
3394
  if (lower.includes("provider") && lower.includes("absent")) {
3058
- ref = { artifact_kind: "trust.bundle", artifact_ref: file, gate_id: "provider.unavailable", claim_type: bundleClaimType, claim_status: "unknown", subject: bundleSubject, freshness: { status: "unknown", summary: "No trust provider is configured" }, authority: { producer: "unknown", summary: "No trust provider is configured" }, integrity: { status: "unknown", summary: "Unknown" }, status: "not_verified", summary: "No trust provider is configured" };
3395
+ ref = { artifact_kind: "trust.bundle", artifact_ref: resolvedFile, gate_id: "provider.unavailable", claim_type: bundleClaimType, claim_status: "unknown", subject: bundleSubject, freshness: { status: "unknown", summary: "No trust provider is configured" }, authority: { producer: "unknown", summary: "No trust provider is configured" }, integrity: { status: "unknown", summary: "Unknown" }, status: "not_verified", summary: "No trust provider is configured" };
3059
3396
  }
3060
3397
  else if (lower.includes("artifact") && lower.includes("absent")) {
3061
- ref = { artifact_kind: "trust.bundle", artifact_ref: file, gate_id: "artifact.unavailable", claim_type: bundleClaimType, claim_status: "unknown", subject: bundleSubject, freshness: { status: "unknown", summary: "Artifact not readable" }, authority: { producer: "unknown", summary: "Artifact not readable" }, integrity: { status: "unknown", summary: "Artifact not readable" }, status: "not_verified", summary: "artifact not readable" };
3398
+ ref = { artifact_kind: "trust.bundle", artifact_ref: resolvedFile, gate_id: "artifact.unavailable", claim_type: bundleClaimType, claim_status: "unknown", subject: bundleSubject, freshness: { status: "unknown", summary: "Artifact not readable" }, authority: { producer: "unknown", summary: "Artifact not readable" }, integrity: { status: "unknown", summary: "Artifact not readable" }, status: "not_verified", summary: "artifact not readable" };
3062
3399
  }
3063
3400
  else {
3064
3401
  const claimStatus = lower.includes("rejected") ? "rejected" : "accepted";
@@ -3066,7 +3403,7 @@ function surfaceCheckFromArtifact(file, index) {
3066
3403
  const producer = lower.includes("missing-authority") ? "unknown" : "surface-local";
3067
3404
  const integrity = lower.includes("mismatch") ? "mismatch" : "matched";
3068
3405
  // Use trust.bundle as the canonical Hachure-aligned artifact_kind for all trust-backed evidence refs
3069
- ref = { artifact_kind: "trust.bundle", artifact_ref: file, gate_id: bundleGateId, claim_type: bundleClaimType, claim_status: claimStatus, subject: bundleSubject, freshness: { status: freshness, summary: freshness === "fresh" ? "fresh" : "not currently verifiable" }, authority: { producer, summary: producer === "unknown" ? "missing authority" : "Local Surface trust producer." }, integrity: { status: integrity, summary: integrity === "matched" ? "matched" : "integrity mismatch" } };
3406
+ ref = { artifact_kind: "trust.bundle", artifact_ref: resolvedFile, gate_id: bundleGateId, claim_type: bundleClaimType, claim_status: claimStatus, subject: bundleSubject, freshness: { status: freshness, summary: freshness === "fresh" ? "fresh" : "not currently verifiable" }, authority: { producer, summary: producer === "unknown" ? "missing authority" : "Local Surface trust producer." }, integrity: { status: integrity, summary: integrity === "matched" ? "matched" : "integrity mismatch" } };
3070
3407
  ref.status = deriveSurfaceStatus(ref);
3071
3408
  ref.summary = ref.status === "pass" ? "accepted" : ref.status === "not_verified" ? "not currently verifiable" : (claimStatus === "rejected" ? "rejected" : producer === "unknown" ? "missing authority" : "integrity mismatch");
3072
3409
  }
@@ -3084,9 +3421,10 @@ function surfaceCheckFromArtifact(file, index) {
3084
3421
  * not the first thing an agent reaches for.
3085
3422
  */
3086
3423
  function validateAcceptanceEvidenceRefs(dir, p) {
3087
- if (p?.flags.has("skip-evidence-ref-runnability-guard")) {
3424
+ const projectRoot = narrativeGuardRoot(dir);
3425
+ const skipRunnabilityGuard = p?.flags.has("skip-evidence-ref-runnability-guard") ?? false;
3426
+ if (skipRunnabilityGuard) {
3088
3427
  process.stderr.write("[record-evidence] evidence-ref runnability guard skipped via --skip-evidence-ref-runnability-guard\n");
3089
- return;
3090
3428
  }
3091
3429
  const file = path.join(dir, "acceptance.json");
3092
3430
  if (!fs.existsSync(file))
@@ -3098,7 +3436,9 @@ function validateAcceptanceEvidenceRefs(dir, p) {
3098
3436
  data.criteria.forEach((criterion, index) => {
3099
3437
  if (criterion.evidence_refs === undefined)
3100
3438
  return;
3101
- const refs = normalizeEvidenceRefs(criterion.evidence_refs, `acceptance.criteria[${index}].evidence_refs`);
3439
+ const refs = normalizeEvidenceRefs(criterion.evidence_refs, `acceptance.criteria[${index}].evidence_refs`, projectRoot);
3440
+ if (skipRunnabilityGuard)
3441
+ return;
3102
3442
  // #412 (AC8): a kind:"command" ref's command source (excerpt, falling back to url when that
3103
3443
  // is where the command string lives) must be a literally runnable shell command, not prose
3104
3444
  // describing a manual verification step — reject at RECORD time instead of letting it reach
@@ -3170,8 +3510,15 @@ function requireStampedClaim(claim, dir) {
3170
3510
  }
3171
3511
  return origin;
3172
3512
  }
3513
+ function loadTrustBundleForTrustMachinery(dir) {
3514
+ const bundleFile = path.join(dir, "trust.bundle");
3515
+ if (fs.existsSync(bundleFile) && isNarrativeArtifactContent(fs.readFileSync(bundleFile))) {
3516
+ die(`trust.bundle in ${dir}: ${NARRATIVE_TRUST_ISOLATION_DIAGNOSTIC}; restore a genuine trust.bundle and keep rendered narratives under ${NARRATIVE_NAMESPACE_ROOT}`);
3517
+ }
3518
+ return loadJson(bundleFile);
3519
+ }
3173
3520
  function checksFromBundle(dir) {
3174
- const bundle = loadJson(path.join(dir, "trust.bundle"));
3521
+ const bundle = loadTrustBundleForTrustMachinery(dir);
3175
3522
  const allClaims = Array.isArray(bundle.claims) ? bundle.claims : [];
3176
3523
  // Validate stamps on every claim up front — any unstamped claim anywhere in the bundle marks
3177
3524
  // it pre-supersession, regardless of whether it is check/acceptance/critique-typed.
@@ -3395,10 +3742,20 @@ function readBundleState(dir) {
3395
3742
  const acceptance = loadJson(path.join(dir, "acceptance.json"));
3396
3743
  const bundledCriteria = criteriaFromBundle(dir);
3397
3744
  const acceptedCriteria = Array.isArray(acceptance.criteria) ? acceptance.criteria : [];
3745
+ if (acceptedCriteria.some((criterion) => !hasNonEmptyString(criterion?.id) || !hasNonEmptyString(criterion?.description))) {
3746
+ die("acceptance.json contains a criterion without a non-empty id and description — refusing to rebuild trust.bundle without durable acceptance identity");
3747
+ }
3748
+ const acceptedIds = acceptedCriteria.map((criterion) => String(criterion.id));
3749
+ if (new Set(acceptedIds).size !== acceptedIds.length) {
3750
+ die("acceptance.json criterion ids must be unique — refusing to rebuild trust.bundle with ambiguous acceptance identity");
3751
+ }
3398
3752
  const contractSignature = (criteria) => JSON.stringify(criteria.map((criterion) => ({
3399
3753
  id: criterion.id ?? null,
3400
3754
  description: criterion.description ?? null,
3401
3755
  })));
3756
+ // Planning may establish or refine a valid criterion contract after session startup. Once the
3757
+ // signatures agree, the bundle remains authoritative for verified status and observed-command
3758
+ // provenance; artifact status edits alone cannot manufacture completion.
3402
3759
  const criteria = acceptedCriteria.length > 0 && contractSignature(acceptedCriteria) !== contractSignature(bundledCriteria)
3403
3760
  ? acceptedCriteria
3404
3761
  : (bundledCriteria.length > 0 ? bundledCriteria : acceptedCriteria);
@@ -3426,7 +3783,7 @@ function mergeChecksById(existing, incoming) {
3426
3783
  return [...byId.values()];
3427
3784
  }
3428
3785
  function critiquesFromBundle(dir) {
3429
- const bundle = loadJson(path.join(dir, "trust.bundle"));
3786
+ const bundle = loadTrustBundleForTrustMachinery(dir);
3430
3787
  if (!Array.isArray(bundle.claims))
3431
3788
  return [];
3432
3789
  for (const c of bundle.claims)
@@ -3453,7 +3810,7 @@ function critiquesFromBundle(dir) {
3453
3810
  });
3454
3811
  }
3455
3812
  function criteriaFromBundle(dir) {
3456
- const bundle = loadJson(path.join(dir, "trust.bundle"));
3813
+ const bundle = loadTrustBundleForTrustMachinery(dir);
3457
3814
  if (!Array.isArray(bundle.claims))
3458
3815
  return [];
3459
3816
  for (const claim of bundle.claims)
@@ -3463,14 +3820,28 @@ function criteriaFromBundle(dir) {
3463
3820
  .map((claim) => {
3464
3821
  const md = claim.metadata && typeof claim.metadata === "object" && !Array.isArray(claim.metadata) ? claim.metadata : {};
3465
3822
  const saved = md.criterion && typeof md.criterion === "object" && !Array.isArray(md.criterion) ? md.criterion : {};
3466
- return {
3823
+ const criterion = {
3467
3824
  id: typeof saved.id === "string" ? saved.id : String(claim.subjectId || "").split("/").pop(),
3468
3825
  description: typeof saved.description === "string" ? saved.description : (claim.fieldOrBehavior || ""),
3469
3826
  status: typeof saved.status === "string" ? saved.status : (claim.value ?? "not_verified"),
3470
3827
  evidence_refs: Array.isArray(saved.evidence_refs) ? saved.evidence_refs : [],
3828
+ ...(Array.isArray(saved.observed_commands) ? { _observed_commands: saved.observed_commands } : {}),
3471
3829
  ...(typeof saved.verified_at === "string" ? { verified_at: saved.verified_at } : {}),
3472
3830
  ...(saved.identity_version === 2 ? { identity_version: 2 } : {}),
3473
3831
  };
3832
+ const observedCommands = Array.isArray(saved.observed_commands) ? saved.observed_commands : [];
3833
+ const evidence = Array.isArray(bundle.evidence) ? bundle.evidence.filter((item) => item?.claimId === claim.id) : [];
3834
+ const event = Array.isArray(bundle.events) ? bundle.events.find((item) => item?.claimId === claim.id && item.status === "verified") : null;
3835
+ const evidenceIds = new Set(evidence.map((item) => item.id));
3836
+ const hasCanonicalEvidence = claim.value === "pass"
3837
+ && claim.status === "verified"
3838
+ && observedCommands.length > 0
3839
+ && evidence.length === observedCommands.length
3840
+ && evidence.every((item) => item.evidenceType === "test_output" && item.passing === true && observedCommands.some((observation) => observation.command === item.execution?.label))
3841
+ && Array.isArray(event?.evidenceIds)
3842
+ && event.evidenceIds.length === evidenceIds.size
3843
+ && event.evidenceIds.every((id) => evidenceIds.has(id));
3844
+ return hasCanonicalEvidence ? markCanonicallyObservedCriterion(criterion) : criterion;
3474
3845
  })
3475
3846
  .filter((criterion) => typeof criterion.id === "string" && criterion.id.length > 0);
3476
3847
  }
@@ -3508,7 +3879,11 @@ async function recordEvidence(p) {
3508
3879
  // (not re-read) as _existingState for the compose-safe merge — one readBundleState call.
3509
3880
  const _existingState = readBundleState(dir);
3510
3881
  const _existingCheckStampById = existingCheckStampMap(_existingState.checks);
3511
- const _checksRaw = [...opts(p, "check-json").map((v) => normalizeCheck(parseJson(v, "--check-json"), false, _existingCheckStampById)), ...opts(p, "surface-trust-json").map(surfaceCheckFromArtifact)];
3882
+ const projectRoot = narrativeGuardRoot(dir);
3883
+ const _checksRaw = [
3884
+ ...opts(p, "check-json").map((v) => normalizeCheck(parseJson(v, "--check-json"), false, _existingCheckStampById, projectRoot)),
3885
+ ...opts(p, "surface-trust-json").map((file, index) => surfaceCheckFromArtifact(file, index, projectRoot)),
3886
+ ];
3512
3887
  // WS8 (AC4, iteration 2): a command-backed check reconciles against CI or fails — it can
3513
3888
  // NEVER be waived. Reject --accepted-gap-reason/--waived-by on any check whose evidence
3514
3889
  // classifies as test_output (build/types/lint/test/command, and security/browser/runtime
@@ -3600,6 +3975,7 @@ async function recordCheck(p, commandArgv) {
3600
3975
  if (commandArgv?.length && commandString)
3601
3976
  die("record-check: pass the command via EITHER `-- <command...>` OR --command, not both");
3602
3977
  const repoRoot = findRepoRootFromDir(dir);
3978
+ rejectNarrativeReference(repoRoot, commandArgv?.join(" ") ?? commandString, "record-check command");
3603
3979
  let displayCommand;
3604
3980
  let exitCode;
3605
3981
  let stdout = "";
@@ -3681,7 +4057,7 @@ ${stderr}` : ""}`.trim());
3681
4057
  status,
3682
4058
  summary,
3683
4059
  command: displayCommand,
3684
- }, false, _existingCheckStampById);
4060
+ }, false, _existingCheckStampById, repoRoot);
3685
4061
  if (outputSha256)
3686
4062
  check._output_sha256 = outputSha256;
3687
4063
  const _mergedChecks = mergeChecksById(_existingState.checks, [check]);
@@ -3792,10 +4168,20 @@ async function recordGateClaim(p) {
3792
4168
  const observedCommandRaw = opts(p, "observed-command-json");
3793
4169
  if (mustRunTests && gateCommands.length === 0)
3794
4170
  die("record-gate-claim requires at least one --command for a passing tests-evidence claim");
3795
- const projectRoot = gateCommands.length > 0 ? canonicalProjectRootForSession(dir) : null;
4171
+ // #619: the narrative evidence-ref guards (validateEvidenceRef / normalizeCheck /
4172
+ // completePassingCriteria) need a non-null, location-independent project root that never
4173
+ // dies on a non-canonical session. normalizeObservedCommands still requires the strict
4174
+ // canonical root, but only when there are commands to normalize.
4175
+ const projectRoot = narrativeGuardRoot(dir);
4176
+ const canonicalRoot = gateCommands.length > 0 ? canonicalProjectRootForSession(dir) : null;
4177
+ for (const command of gateCommands)
4178
+ rejectNarrativeReference(projectRoot, command, "record-gate-claim command");
3796
4179
  const observedCommands = gateCommands.length > 0
3797
- ? await normalizeObservedCommands(gateCommands, projectRoot, mustRunTests, statusVal)
4180
+ ? await normalizeObservedCommands(gateCommands, canonicalRoot, mustRunTests, statusVal)
3798
4181
  : [];
4182
+ // #634: persist the writer's real executions into the hash-chained command-log so the
4183
+ // capture fold has a deterministic observation even on exit-code-blind hosts.
4184
+ appendWriterObservedCommands(dir, observedCommands, ts);
3799
4185
  const observedCommandNames = new Set(observedCommands.map((entry) => entry.command));
3800
4186
  let outputSha256 = null;
3801
4187
  if (!mustRunTests && gateCommands.length > 1)
@@ -3826,7 +4212,7 @@ async function recordGateClaim(p) {
3826
4212
  ...(routeReason ? { _gate_claim_route_reason: routeReason } : {}),
3827
4213
  };
3828
4214
  // Include structured evidence refs if provided
3829
- const evidenceRefs = opts(p, "evidence-ref-json").map((v) => validateEvidenceRef(parseJson(v, "--evidence-ref-json"), "--evidence-ref-json"));
4215
+ const evidenceRefs = opts(p, "evidence-ref-json").map((v) => validateEvidenceRef(parseJson(v, "--evidence-ref-json"), "--evidence-ref-json", projectRoot));
3830
4216
  const producer = expectedGateProducer(exactFlowContext?.flowId ?? activeStep.flowId, activeStep.stepId, targetExpectation.id);
3831
4217
  if (statusVal === "pass")
3832
4218
  validateReviewableGateEvidence(dir, slug, evidenceRefs, producer, `gate claim ${targetExpectation.id}`);
@@ -3849,7 +4235,7 @@ async function recordGateClaim(p) {
3849
4235
  check.command = gateCommand;
3850
4236
  if (observedCommands.length > 0)
3851
4237
  check._observed_commands = observedCommands;
3852
- const checkNormalized = normalizeCheck(check, /* allowGateClaimPrefix */ true);
4238
+ const checkNormalized = normalizeCheck(check, /* allowGateClaimPrefix */ true, undefined, projectRoot);
3853
4239
  if (outputSha256)
3854
4240
  checkNormalized._output_sha256 = outputSha256;
3855
4241
  // WS8 (ADR 0020): honor the accepted-gap waiver flags for a gate claim too.
@@ -3864,7 +4250,7 @@ async function recordGateClaim(p) {
3864
4250
  // SAME expectation id supersedes the earlier check for that expectation (mergeChecksById); a
3865
4251
  // gate claim against a different expectation is additive.
3866
4252
  const _existingState = readBundleState(dir);
3867
- const criteria = mustRunTests ? completePassingCriteria(_existingState.criteria, opts(p, "criterion-json"), observedCommandNames, ts) : _existingState.criteria;
4253
+ const criteria = mustRunTests ? completePassingCriteria(_existingState.criteria, opts(p, "criterion-json"), observedCommands, ts, projectRoot) : _existingState.criteria;
3868
4254
  if (mustRunTests) {
3869
4255
  const liveCritiques = _existingState.critiques.filter((critique) => !critique.superseded_by);
3870
4256
  if (liveCritiques.length === 0 || liveCritiques.some((critique) => !critiqueIsCleanAndCurrent(dir, critique))) {
@@ -3923,6 +4309,7 @@ async function promote(p) {
3923
4309
  // repo-relative paths when the ref lives under the repo, so the claim is portable.
3924
4310
  const targets = [];
3925
4311
  for (const raw of rawPaths) {
4312
+ rejectNarrativeReference(repoRoot, raw, "promote --evidence-path");
3926
4313
  const abs = path.isAbsolute(raw) ? raw : path.resolve(repoRoot, raw);
3927
4314
  if (!fs.existsSync(abs))
3928
4315
  die(`promote --evidence-path does not exist on disk: ${raw} (resolved: ${abs}). Promotion evidence refs must point at durable docs that were actually written.`);
@@ -4053,21 +4440,27 @@ async function advanceState(p) {
4053
4440
  }
4054
4441
  return 0;
4055
4442
  }
4056
- export function normalizeFinding(raw) {
4443
+ export function normalizeFinding(raw, projectRoot = process.cwd()) {
4057
4444
  if (raw.file_refs !== undefined && !Array.isArray(raw.file_refs))
4058
4445
  die("file_refs must be an array");
4446
+ for (const ref of raw.file_refs ?? []) {
4447
+ if (typeof ref !== "string")
4448
+ die("file_refs entries must be strings");
4449
+ rejectNarrativeReference(projectRoot, ref, "finding file_ref");
4450
+ }
4059
4451
  return raw;
4060
4452
  }
4061
4453
  async function recordCritique(p) {
4062
4454
  const dir = artifactDirFrom(p.positional[0] || die("artifact directory is required"));
4063
4455
  const slug = taskSlugFor(dir, opt(p, "task-slug"));
4456
+ const projectRoot = canonicalProjectRootForSession(dir);
4064
4457
  const verdict = opt(p, "verdict");
4065
4458
  if (!critiqueStatuses.has(verdict))
4066
4459
  die("record-critique requires --verdict pass, fail, or not_verified");
4067
4460
  const summary = opt(p, "summary");
4068
4461
  if (!hasNonEmptyString(summary))
4069
4462
  die("record-critique requires --summary");
4070
- const lanes = normalizeCritiqueLanes(opts(p, "lane-json"));
4463
+ const lanes = normalizeCritiqueLanes(opts(p, "lane-json"), projectRoot);
4071
4464
  const reviewArtifacts = reviewTargetArtifacts(dir, opts(p, "artifact-ref"), "record-critique review_target");
4072
4465
  if (verdict === "pass" && (lanes.some((lane) => lane.status !== "pass") || reviewArtifacts.length === 0)) {
4073
4466
  die("a passing critique requires every lane to pass and at least one local reviewed --artifact-ref");
@@ -4097,10 +4490,10 @@ async function recordCritique(p) {
4097
4490
  lanes,
4098
4491
  review_target: {
4099
4492
  artifacts: reviewArtifacts,
4100
- workspace_snapshot: captureReviewWorkspaceSnapshot(canonicalProjectRootForSession(dir), reviewArtifacts.map((artifact) => ({ file: String(artifact.file), sha256: String(artifact.sha256) }))),
4493
+ workspace_snapshot: captureReviewWorkspaceSnapshot(projectRoot, reviewArtifacts.map((artifact) => ({ file: String(artifact.file), sha256: String(artifact.sha256) }))),
4101
4494
  },
4102
4495
  artifact_refs: reviewArtifacts.map((artifact) => artifact.file),
4103
- findings: opts(p, "finding-json").map((v) => normalizeFinding(parseJson(v, "--finding-json"))),
4496
+ findings: opts(p, "finding-json").map((v) => normalizeFinding(parseJson(v, "--finding-json"), projectRoot)),
4104
4497
  };
4105
4498
  if (critique.verdict === "pass" && critique.findings.some((f) => f.status === "open"))
4106
4499
  die("required critique must pass");
@@ -4175,16 +4568,46 @@ async function importCritique(p) {
4175
4568
  die("required critique must pass");
4176
4569
  return result;
4177
4570
  }
4571
+ function rejectNarrativeReleaseReferences(projectRoot, value, label, referenceField = false) {
4572
+ if (typeof value === "string") {
4573
+ if (referenceField)
4574
+ rejectNarrativeReference(projectRoot, value, label);
4575
+ return;
4576
+ }
4577
+ if (Array.isArray(value)) {
4578
+ for (const entry of value)
4579
+ rejectNarrativeReleaseReferences(projectRoot, entry, label, referenceField);
4580
+ return;
4581
+ }
4582
+ if (!value || typeof value !== "object")
4583
+ return;
4584
+ for (const [key, entry] of Object.entries(value)) {
4585
+ const isReferenceField = /^(?:refs?|files?|paths?|(?:evidence|artifact|source)_(?:refs?|files?|paths?))$/.test(key);
4586
+ rejectNarrativeReleaseReferences(projectRoot, entry, `${label}.${key}`, isReferenceField);
4587
+ }
4588
+ }
4178
4589
  async function recordRelease(p) {
4179
4590
  const dir = artifactDirFrom(p.positional[0] || die("artifact directory is required"));
4180
4591
  const slug = taskSlugFor(dir, opt(p, "task-slug"));
4592
+ const projectRoot = narrativeGuardRoot(dir);
4181
4593
  const decision = opt(p, "decision");
4182
4594
  if (!["merge", "release", "deploy", "hold", "rollback_required"].includes(decision))
4183
4595
  die("decision must be one of: merge, release, deploy, hold, rollback_required");
4184
4596
  const gates = opts(p, "gate-json").map((v) => parseJson(v, "--gate-json"));
4185
4597
  if (["merge", "release", "deploy"].includes(decision) && !gates.some((g) => g.name === decision && g.status === "pass"))
4186
4598
  die(`positive release decision requires ${decision} gate to pass`);
4187
- const payload = { ...sidecarBase(slug), decision: opt(p, "decision"), updated_at: opt(p, "timestamp", now()), scope: opt(p, "scope"), evidence_ref: opt(p, "evidence-ref"), gates: opts(p, "gate-json").map((v) => parseJson(v, "--gate-json")), rollback_plan: parseJson(opt(p, "rollback-json", '{"status":"not_required","summary":"Not required.","owner":"maintainer"}'), "--rollback-json"), observability_plan: parseJson(opt(p, "observability-json", '{"status":"not_required","summary":"Not required."}'), "--observability-json"), post_deploy_checks: opts(p, "post-deploy-json").map((v) => parseJson(v, "--post-deploy-json")), docs: parseJson(opt(p, "docs-json", '{"status":"not_needed","summary":"Not needed."}'), "--docs-json") };
4599
+ const evidenceRef = opt(p, "evidence-ref");
4600
+ const rollbackPlan = parseJson(opt(p, "rollback-json", '{"status":"not_required","summary":"Not required.","owner":"maintainer"}'), "--rollback-json");
4601
+ const observabilityPlan = parseJson(opt(p, "observability-json", '{"status":"not_required","summary":"Not required."}'), "--observability-json");
4602
+ const postDeployChecks = opts(p, "post-deploy-json").map((v) => parseJson(v, "--post-deploy-json"));
4603
+ const docs = parseJson(opt(p, "docs-json", '{"status":"not_needed","summary":"Not needed."}'), "--docs-json");
4604
+ rejectNarrativeReference(projectRoot, evidenceRef, "release evidence_ref");
4605
+ rejectNarrativeReleaseReferences(projectRoot, gates, "release gates");
4606
+ rejectNarrativeReleaseReferences(projectRoot, rollbackPlan, "release rollback_plan");
4607
+ rejectNarrativeReleaseReferences(projectRoot, observabilityPlan, "release observability_plan");
4608
+ rejectNarrativeReleaseReferences(projectRoot, postDeployChecks, "release post_deploy_checks");
4609
+ rejectNarrativeReleaseReferences(projectRoot, docs, "release docs");
4610
+ const payload = { ...sidecarBase(slug), decision: opt(p, "decision"), updated_at: opt(p, "timestamp", now()), scope: opt(p, "scope"), evidence_ref: evidenceRef, gates, rollback_plan: rollbackPlan, observability_plan: observabilityPlan, post_deploy_checks: postDeployChecks, docs };
4188
4611
  const stateSummary = opt(p, "summary").trim() || `Release readiness recorded for ${decision}.`;
4189
4612
  writeJson(path.join(dir, "release.json"), payload);
4190
4613
  writeState(dir, slug, "delivered", "release", payload.updated_at, stateSummary);
@@ -5460,9 +5883,14 @@ function validateLearningPrevention(prevention) {
5460
5883
  if (!["open", "completed", "accepted", "deferred", "rejected"].includes(value.status))
5461
5884
  die("correction.prevention.status must be one of: open, completed, accepted, deferred, rejected");
5462
5885
  }
5463
- export function normalizeLearning(raw, timestamp) {
5886
+ export function normalizeLearning(raw, timestamp, projectRoot = process.cwd()) {
5464
5887
  if (!Array.isArray(raw.source_refs))
5465
5888
  die("source_refs must be an array");
5889
+ for (const ref of raw.source_refs) {
5890
+ if (typeof ref !== "string")
5891
+ die("source_refs entries must be strings");
5892
+ rejectNarrativeReference(projectRoot, ref, "learning source_ref");
5893
+ }
5466
5894
  if (!Array.isArray(raw.facts))
5467
5895
  die("facts must be an array");
5468
5896
  if (!Array.isArray(raw.routing))
@@ -5476,7 +5904,8 @@ async function recordLearning(p) {
5476
5904
  const dir = artifactDirFrom(p.positional[0] || die("artifact directory is required"));
5477
5905
  const slug = taskSlugFor(dir, opt(p, "task-slug"));
5478
5906
  const timestamp = opt(p, "timestamp", now());
5479
- const records = opts(p, "record-json").map((v) => normalizeLearning(parseJson(v, "--record-json"), timestamp));
5907
+ const projectRoot = narrativeGuardRoot(dir);
5908
+ const records = opts(p, "record-json").map((v) => normalizeLearning(parseJson(v, "--record-json"), timestamp, projectRoot));
5480
5909
  const status = opt(p, "status", "learned");
5481
5910
  if (status === "learned" && records.some((r) => r.routing.some((x) => x.status === "open")))
5482
5911
  die("learning status learned cannot have open routing");
@@ -5498,7 +5927,7 @@ function evidenceClean(dir) {
5498
5927
  // legacy (pre-bundle-era) sessions that never wrote a trust.bundle at all — unrelated to origin
5499
5928
  // stamping. When a trust.bundle IS present, every claim must be stamped (requireStampedClaim);
5500
5929
  // there is no claimType-derivation fallback for an unstamped claim (#268/#344).
5501
- const bundle = loadJson(path.join(dir, "trust.bundle"));
5930
+ const bundle = loadTrustBundleForTrustMachinery(dir);
5502
5931
  if (Array.isArray(bundle.claims)) {
5503
5932
  for (const c of bundle.claims)
5504
5933
  requireStampedClaim(c, dir);
@@ -5520,7 +5949,7 @@ function evidenceClean(dir) {
5520
5949
  }
5521
5950
  function critiqueClean(dir) {
5522
5951
  // trust.bundle is the sole critique artifact. Legacy critique.json must not influence gates.
5523
- const bundle = loadJson(path.join(dir, "trust.bundle"));
5952
+ const bundle = loadTrustBundleForTrustMachinery(dir);
5524
5953
  if (Array.isArray(bundle.claims)) {
5525
5954
  for (const c of bundle.claims)
5526
5955
  requireStampedClaim(c, dir);
@@ -5545,11 +5974,17 @@ function assertExistingLearningValid(dir) {
5545
5974
  if (!fs.existsSync(file))
5546
5975
  return;
5547
5976
  const data = loadJson(file);
5977
+ const projectRoot = narrativeGuardRoot(dir);
5548
5978
  if (!Array.isArray(data.records))
5549
5979
  die("learning records must be an array");
5550
5980
  for (const record of data.records) {
5551
5981
  if (!Array.isArray(record.source_refs))
5552
5982
  die("source_refs must be an array");
5983
+ for (const ref of record.source_refs) {
5984
+ if (typeof ref !== "string")
5985
+ die("source_refs entries must be strings");
5986
+ rejectNarrativeReference(projectRoot, ref, "learning source_ref");
5987
+ }
5553
5988
  if (!Array.isArray(record.facts))
5554
5989
  die("facts must be an array");
5555
5990
  if (!Array.isArray(record.routing))
@@ -5563,6 +5998,7 @@ async function dogfoodPass(p) {
5563
5998
  const root = opt(p, "artifact-root") ? path.resolve(opt(p, "artifact-root")) : defaultArtifactRootForRead();
5564
5999
  const dir = path.resolve(opt(p, "artifact-dir") || currentDir(root) || "");
5565
6000
  requireArtifactDirUnderRoot(dir, root);
6001
+ const projectRoot = narrativeGuardRoot(dir);
5566
6002
  assertExistingLearningValid(dir);
5567
6003
  const verdict = opt(p, "verdict");
5568
6004
  if (verdict === "pass") {
@@ -5575,7 +6011,7 @@ async function dogfoodPass(p) {
5575
6011
  // trust.bundle does not yet exist (loadJson falls back to {}), exactly as
5576
6012
  // recordEvidence/recordCheck rely on.
5577
6013
  const _dogfoodExistingCheckStampById = existingCheckStampMap(readBundleState(dir).checks);
5578
- const checks = opts(p, "check-json").map((v) => normalizeCheck(parseJson(v, "--check-json"), false, _dogfoodExistingCheckStampById));
6014
+ const checks = opts(p, "check-json").map((v) => normalizeCheck(parseJson(v, "--check-json"), false, _dogfoodExistingCheckStampById, projectRoot));
5579
6015
  if (checks.some((c) => c.status !== "pass" && c.status !== "skip"))
5580
6016
  die("clean evidence requires all non-skipped checks to pass");
5581
6017
  // Phase 4c: evidence check reads from trust.bundle (sole verification artifact); legacy evidence.json fallback in evidenceClean.
@@ -5591,7 +6027,7 @@ async function dogfoodPass(p) {
5591
6027
  if (p.flags.has("require-critique") || opt(p, "release-decision")) {
5592
6028
  const newCritiqueVerdict = opt(p, "critique-verdict", "pass");
5593
6029
  for (const value of opts(p, "finding-json"))
5594
- normalizeFinding(parseJson(value, "--finding-json"));
6030
+ normalizeFinding(parseJson(value, "--finding-json"), projectRoot);
5595
6031
  if (newCritiqueVerdict !== "pass")
5596
6032
  die(opt(p, "release-decision") ? "requires clean critique" : "requires clean critique before recording pass evidence");
5597
6033
  if (!opt(p, "critique-id") && !critiqueClean(dir))
@@ -5600,7 +6036,7 @@ async function dogfoodPass(p) {
5600
6036
  die(opt(p, "release-decision") ? "requires clean critique" : "requires clean critique before recording pass evidence");
5601
6037
  }
5602
6038
  }
5603
- const learningRecords = opts(p, "learning-record-json").map((v) => normalizeLearning(parseJson(v, "--learning-record-json"), opt(p, "timestamp", now())));
6039
+ const learningRecords = opts(p, "learning-record-json").map((v) => normalizeLearning(parseJson(v, "--learning-record-json"), opt(p, "timestamp", now()), projectRoot));
5604
6040
  if (opt(p, "learning-status") === "learned" && learningRecords.some((r) => r.routing.some((x) => x.status === "open")))
5605
6041
  die("learned status cannot have open learning routing");
5606
6042
  if (opt(p, "learning-status") === "learned" && learningRecords.some((r) => r.correction === undefined))
@@ -5710,7 +6146,7 @@ export function gateAdvisoryFix(calibration, claimId, answerStatus) {
5710
6146
  return `Ensure \`workflow-sidecar record-evidence\` writes a definitive event for claim \`${claimId}\` (currently \`${s}\`) before \`stop-goal-fit\` evaluates. The gate had no resolved evidence to act on.`;
5711
6147
  }
5712
6148
  /**
5713
- * Build a schema-conformant InquiryRecord for the hachure inquiry-record.schema.json.
6149
+ * Build a schema-conformant InquiryRecord for @kontourai/surface's canonical InquiryRecord schema.
5714
6150
  * Strips Surface-internal fields (identityLinkIds, transitiveRuleIds) from
5715
6151
  * resolutionPath that are valid in the TS type but not in the JSON schema.
5716
6152
  * Sets answer.value to the gate-review value-add: { calibration, advisoryFix, gateFired, sessionSlug }.
@@ -5888,16 +6324,18 @@ async function gateReview(p) {
5888
6324
  : [];
5889
6325
  }
5890
6326
  const records = buildGateInquiryRecords(bundle, blockSignal, slug, expectedCriterionIds, surface);
5891
- // Validate each record against the hachure inquiry-record.schema.json (fail-open)
5892
- const validator = getHachureInquiryRecordValidator();
6327
+ // Validate each record against the canonical InquiryRecord schema via Surface (fail-open)
5893
6328
  let schemaValid = true;
5894
6329
  const validationErrors = [];
5895
- for (const record of records) {
5896
- if (validator) {
5897
- const result = validator(record);
5898
- if (!result.valid) {
6330
+ const inquiryValidator = surface?.validateInquiryRecord;
6331
+ if (typeof inquiryValidator === "function") {
6332
+ for (const record of records) {
6333
+ try {
6334
+ inquiryValidator(record);
6335
+ }
6336
+ catch (err) {
5899
6337
  schemaValid = false;
5900
- validationErrors.push(...result.errors.map((e) => `${record["id"] ?? "?"}: ${e}`));
6338
+ validationErrors.push(`${record["id"] ?? "?"}: ${err instanceof Error ? err.message : String(err)}`);
5901
6339
  }
5902
6340
  }
5903
6341
  }
@@ -5916,7 +6354,7 @@ async function gateReview(p) {
5916
6354
  .filter(([, n]) => n > 0)
5917
6355
  .map(([k, n]) => `${k}=${n}`)
5918
6356
  .join(", ");
5919
- const schemaTag = validator ? (schemaValid ? " schema:valid" : " schema:INVALID") : " schema:unavailable";
6357
+ const schemaTag = typeof inquiryValidator === "function" ? (schemaValid ? " schema:valid" : " schema:INVALID") : " schema:unavailable";
5920
6358
  console.log(`gate-review: ${records.length} InquiryRecord(s) [${summary}]${schemaTag} → ${outputPath}`);
5921
6359
  return 0;
5922
6360
  }
@@ -6432,14 +6870,6 @@ async function liveness(p) {
6432
6870
  die("liveness action must be one of: claim | heartbeat | release | status | whoami | verdict");
6433
6871
  return 1;
6434
6872
  }
6435
- // ─────────────────────────────────────────────────────────────────────────────
6436
- // ─── Claim Lookup — pure helper (promotable to Surface #171) ─────────────────
6437
- // buildClaimExplanation + its types are extracted to ./sidecar-claim-explain.ts
6438
- // (ops#22): a PURE projection (report + bundle + id in, structured explanation out)
6439
- // with no fs/CLI/shared state, unit-tested in isolation. Re-exported here so the
6440
- // library facade (src/index.ts) and the IO `claimLookup` handler below are unchanged.
6441
- export { buildClaimExplanation } from "./sidecar-claim-explain.js";
6442
- import { buildClaimExplanation } from "./sidecar-claim-explain.js";
6443
6873
  /**
6444
6874
  * claim <id> <dir>
6445
6875
  *
@@ -6476,27 +6906,16 @@ Available claim ids:
6476
6906
  }
6477
6907
  // Load Surface via tryLoadSurface() (ESM, cached, fail-open pattern)
6478
6908
  const surface = await tryLoadSurface();
6479
- if (!surface || typeof surface.buildTrustReport !== "function" || typeof surface.buildDerivationDrilldown !== "function") {
6480
- process.stderr.write(`[claim] @kontourai/surface unavailable or missing buildTrustReport/buildDerivationDrilldown
6909
+ if (!surface || typeof surface.buildTrustReport !== "function" || typeof surface.explainClaim !== "function") {
6910
+ process.stderr.write(`[claim] @kontourai/surface unavailable or missing buildTrustReport/explainClaim (needs >=2.10)
6481
6911
  `);
6482
6912
  return 0; // fail-open, consistent with gate-review pattern
6483
6913
  }
6484
- // Build TrustReport (required buildDerivationDrilldown needs TrustReport, not TrustBundle)
6914
+ // Build TrustReport, then the structured explanation Surface's explainClaim
6915
+ // (#171 lift) composes the derivation drilldown internally, fail-soft, exactly
6916
+ // as the retired local helper + caller-side enrichment did.
6485
6917
  const report = surface.buildTrustReport(bundle);
6486
- // Build the structured explanation (pure, promotable to #171)
6487
- const explanation = buildClaimExplanation(report, bundle, claimId);
6488
- // Enrich the why.directInputs/leafClaims/diagnostics from the drilldown
6489
- try {
6490
- const drilldown = surface.buildDerivationDrilldown(report, claimId);
6491
- if (drilldown) {
6492
- explanation.why.directInputs = Array.isArray(drilldown.directInputs) ? drilldown.directInputs : [];
6493
- explanation.why.leafClaims = Array.isArray(drilldown.leafClaims) ? drilldown.leafClaims : [];
6494
- explanation.why.diagnostics = Array.isArray(drilldown.diagnostics) ? drilldown.diagnostics : [];
6495
- }
6496
- }
6497
- catch {
6498
- // buildDerivationDrilldown threw (e.g. claim not in report) — proceed without drilldown
6499
- }
6918
+ const explanation = surface.explainClaim(report, claimId);
6500
6919
  if (p.flags.has("json")) {
6501
6920
  console.log(JSON.stringify(explanation, null, 2));
6502
6921
  return 0;