@kontourai/flow-agents 3.2.0 → 3.4.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 (281) hide show
  1. package/.github/workflows/add-to-project.yml +15 -0
  2. package/.github/workflows/ci.yml +173 -0
  3. package/CHANGELOG.md +54 -0
  4. package/CONTEXT.md +5 -1
  5. package/README.md +19 -8
  6. package/build/src/builder-flow-run-adapter.d.ts +80 -0
  7. package/build/src/builder-flow-run-adapter.js +241 -0
  8. package/build/src/builder-flow-runtime.d.ts +16 -0
  9. package/build/src/builder-flow-runtime.js +290 -0
  10. package/build/src/cli/assignment-provider.js +10 -1
  11. package/build/src/cli/builder-run.d.ts +1 -0
  12. package/build/src/cli/builder-run.js +27 -0
  13. package/build/src/cli/effective-backlog-settings.js +70 -2
  14. package/build/src/cli/init.d.ts +34 -0
  15. package/build/src/cli/init.js +341 -61
  16. package/build/src/cli/kit.js +55 -12
  17. package/build/src/cli/pull-work-provider.js +346 -5
  18. package/build/src/cli/skill-drift-check.d.ts +1 -0
  19. package/build/src/cli/skill-drift-check.js +165 -0
  20. package/build/src/cli/telemetry-doctor.d.ts +37 -0
  21. package/build/src/cli/telemetry-doctor.js +53 -6
  22. package/build/src/cli/validate-hook-influence.js +37 -7
  23. package/build/src/cli/workflow-artifact-cleanup-audit.js +418 -11
  24. package/build/src/cli/workflow-sidecar.d.ts +310 -1
  25. package/build/src/cli/workflow-sidecar.js +1914 -126
  26. package/build/src/cli.js +5 -0
  27. package/build/src/flow-kit/validate.d.ts +54 -34
  28. package/build/src/flow-kit/validate.js +237 -26
  29. package/build/src/index.d.ts +2 -0
  30. package/build/src/index.js +1 -0
  31. package/build/src/lib/console-connect-options.d.ts +97 -0
  32. package/build/src/lib/console-connect-options.js +199 -0
  33. package/build/src/lib/console-telemetry-validate.d.ts +49 -0
  34. package/build/src/lib/console-telemetry-validate.js +91 -0
  35. package/build/src/lib/flow-resolver.d.ts +54 -1
  36. package/build/src/lib/flow-resolver.js +112 -5
  37. package/build/src/lib/fs.d.ts +17 -0
  38. package/build/src/lib/fs.js +172 -0
  39. package/build/src/lib/local-artifact-root.d.ts +44 -1
  40. package/build/src/lib/local-artifact-root.js +131 -3
  41. package/build/src/runtime-adapters.d.ts +39 -3
  42. package/build/src/runtime-adapters.js +77 -31
  43. package/build/src/tools/build-universal-bundles.js +40 -2
  44. package/build/src/tools/codex-agent-routing.d.ts +2 -0
  45. package/build/src/tools/codex-agent-routing.js +49 -0
  46. package/build/src/tools/generate-context-map.js +1 -0
  47. package/build/src/tools/validate-source-tree.js +30 -3
  48. package/context/contracts/artifact-contract.md +16 -2
  49. package/context/scripts/hooks/lib/kit-catalog.js +235 -0
  50. package/context/scripts/hooks/lib/runnable-command.js +177 -0
  51. package/context/scripts/hooks/stop-goal-fit.js +278 -48
  52. package/context/scripts/hooks/workflow-steering.js +194 -22
  53. package/context/scripts/package.json +3 -0
  54. package/context/scripts/telemetry/install-console-config.sh +25 -4
  55. package/context/scripts/telemetry/lib/config.sh +102 -12
  56. package/context/scripts/telemetry/lib/pricing.sh +50 -0
  57. package/context/scripts/telemetry/lib/session.sh +3 -0
  58. package/context/scripts/telemetry/lib/transport.sh +87 -0
  59. package/context/scripts/telemetry/lib/usage.sh +205 -4
  60. package/context/scripts/telemetry/telemetry.conf +6 -0
  61. package/context/scripts/telemetry/telemetry.sh +48 -0
  62. package/context/settings/workspace-backlog-provider-settings.example.json +48 -0
  63. package/docs/agent-usage-feedback-loop.md +35 -0
  64. package/docs/architecture-engine-and-kits.md +110 -0
  65. package/docs/context-map.md +2 -0
  66. package/docs/coordination-guide.md +370 -0
  67. package/docs/decisions/agent-coordination.md +26 -9
  68. package/docs/decisions/embeddable-engine.md +152 -0
  69. package/docs/decisions/index.md +5 -3
  70. package/docs/decisions/trust-ledger-retention.md +88 -0
  71. package/docs/decisions/trust-reconcile.md +42 -9
  72. package/docs/decisions/workflow-enforcement.md +31 -9
  73. package/docs/fixture-ownership.md +6 -2
  74. package/docs/implementing-trust-reconciliation.md +129 -0
  75. package/docs/index.md +23 -9
  76. package/docs/integrations/flow-agents-console.md +275 -0
  77. package/docs/integrations/index.md +4 -0
  78. package/docs/kit-authoring-guide.md +52 -21
  79. package/docs/spec/builder-flow-runtime.md +80 -0
  80. package/docs/spec/runtime-hook-surface.md +45 -1
  81. package/docs/specs/economics-record-contract.md +270 -0
  82. package/docs/specs/harness-capability-matrix.md +74 -0
  83. package/docs/specs/learning-review-proposals-contract.md +340 -0
  84. package/docs/specs/routing-efficiency-review.md +59 -0
  85. package/docs/verifiable-trust.md +74 -25
  86. package/docs/workflow-artifact-lifecycle.md +38 -1
  87. package/docs/workflow-usage-guide.md +10 -0
  88. package/evals/acceptance/prove-capture-teeth.sh +132 -0
  89. package/evals/ci/antigaming-suite.sh +2 -0
  90. package/evals/ci/run-baseline.sh +78 -4
  91. package/evals/fixtures/economics/acceptance.json +12 -0
  92. package/evals/fixtures/economics/agents/tool-worker-1/events.jsonl +2 -0
  93. package/evals/fixtures/economics/agents/tool-worker-2/events.jsonl +2 -0
  94. package/evals/fixtures/economics/agents/tool-worker-3/events.jsonl +2 -0
  95. package/evals/fixtures/economics/agents/tool-worker-4/events.jsonl +1 -0
  96. package/evals/fixtures/economics/agents/tool-worker-5/events.jsonl +2 -0
  97. package/evals/fixtures/economics/critique.json +22 -0
  98. package/evals/fixtures/economics/expected-record.json +71 -0
  99. package/evals/fixtures/economics/session-usage-event.json +1 -0
  100. package/evals/fixtures/economics/state.json +11 -0
  101. package/evals/fixtures/economics/transcript.jsonl +3 -0
  102. package/evals/fixtures/hook-influence/cases.json +7 -7
  103. package/evals/fixtures/learning-review-proposals/balanced/economics.jsonl +6 -0
  104. package/evals/fixtures/learning-review-proposals/effect-follow-up/economics.jsonl +5 -0
  105. package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-1/trust.bundle +21 -0
  106. package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-2/trust.bundle +21 -0
  107. package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-3/trust.bundle +21 -0
  108. package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-4/trust.bundle +21 -0
  109. package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-5/trust.bundle +21 -0
  110. package/evals/fixtures/learning-review-proposals/pattern-present/economics.jsonl +6 -0
  111. package/evals/fixtures/learning-review-proposals/pattern-present/expected-aggregates.json +30 -0
  112. package/evals/fixtures/learning-review-proposals/pattern-present/expected-aggregates.md +66 -0
  113. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-1/gate-review.inquiries.json +26 -0
  114. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-1/trust.bundle +21 -0
  115. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-2/gate-review.inquiries.json +26 -0
  116. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-2/trust.bundle +21 -0
  117. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-3/gate-review.inquiries.json +26 -0
  118. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-3/trust.bundle +21 -0
  119. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-4/gate-review.inquiries.json +26 -0
  120. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-4/trust.bundle +21 -0
  121. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-5/trust.bundle +21 -0
  122. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-6/trust.bundle +21 -0
  123. package/evals/fixtures/learning-review-proposals/repeat-window/economics.jsonl +6 -0
  124. package/evals/fixtures/learning-review-proposals/under-threshold/economics.jsonl +3 -0
  125. package/evals/fixtures/reconcile-preflight/disputed-critique-unsuperseded.json +48 -0
  126. package/evals/fixtures/reconcile-preflight/standalone-disputed-session-local.json +59 -0
  127. package/evals/fixtures/telemetry/usage-transcript-sample.jsonl +4 -0
  128. package/evals/fixtures/trust-reconcile-exploits/mcp-degrade.json +42 -0
  129. package/evals/integration/test_builder_entry_enforcement.sh +241 -0
  130. package/evals/integration/test_builder_step_producers.sh +18 -10
  131. package/evals/integration/test_bundle_install.sh +172 -0
  132. package/evals/integration/test_checkpoint_signing.sh +10 -2
  133. package/evals/integration/test_ci_actor_identity.sh +221 -0
  134. package/evals/integration/test_console_tenant_isolation.sh +167 -0
  135. package/evals/integration/test_critique_supersession_roundtrip.sh +4 -1
  136. package/evals/integration/test_dual_emit_flow_step.sh +10 -4
  137. package/evals/integration/test_economics_record.sh +674 -0
  138. package/evals/integration/test_effective_backlog_settings.sh +1 -1
  139. package/evals/integration/test_evidence_capture_hook.sh +17 -2
  140. package/evals/integration/test_exemption_usage_review.sh +198 -0
  141. package/evals/integration/test_fixture_retirement_audit.sh +2 -2
  142. package/evals/integration/test_flow_kit_install_git.sh +83 -0
  143. package/evals/integration/test_flowdef_session_activation.sh +0 -1
  144. package/evals/integration/test_flowdef_session_history_preservation.sh +13 -3
  145. package/evals/integration/test_gate_lockdown.sh +7 -0
  146. package/evals/integration/test_gate_review_inquiry_records.sh +9 -1
  147. package/evals/integration/test_goal_fit_hook.sh +2031 -0
  148. package/evals/integration/test_hook_category_behaviors.sh +8 -1
  149. package/evals/integration/test_hook_influence_cases.sh +25 -1
  150. package/evals/integration/test_install_merge.sh +227 -2
  151. package/evals/integration/test_kit_conformance_levels.sh +6 -6
  152. package/evals/integration/test_learning_review_proposals.sh +329 -0
  153. package/evals/integration/test_liveness_conflict_injection.sh +26 -22
  154. package/evals/integration/test_liveness_console_relay.sh +166 -0
  155. package/evals/integration/test_liveness_heartbeat.sh +17 -17
  156. package/evals/integration/test_liveness_worktree_root.sh +575 -0
  157. package/evals/integration/test_phase_map_and_gate_claim.sh +6 -1
  158. package/evals/integration/test_publish_delivery.sh +389 -2
  159. package/evals/integration/test_pull_work_board.sh +200 -0
  160. package/evals/integration/test_pull_work_provider.sh +1 -1
  161. package/evals/integration/test_reconcile_preflight.sh +304 -0
  162. package/evals/integration/test_record_check.sh +378 -0
  163. package/evals/integration/test_routing_efficiency.sh +71 -0
  164. package/evals/integration/test_runtime_adapter_activation.sh +28 -0
  165. package/evals/integration/test_session_resume_roundtrip.sh +16 -19
  166. package/evals/integration/test_skill_drift_check.sh +870 -0
  167. package/evals/integration/test_takeover_protocol.sh +340 -0
  168. package/evals/integration/test_telemetry.sh +445 -0
  169. package/evals/integration/test_telemetry_doctor.sh +66 -0
  170. package/evals/integration/test_telemetry_usage_pipeline.sh +228 -0
  171. package/evals/integration/test_trust_reconcile_negatives.sh +121 -13
  172. package/evals/integration/test_trust_reconcile_trailer_diagnostic.sh +247 -0
  173. package/evals/integration/test_usage_cost.sh +61 -0
  174. package/evals/integration/test_verify_hold.sh +910 -0
  175. package/evals/integration/test_veritas_governance_kit.sh +257 -0
  176. package/evals/integration/test_workflow_artifact_cleanup_audit.sh +575 -3
  177. package/evals/integration/test_workflow_sidecar_writer.sh +1395 -0
  178. package/evals/integration/test_workflow_steering_hook.sh +157 -16
  179. package/evals/integration/test_workspace_settings.sh +176 -0
  180. package/evals/lib/env.sh +26 -0
  181. package/evals/lib/node.sh +8 -0
  182. package/evals/run.sh +37 -0
  183. package/evals/static/test_ci_integration_coverage.sh +115 -0
  184. package/evals/static/test_declared_scope_forms_documented.sh +114 -0
  185. package/evals/static/test_universal_bundles.sh +34 -0
  186. package/evals/static/test_validate_source_kit_asset_scope.sh +259 -0
  187. package/evals/static/test_workflow_skills.sh +1 -1
  188. package/kits/builder/flows/build.flow.json +9 -18
  189. package/kits/builder/flows/publish-learn.flow.json +5 -1
  190. package/kits/builder/kit.json +120 -0
  191. package/kits/builder/skills/continue-work/SKILL.md +2 -0
  192. package/kits/builder/skills/deliver/SKILL.md +115 -0
  193. package/kits/builder/skills/evidence-gate/SKILL.md +12 -0
  194. package/kits/builder/skills/execute-plan/SKILL.md +9 -0
  195. package/kits/builder/skills/learning-review/SKILL.md +51 -0
  196. package/kits/builder/skills/plan-work/SKILL.md +17 -20
  197. package/kits/builder/skills/pull-work/SKILL.md +33 -2
  198. package/kits/builder/skills/release-readiness/SKILL.md +12 -0
  199. package/kits/knowledge/kit.json +9 -0
  200. package/kits/veritas-governance/docs/README.md +113 -7
  201. package/kits/veritas-governance/fixtures/exemption/approved.trust-bundle.json +74 -0
  202. package/kits/veritas-governance/fixtures/exemption/not-approved.trust-bundle.json +74 -0
  203. package/kits/veritas-governance/fixtures/exemption-review/mixed-fresh-stale.DECLARED.json +14 -0
  204. package/kits/veritas-governance/flows/exemption-issuance.flow.json +35 -0
  205. package/kits/veritas-governance/kit.json +19 -0
  206. package/kits/veritas-governance/skills/exemption-usage-review/SKILL.md +128 -0
  207. package/kits/veritas-governance/skills/exemption-usage-review/review-exemptions.mjs +231 -0
  208. package/package.json +2 -2
  209. package/packaging/manifest.json +29 -0
  210. package/schemas/backlog-provider-settings.schema.json +13 -0
  211. package/schemas/workflow-state.schema.json +44 -0
  212. package/scripts/README.md +4 -0
  213. package/scripts/check-content-boundary.cjs +8 -1
  214. package/scripts/ci/trust-reconcile.js +214 -253
  215. package/scripts/hooks/codex-hook-adapter.js +77 -2
  216. package/scripts/hooks/evidence-capture.js +38 -5
  217. package/scripts/hooks/lib/actor-identity.js +82 -0
  218. package/scripts/hooks/lib/codex-exit-code.js +316 -0
  219. package/scripts/hooks/lib/kit-catalog.js +235 -0
  220. package/scripts/hooks/lib/liveness-write.js +28 -1
  221. package/scripts/hooks/lib/local-artifact-paths.js +97 -1
  222. package/scripts/hooks/lib/runnable-command.js +177 -0
  223. package/scripts/hooks/lib/skill-drift.js +350 -0
  224. package/scripts/hooks/stop-goal-fit.js +278 -48
  225. package/scripts/hooks/workflow-steering.js +194 -22
  226. package/scripts/install-codex-home.sh +97 -47
  227. package/scripts/install-merge.js +72 -14
  228. package/scripts/install-owned-files.js +178 -0
  229. package/scripts/lib/reconcile-shape.js +381 -0
  230. package/scripts/liveness/relay.sh +84 -0
  231. package/scripts/telemetry/economics-record.schema.json +145 -0
  232. package/scripts/telemetry/economics-record.sh +331 -0
  233. package/scripts/telemetry/install-console-config.sh +25 -4
  234. package/scripts/telemetry/learning-review-decide.sh +124 -0
  235. package/scripts/telemetry/learning-review-proposals.schema.json +161 -0
  236. package/scripts/telemetry/learning-review-proposals.sh +484 -0
  237. package/scripts/telemetry/lib/config.sh +102 -12
  238. package/scripts/telemetry/lib/pricing.sh +14 -6
  239. package/scripts/telemetry/lib/session.sh +3 -0
  240. package/scripts/telemetry/lib/transport.sh +133 -15
  241. package/scripts/telemetry/lib/usage.sh +121 -28
  242. package/scripts/telemetry/routing-efficiency.sh +0 -0
  243. package/scripts/telemetry/telemetry.conf +6 -0
  244. package/scripts/telemetry/telemetry.sh +48 -0
  245. package/src/builder-flow-run-adapter.ts +357 -0
  246. package/src/builder-flow-runtime.ts +348 -0
  247. package/src/cli/assignment-provider.ts +12 -1
  248. package/src/cli/builder-flow-run-adapter.test.mjs +495 -0
  249. package/src/cli/builder-flow-runtime.test.mjs +213 -0
  250. package/src/cli/builder-run.ts +28 -0
  251. package/src/cli/codex-agent-routing.test.mjs +44 -0
  252. package/src/cli/codex-exit-code.test.mjs +207 -0
  253. package/src/cli/console-connect-options.test.mjs +329 -0
  254. package/src/cli/console-telemetry-validate.test.mjs +157 -0
  255. package/src/cli/effective-backlog-settings.ts +68 -2
  256. package/src/cli/flow-resolver-composition.test.mjs +72 -0
  257. package/src/cli/init.test.mjs +161 -0
  258. package/src/cli/init.ts +407 -62
  259. package/src/cli/kit-metadata-security.test.mjs +443 -0
  260. package/src/cli/kit.ts +50 -12
  261. package/src/cli/pull-work-provider.ts +377 -3
  262. package/src/cli/sidecar-pure-helpers.test.mjs +64 -0
  263. package/src/cli/skill-drift-check.ts +196 -0
  264. package/src/cli/telemetry-doctor.test.mjs +53 -0
  265. package/src/cli/telemetry-doctor.ts +50 -7
  266. package/src/cli/validate-hook-influence.ts +37 -6
  267. package/src/cli/workflow-artifact-cleanup-audit.ts +483 -10
  268. package/src/cli/workflow-sidecar.ts +1980 -119
  269. package/src/cli.ts +5 -0
  270. package/src/flow-kit/validate.ts +277 -38
  271. package/src/index.ts +19 -0
  272. package/src/lib/console-connect-options.ts +261 -0
  273. package/src/lib/console-telemetry-validate.ts +88 -0
  274. package/src/lib/flow-resolver.ts +117 -4
  275. package/src/lib/fs.ts +160 -0
  276. package/src/lib/local-artifact-root.ts +129 -3
  277. package/src/runtime-adapters.ts +113 -33
  278. package/src/tools/build-universal-bundles.ts +36 -2
  279. package/src/tools/codex-agent-routing.ts +48 -0
  280. package/src/tools/generate-context-map.ts +1 -0
  281. package/src/tools/validate-source-tree.ts +29 -3
@@ -77,6 +77,263 @@ echo "--- captured real-veritas-output fixtures ---"
77
77
  gate_case "$KIT/fixtures/readiness/ready.readiness-report.json" "positive" 0
78
78
  gate_case "$KIT/fixtures/readiness/not-ready.readiness-report.json" "negative" 1
79
79
 
80
+ # --- exemption-issuance flow: registration/K0 validity ---------------------------
81
+ EXEMPTION_FLOWDEF="$KIT/flows/exemption-issuance.flow.json"
82
+ if node -e "const f=require('$EXEMPTION_FLOWDEF'); if(f.id!=='veritas-governance.exemption-issuance') process.exit(1);" 2>/dev/null; then
83
+ pass "exemption-issuance flow file has id veritas-governance.exemption-issuance"
84
+ else
85
+ fail "exemption-issuance flow file missing or has unexpected id"
86
+ fi
87
+ if rg -q '"id": *"veritas-governance\.exemption-issuance"' "$KIT/kit.json" && rg -q '"path": *"flows/exemption-issuance\.flow\.json"' "$KIT/kit.json"; then
88
+ pass "exemption-issuance flow is registered in kit.json flows[]"
89
+ else
90
+ fail "exemption-issuance flow is NOT registered in kit.json flows[]"
91
+ fi
92
+ # Re-run the whole-kit container validation (already covers the new flow file too, since
93
+ # `flow kit validate` walks every flows[] entry) — a second explicit assertion, scoped to
94
+ # this flow's own gate shape, per AC2.
95
+ if node -e "
96
+ const f=require('$EXEMPTION_FLOWDEF');
97
+ const g=Object.values(f.gates)[0];
98
+ const bc=g.expects[0].bundle_claim;
99
+ if (g.expects[0].kind!=='trust.bundle') process.exit(1);
100
+ if (bc.claimType!=='no-agent-delivery-exemption-approval') process.exit(1);
101
+ if (bc.subjectType!=='delivery-scope') process.exit(1);
102
+ if (JSON.stringify(bc.accepted_statuses)!=='[\"verified\"]') process.exit(1);
103
+ " 2>/dev/null; then
104
+ pass "exemption-issuance gate expects[] kind is trust.bundle and pins claimType/subjectType/accepted_statuses exactly"
105
+ else
106
+ fail "exemption-issuance gate expects[] shape does not match the pinned claim selector"
107
+ fi
108
+
109
+ # --- exemption-issuance flow: positive/negative gate cases (AC3) -----------------
110
+ # gate_case() above is readiness-check-specific (drives the readiness adapter + fixed gate
111
+ # id gate-check-gate). The exemption-issuance flow has no adapter (issue's Files list does
112
+ # not add one — the approval bundle is hand-authored/fixture-authored) and a different gate
113
+ # id, so it gets its own small helper.
114
+ # $1 bundle fixture path, $2 label, $3 expected exit (0=pass, 1=block)
115
+ exemption_gate_case() {
116
+ local bundle="$1" label="$2" expect="$3"
117
+ local work="$TMP_DIR/$label"; mkdir -p "$work"; ( cd "$work" && node "$FLOW_CLI" init >/dev/null 2>&1 )
118
+ ( cd "$work" && node "$FLOW_CLI" start "$EXEMPTION_FLOWDEF" --run-id "$label" >/dev/null 2>&1 )
119
+ ( cd "$work" && node "$FLOW_CLI" attach-evidence "$label" --gate human-approval-gate --file "$bundle" --bundle >"$work/attach.out" 2>&1 )
120
+ if ! rg -q 'kind: trust.bundle' "$work/attach.out"; then
121
+ fail "[$label] evidence did not attach as kind: trust.bundle"; sed -n '1,20p' "$work/attach.out"; return
122
+ fi
123
+ ( cd "$work" && node "$FLOW_CLI" evaluate "$label" --gate human-approval-gate --exit-code >"$work/eval.out" 2>&1 )
124
+ local got=$?
125
+ if [[ "$got" == "$expect" ]]; then
126
+ pass "[$label] exemption-issuance gate evaluate exit $got as expected ($(rg -o '^(pass|block|route-back) human-approval-gate.*' "$work/eval.out" | head -1))"
127
+ else
128
+ fail "[$label] exemption-issuance gate evaluate exit $got, expected $expect"; sed -n '1,20p' "$work/eval.out"
129
+ fi
130
+ }
131
+
132
+ echo "--- exemption-issuance: gate blocks without a verified approval, passes with one ---"
133
+ exemption_gate_case "$KIT/fixtures/exemption/approved.trust-bundle.json" "exemption-positive" 0
134
+ exemption_gate_case "$KIT/fixtures/exemption/not-approved.trust-bundle.json" "exemption-negative" 1
135
+
136
+ # --- exemption-issuance: issue-step DECLARED append semantics (AC4) --------------
137
+ # Seed a scratch delivery/DECLARED with main's real 2-entry array (inlined here, not read
138
+ # live from the repo, so this eval is not coupled to that file's future contents), then
139
+ # simulate the issue step's write: append a third, well-formed entry. Assert append, not
140
+ # clobber: length 3, and the first two entries byte-identical pre/post.
141
+ echo "--- exemption-issuance: issue step appends to delivery/DECLARED, does not clobber ---"
142
+ DECLARED_WORK="$TMP_DIR/declared-append"
143
+ mkdir -p "$DECLARED_WORK/delivery"
144
+ SEEDED_DECLARED='[
145
+ {
146
+ "scope": "author:dependabot[bot]",
147
+ "reason": "dependabot dependency-update PRs; no agent delivery involved",
148
+ "approved_by": "brian.anderson1222 (AC8 option-a decision, ADR 0022 approval 2026-07-02)",
149
+ "declared_at": "2026-07-03T16:27:21Z"
150
+ },
151
+ {
152
+ "scope": "author:github-actions[bot] branch-prefix:release-please--",
153
+ "reason": "release-please automation PR; no agent delivery involved",
154
+ "approved_by": "brian.anderson1222 (AC8 option-a decision, ADR 0022 approval 2026-07-02)",
155
+ "declared_at": "2026-07-03T16:27:21Z"
156
+ }
157
+ ]'
158
+ printf '%s' "$SEEDED_DECLARED" > "$DECLARED_WORK/delivery/DECLARED.seed"
159
+
160
+ NEW_ENTRY_SCOPE="author:exemption-eval-bot[bot]"
161
+ NEW_ENTRY_ACTOR="exemption-eval-bot[bot]"
162
+ NEW_ENTRY_REASON="exemption-issuance eval: no-agent-delivery exemption for a test bot scope"
163
+ NEW_ENTRY_APPROVED_BY="eval-fixture-approver (exemption-issuance flow)"
164
+ NEW_ENTRY_DECLARED_AT="2026-07-01T00:00:00Z"
165
+
166
+ # Simulate the issue step: read-modify-write (parse the existing array, append one entry).
167
+ node -e "
168
+ const fs = require('fs');
169
+ const seedPath = '$DECLARED_WORK/delivery/DECLARED.seed';
170
+ const outPath = '$DECLARED_WORK/delivery/DECLARED';
171
+ const existing = JSON.parse(fs.readFileSync(seedPath, 'utf8'));
172
+ const appended = existing.concat([{
173
+ scope: '$NEW_ENTRY_SCOPE',
174
+ reason: '$NEW_ENTRY_REASON',
175
+ approved_by: '$NEW_ENTRY_APPROVED_BY',
176
+ declared_at: '$NEW_ENTRY_DECLARED_AT'
177
+ }]);
178
+ fs.writeFileSync(outPath, JSON.stringify(appended, null, 2) + '\n');
179
+ "
180
+
181
+ if node -e "
182
+ const fs = require('fs');
183
+ const seeded = JSON.parse(fs.readFileSync('$DECLARED_WORK/delivery/DECLARED.seed', 'utf8'));
184
+ const appended = JSON.parse(fs.readFileSync('$DECLARED_WORK/delivery/DECLARED', 'utf8'));
185
+ if (!Array.isArray(appended) || appended.length !== 3) process.exit(1);
186
+ if (JSON.stringify(appended[0]) !== JSON.stringify(seeded[0])) process.exit(1);
187
+ if (JSON.stringify(appended[1]) !== JSON.stringify(seeded[1])) process.exit(1);
188
+ "; then
189
+ pass "issue-step append: 3 entries present, first two byte-identical to the seeded 2-entry array (append, not clobber)"
190
+ else
191
+ fail "issue-step append: entry count or pre-existing entries diverged from the seeded array"
192
+ fi
193
+
194
+ if node -e "
195
+ const fs = require('fs');
196
+ const appended = JSON.parse(fs.readFileSync('$DECLARED_WORK/delivery/DECLARED', 'utf8'));
197
+ const entry = appended[2];
198
+ const required = ['scope', 'reason', 'approved_by', 'declared_at'];
199
+ for (const f of required) {
200
+ if (typeof entry[f] !== 'string' || entry[f].trim() === '') process.exit(1);
201
+ }
202
+ "; then
203
+ pass "issue-step append: appended entry is well-formed (scope/reason/approved_by/declared_at all present, non-empty)"
204
+ else
205
+ fail "issue-step append: appended entry is missing/empty a required field"
206
+ fi
207
+
208
+ # --- exemption-issuance: REAL trust-reconcile.js reads the appended marker (AC5) -
209
+ # Reuse test_trust_reconcile_negatives.sh section 7's write_declared/env-override pattern:
210
+ # TRUST_RECONCILE_COMMANDS is a trivial pass so Step 1 always succeeds; TRUST_RECONCILE_ACTOR
211
+ # is set to match the appended entry's author: scope exactly (the bare actor name, without
212
+ # the "author:" prefix, matching matchesScope()'s author: condition contract).
213
+ echo "--- exemption-issuance: REAL trust-reconcile.js reads the appended marker as well-formed and in-scope ---"
214
+ RECONCILE="$ROOT/scripts/ci/trust-reconcile.js"
215
+ RECONCILE_CMD="node -e 'process.exit(0)'"
216
+
217
+ reconcile_out="$(TRUST_RECONCILE_ACTOR="$NEW_ENTRY_ACTOR" TRUST_RECONCILE_COMMANDS="$RECONCILE_CMD" \
218
+ node "$RECONCILE" --repo-root "$DECLARED_WORK" 2>&1)"
219
+ reconcile_code=$?
220
+ if [[ "$reconcile_code" -eq 0 ]]; then
221
+ pass "trust-reconcile.js exits 0 against the appended 3-entry delivery/DECLARED (in-scope match)"
222
+ else
223
+ fail "trust-reconcile.js expected exit 0, got $reconcile_code — output: $reconcile_out"
224
+ fi
225
+ if echo "$reconcile_out" | grep -qF "DECLARED (no-agent-delivery): $NEW_ENTRY_SCOPE — $NEW_ENTRY_REASON (approved by $NEW_ENTRY_APPROVED_BY, declared $NEW_ENTRY_DECLARED_AT)"; then
226
+ pass "trust-reconcile.js emits the exact DECLARED (no-agent-delivery) line for the appended entry"
227
+ else
228
+ fail "trust-reconcile.js did not emit the expected DECLARED (no-agent-delivery) line — output: $reconcile_out"
229
+ fi
230
+
231
+ # --- exemption-issuance: negative — malformed/out-of-scope append still fails closed (AC6) --
232
+ # This case's DECLARED file has the two pre-existing WELL-FORMED entries (dependabot,
233
+ # release-please) plus a malformed appended (third) entry (missing approved_by). The
234
+ # malformed entry is excluded from parseDeclaredMarker()'s wellFormed[] bucket entirely (its
235
+ # "missing required field(s)" line is only a non-fatal stderr warning, logged so one bad
236
+ # entry cannot silently mask a good one -- see resolveDeclaredExemption()); scope-matching
237
+ # then proceeds against the two remaining well-formed entries, neither of which matches this
238
+ # case's TRUST_RECONCILE_ACTOR context, so the FATAL diagnostic that actually fires is the
239
+ # scope-mismatch path, not the missing-field path. This proves: a malformed appended entry
240
+ # is ignored as an entry (does not itself exempt Step 2), and with no other in-scope
241
+ # well-formed entry present, the reconciler still fails closed overall.
242
+ echo "--- exemption-issuance: malformed appended entry is ignored; no in-scope entry remains -> fails closed ---"
243
+ DECLARED_NEG_WORK="$TMP_DIR/declared-append-negative"
244
+ mkdir -p "$DECLARED_NEG_WORK/delivery"
245
+ # Same seed, but the appended (third) entry is missing approved_by — malformed.
246
+ SEEDED_DECLARED_JSON="$SEEDED_DECLARED" NEW_ENTRY_SCOPE="$NEW_ENTRY_SCOPE" NEW_ENTRY_REASON="$NEW_ENTRY_REASON" NEW_ENTRY_DECLARED_AT="$NEW_ENTRY_DECLARED_AT" OUT_PATH="$DECLARED_NEG_WORK/delivery/DECLARED" node -e "
247
+ const fs = require('fs');
248
+ const existing = JSON.parse(process.env.SEEDED_DECLARED_JSON);
249
+ const appended = existing.concat([{
250
+ scope: process.env.NEW_ENTRY_SCOPE,
251
+ reason: process.env.NEW_ENTRY_REASON,
252
+ declared_at: process.env.NEW_ENTRY_DECLARED_AT
253
+ }]);
254
+ fs.writeFileSync(process.env.OUT_PATH, JSON.stringify(appended, null, 2) + '\n');
255
+ "
256
+ neg_out="$(TRUST_RECONCILE_ACTOR="$NEW_ENTRY_ACTOR" TRUST_RECONCILE_COMMANDS="$RECONCILE_CMD" \
257
+ node "$RECONCILE" --repo-root "$DECLARED_NEG_WORK" 2>&1)"
258
+ neg_code=$?
259
+ if [[ "$neg_code" -ne 0 ]]; then
260
+ pass "trust-reconcile.js fails closed ($neg_code): malformed appended entry ignored, no in-scope well-formed entry remains"
261
+ else
262
+ fail "trust-reconcile.js expected non-zero exit on a malformed appended entry, got 0 — output: $neg_out"
263
+ fi
264
+ if echo "$neg_out" | grep -qF "[bundle-required-no-declared-marker] delivery/DECLARED marker present but out of scope for this change"; then
265
+ pass "trust-reconcile.js's FATAL diagnostic is the out-of-scope path (the malformed entry itself never satisfies the gate)"
266
+ else
267
+ fail "trust-reconcile.js did not emit the expected out-of-scope FATAL diagnostic — output: $neg_out"
268
+ fi
269
+
270
+ # Companion variant (true missing-field proof): a DECLARED file whose ONLY entry is the
271
+ # malformed one (no other well-formed entry to fall through to) — here the FATAL diagnostic
272
+ # IS the malformed/missing-field path itself, naming approved_by, per the merged reconciler's
273
+ # exact string (resolveDeclaredExemption(), wellFormed.length === 0 branch).
274
+ echo "--- exemption-issuance: DECLARED file with ONLY a malformed entry -> the missing-field diagnostic itself is fatal ---"
275
+ DECLARED_ONLY_MALFORMED_WORK="$TMP_DIR/declared-only-malformed"
276
+ mkdir -p "$DECLARED_ONLY_MALFORMED_WORK/delivery"
277
+ NEW_ENTRY_SCOPE="$NEW_ENTRY_SCOPE" NEW_ENTRY_REASON="$NEW_ENTRY_REASON" NEW_ENTRY_DECLARED_AT="$NEW_ENTRY_DECLARED_AT" OUT_PATH="$DECLARED_ONLY_MALFORMED_WORK/delivery/DECLARED" node -e "
278
+ const fs = require('fs');
279
+ const onlyEntry = [{
280
+ scope: process.env.NEW_ENTRY_SCOPE,
281
+ reason: process.env.NEW_ENTRY_REASON,
282
+ declared_at: process.env.NEW_ENTRY_DECLARED_AT
283
+ }];
284
+ fs.writeFileSync(process.env.OUT_PATH, JSON.stringify(onlyEntry, null, 2) + '\n');
285
+ "
286
+ only_malformed_out="$(TRUST_RECONCILE_ACTOR="$NEW_ENTRY_ACTOR" TRUST_RECONCILE_COMMANDS="$RECONCILE_CMD" \
287
+ node "$RECONCILE" --repo-root "$DECLARED_ONLY_MALFORMED_WORK" 2>&1)"
288
+ only_malformed_code=$?
289
+ if [[ "$only_malformed_code" -ne 0 ]]; then
290
+ pass "trust-reconcile.js fails closed ($only_malformed_code) when DECLARED's only entry is malformed"
291
+ else
292
+ fail "trust-reconcile.js expected non-zero exit when DECLARED's only entry is malformed, got 0 — output: $only_malformed_out"
293
+ fi
294
+ if echo "$only_malformed_out" | grep -qF "[bundle-required-no-declared-marker] delivery/DECLARED marker is malformed: missing required field(s) [approved_by]"; then
295
+ pass "trust-reconcile.js's FATAL diagnostic IS the missing-field path, naming approved_by (true missing-field proof)"
296
+ else
297
+ fail "trust-reconcile.js did not emit the expected FATAL missing-field diagnostic naming approved_by — output: $only_malformed_out"
298
+ fi
299
+
300
+ # Second negative variant: well-formed but out-of-scope append (resolved context does not
301
+ # match the new entry's scope) — proves scope-mismatch also fails closed, not just malformed
302
+ # fields.
303
+ DECLARED_NEG_SCOPE_WORK="$TMP_DIR/declared-append-out-of-scope"
304
+ mkdir -p "$DECLARED_NEG_SCOPE_WORK/delivery"
305
+ SEEDED_DECLARED_JSON="$SEEDED_DECLARED" NEW_ENTRY_SCOPE="$NEW_ENTRY_SCOPE" NEW_ENTRY_REASON="$NEW_ENTRY_REASON" NEW_ENTRY_APPROVED_BY="$NEW_ENTRY_APPROVED_BY" NEW_ENTRY_DECLARED_AT="$NEW_ENTRY_DECLARED_AT" OUT_PATH="$DECLARED_NEG_SCOPE_WORK/delivery/DECLARED" node -e "
306
+ const fs = require('fs');
307
+ const existing = JSON.parse(process.env.SEEDED_DECLARED_JSON);
308
+ const appended = existing.concat([{
309
+ scope: process.env.NEW_ENTRY_SCOPE,
310
+ reason: process.env.NEW_ENTRY_REASON,
311
+ approved_by: process.env.NEW_ENTRY_APPROVED_BY,
312
+ declared_at: process.env.NEW_ENTRY_DECLARED_AT
313
+ }]);
314
+ fs.writeFileSync(process.env.OUT_PATH, JSON.stringify(appended, null, 2) + '\n');
315
+ "
316
+ neg_scope_out="$(TRUST_RECONCILE_ACTOR="not-the-right-actor[bot]" TRUST_RECONCILE_COMMANDS="$RECONCILE_CMD" \
317
+ node "$RECONCILE" --repo-root "$DECLARED_NEG_SCOPE_WORK" 2>&1)"
318
+ neg_scope_code=$?
319
+ if [[ "$neg_scope_code" -ne 0 ]]; then
320
+ pass "trust-reconcile.js fails closed ($neg_scope_code) when the resolved context does not match the appended entry's scope"
321
+ else
322
+ fail "trust-reconcile.js expected non-zero exit for an out-of-scope append, got 0 — output: $neg_scope_out"
323
+ fi
324
+ if echo "$neg_scope_out" | grep -qF "out of scope" || echo "$neg_scope_out" | grep -qF "bundle-required-no-declared-marker"; then
325
+ pass "trust-reconcile.js emits 'out of scope' or 'bundle-required-no-declared-marker' for the out-of-scope append"
326
+ else
327
+ fail "trust-reconcile.js did not emit the expected out-of-scope diagnostic — output: $neg_scope_out"
328
+ fi
329
+
330
+ # --- exemption-issuance: no-fork check, specific to this task's new assets (AC8) -
331
+ if grep -rn "veritas-governance" "$ROOT/scripts/ci/trust-reconcile.js" >/dev/null 2>&1; then
332
+ fail "scripts/ci/trust-reconcile.js references kits/veritas-governance (no-fork/layering violated)"
333
+ else
334
+ pass "no-fork: scripts/ci/trust-reconcile.js has zero references to veritas-governance (anchor stays kit-agnostic)"
335
+ fi
336
+
80
337
  # --- Optional live Veritas leg --------------------------------------------------
81
338
  VBIN="${VERITAS_BIN:-veritas}"
82
339
  if command -v "$VBIN" >/dev/null 2>&1 && [[ -n "${VERITAS_GOVERNED_REPO:-}" && -d "${VERITAS_GOVERNED_REPO:-}/.veritas" ]]; then