@kontourai/flow-agents 3.3.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 (256) hide show
  1. package/.github/workflows/add-to-project.yml +15 -0
  2. package/.github/workflows/ci.yml +161 -0
  3. package/CHANGELOG.md +41 -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/builder-run.d.ts +1 -0
  11. package/build/src/cli/builder-run.js +27 -0
  12. package/build/src/cli/effective-backlog-settings.js +70 -2
  13. package/build/src/cli/init.d.ts +34 -0
  14. package/build/src/cli/init.js +341 -61
  15. package/build/src/cli/kit.js +55 -12
  16. package/build/src/cli/pull-work-provider.js +346 -5
  17. package/build/src/cli/skill-drift-check.d.ts +1 -0
  18. package/build/src/cli/skill-drift-check.js +165 -0
  19. package/build/src/cli/telemetry-doctor.d.ts +37 -0
  20. package/build/src/cli/telemetry-doctor.js +53 -6
  21. package/build/src/cli/validate-hook-influence.js +37 -7
  22. package/build/src/cli/workflow-sidecar.d.ts +93 -8
  23. package/build/src/cli/workflow-sidecar.js +1175 -158
  24. package/build/src/cli.js +5 -0
  25. package/build/src/flow-kit/validate.d.ts +54 -34
  26. package/build/src/flow-kit/validate.js +237 -26
  27. package/build/src/index.d.ts +2 -0
  28. package/build/src/index.js +1 -0
  29. package/build/src/lib/console-connect-options.d.ts +97 -0
  30. package/build/src/lib/console-connect-options.js +199 -0
  31. package/build/src/lib/console-telemetry-validate.d.ts +49 -0
  32. package/build/src/lib/console-telemetry-validate.js +91 -0
  33. package/build/src/lib/flow-resolver.d.ts +54 -1
  34. package/build/src/lib/flow-resolver.js +112 -5
  35. package/build/src/lib/fs.d.ts +17 -0
  36. package/build/src/lib/fs.js +172 -0
  37. package/build/src/lib/local-artifact-root.d.ts +44 -1
  38. package/build/src/lib/local-artifact-root.js +131 -3
  39. package/build/src/runtime-adapters.d.ts +39 -3
  40. package/build/src/runtime-adapters.js +77 -31
  41. package/build/src/tools/build-universal-bundles.js +40 -2
  42. package/build/src/tools/codex-agent-routing.d.ts +2 -0
  43. package/build/src/tools/codex-agent-routing.js +49 -0
  44. package/build/src/tools/generate-context-map.js +1 -0
  45. package/build/src/tools/validate-source-tree.js +27 -1
  46. package/context/scripts/hooks/lib/kit-catalog.js +235 -0
  47. package/context/scripts/hooks/lib/runnable-command.js +177 -0
  48. package/context/scripts/hooks/stop-goal-fit.js +278 -48
  49. package/context/scripts/hooks/workflow-steering.js +121 -21
  50. package/context/scripts/package.json +3 -0
  51. package/context/scripts/telemetry/install-console-config.sh +25 -4
  52. package/context/scripts/telemetry/lib/config.sh +102 -12
  53. package/context/scripts/telemetry/lib/pricing.sh +50 -0
  54. package/context/scripts/telemetry/lib/session.sh +3 -0
  55. package/context/scripts/telemetry/lib/transport.sh +87 -0
  56. package/context/scripts/telemetry/lib/usage.sh +205 -4
  57. package/context/scripts/telemetry/telemetry.conf +6 -0
  58. package/context/scripts/telemetry/telemetry.sh +48 -0
  59. package/context/settings/workspace-backlog-provider-settings.example.json +48 -0
  60. package/docs/agent-usage-feedback-loop.md +35 -0
  61. package/docs/architecture-engine-and-kits.md +110 -0
  62. package/docs/context-map.md +2 -0
  63. package/docs/decisions/embeddable-engine.md +152 -0
  64. package/docs/decisions/index.md +3 -1
  65. package/docs/decisions/trust-ledger-retention.md +88 -0
  66. package/docs/decisions/workflow-enforcement.md +31 -9
  67. package/docs/fixture-ownership.md +3 -0
  68. package/docs/implementing-trust-reconciliation.md +129 -0
  69. package/docs/index.md +19 -9
  70. package/docs/integrations/flow-agents-console.md +167 -0
  71. package/docs/kit-authoring-guide.md +52 -21
  72. package/docs/spec/builder-flow-runtime.md +80 -0
  73. package/docs/spec/runtime-hook-surface.md +45 -1
  74. package/docs/specs/economics-record-contract.md +270 -0
  75. package/docs/specs/harness-capability-matrix.md +74 -0
  76. package/docs/specs/learning-review-proposals-contract.md +340 -0
  77. package/docs/specs/routing-efficiency-review.md +59 -0
  78. package/docs/verifiable-trust.md +74 -25
  79. package/docs/workflow-usage-guide.md +10 -0
  80. package/evals/acceptance/prove-capture-teeth.sh +132 -0
  81. package/evals/ci/antigaming-suite.sh +1 -0
  82. package/evals/ci/run-baseline.sh +72 -4
  83. package/evals/fixtures/economics/acceptance.json +12 -0
  84. package/evals/fixtures/economics/agents/tool-worker-1/events.jsonl +2 -0
  85. package/evals/fixtures/economics/agents/tool-worker-2/events.jsonl +2 -0
  86. package/evals/fixtures/economics/agents/tool-worker-3/events.jsonl +2 -0
  87. package/evals/fixtures/economics/agents/tool-worker-4/events.jsonl +1 -0
  88. package/evals/fixtures/economics/agents/tool-worker-5/events.jsonl +2 -0
  89. package/evals/fixtures/economics/critique.json +22 -0
  90. package/evals/fixtures/economics/expected-record.json +71 -0
  91. package/evals/fixtures/economics/session-usage-event.json +1 -0
  92. package/evals/fixtures/economics/state.json +11 -0
  93. package/evals/fixtures/economics/transcript.jsonl +3 -0
  94. package/evals/fixtures/hook-influence/cases.json +7 -7
  95. package/evals/fixtures/learning-review-proposals/balanced/economics.jsonl +6 -0
  96. package/evals/fixtures/learning-review-proposals/effect-follow-up/economics.jsonl +5 -0
  97. package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-1/trust.bundle +21 -0
  98. package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-2/trust.bundle +21 -0
  99. package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-3/trust.bundle +21 -0
  100. package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-4/trust.bundle +21 -0
  101. package/evals/fixtures/learning-review-proposals/effect-follow-up/sessions/task-lr-ef-5/trust.bundle +21 -0
  102. package/evals/fixtures/learning-review-proposals/pattern-present/economics.jsonl +6 -0
  103. package/evals/fixtures/learning-review-proposals/pattern-present/expected-aggregates.json +30 -0
  104. package/evals/fixtures/learning-review-proposals/pattern-present/expected-aggregates.md +66 -0
  105. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-1/gate-review.inquiries.json +26 -0
  106. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-1/trust.bundle +21 -0
  107. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-2/gate-review.inquiries.json +26 -0
  108. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-2/trust.bundle +21 -0
  109. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-3/gate-review.inquiries.json +26 -0
  110. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-3/trust.bundle +21 -0
  111. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-4/gate-review.inquiries.json +26 -0
  112. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-4/trust.bundle +21 -0
  113. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-5/trust.bundle +21 -0
  114. package/evals/fixtures/learning-review-proposals/pattern-present/sessions/task-lr-pp-6/trust.bundle +21 -0
  115. package/evals/fixtures/learning-review-proposals/repeat-window/economics.jsonl +6 -0
  116. package/evals/fixtures/learning-review-proposals/under-threshold/economics.jsonl +3 -0
  117. package/evals/fixtures/telemetry/usage-transcript-sample.jsonl +4 -0
  118. package/evals/fixtures/trust-reconcile-exploits/mcp-degrade.json +42 -0
  119. package/evals/integration/test_builder_entry_enforcement.sh +241 -0
  120. package/evals/integration/test_builder_step_producers.sh +18 -10
  121. package/evals/integration/test_bundle_install.sh +172 -0
  122. package/evals/integration/test_console_tenant_isolation.sh +167 -0
  123. package/evals/integration/test_critique_supersession_roundtrip.sh +4 -1
  124. package/evals/integration/test_dual_emit_flow_step.sh +10 -4
  125. package/evals/integration/test_economics_record.sh +674 -0
  126. package/evals/integration/test_effective_backlog_settings.sh +1 -1
  127. package/evals/integration/test_evidence_capture_hook.sh +17 -2
  128. package/evals/integration/test_exemption_usage_review.sh +198 -0
  129. package/evals/integration/test_fixture_retirement_audit.sh +2 -2
  130. package/evals/integration/test_flow_kit_install_git.sh +83 -0
  131. package/evals/integration/test_flowdef_session_activation.sh +0 -1
  132. package/evals/integration/test_flowdef_session_history_preservation.sh +13 -3
  133. package/evals/integration/test_gate_lockdown.sh +7 -0
  134. package/evals/integration/test_gate_review_inquiry_records.sh +9 -1
  135. package/evals/integration/test_goal_fit_hook.sh +2031 -0
  136. package/evals/integration/test_hook_category_behaviors.sh +8 -1
  137. package/evals/integration/test_hook_influence_cases.sh +25 -1
  138. package/evals/integration/test_install_merge.sh +227 -2
  139. package/evals/integration/test_kit_conformance_levels.sh +6 -6
  140. package/evals/integration/test_learning_review_proposals.sh +329 -0
  141. package/evals/integration/test_liveness_conflict_injection.sh +26 -22
  142. package/evals/integration/test_liveness_console_relay.sh +166 -0
  143. package/evals/integration/test_liveness_heartbeat.sh +17 -17
  144. package/evals/integration/test_liveness_worktree_root.sh +575 -0
  145. package/evals/integration/test_phase_map_and_gate_claim.sh +6 -1
  146. package/evals/integration/test_publish_delivery.sh +331 -1
  147. package/evals/integration/test_pull_work_board.sh +200 -0
  148. package/evals/integration/test_pull_work_provider.sh +1 -1
  149. package/evals/integration/test_record_check.sh +378 -0
  150. package/evals/integration/test_routing_efficiency.sh +71 -0
  151. package/evals/integration/test_runtime_adapter_activation.sh +28 -0
  152. package/evals/integration/test_session_resume_roundtrip.sh +16 -19
  153. package/evals/integration/test_skill_drift_check.sh +870 -0
  154. package/evals/integration/test_telemetry.sh +445 -0
  155. package/evals/integration/test_telemetry_doctor.sh +66 -0
  156. package/evals/integration/test_telemetry_usage_pipeline.sh +228 -0
  157. package/evals/integration/test_trust_reconcile_negatives.sh +30 -13
  158. package/evals/integration/test_trust_reconcile_trailer_diagnostic.sh +247 -0
  159. package/evals/integration/test_usage_cost.sh +61 -0
  160. package/evals/integration/test_workflow_sidecar_writer.sh +1395 -0
  161. package/evals/integration/test_workflow_steering_hook.sh +157 -16
  162. package/evals/integration/test_workspace_settings.sh +176 -0
  163. package/evals/lib/env.sh +26 -0
  164. package/evals/lib/node.sh +8 -0
  165. package/evals/run.sh +29 -0
  166. package/evals/static/test_ci_integration_coverage.sh +115 -0
  167. package/evals/static/test_declared_scope_forms_documented.sh +114 -0
  168. package/evals/static/test_universal_bundles.sh +34 -0
  169. package/evals/static/test_validate_source_kit_asset_scope.sh +259 -0
  170. package/evals/static/test_workflow_skills.sh +1 -1
  171. package/kits/builder/flows/build.flow.json +9 -18
  172. package/kits/builder/flows/publish-learn.flow.json +5 -1
  173. package/kits/builder/kit.json +120 -0
  174. package/kits/builder/skills/deliver/SKILL.md +42 -0
  175. package/kits/builder/skills/evidence-gate/SKILL.md +12 -0
  176. package/kits/builder/skills/execute-plan/SKILL.md +9 -0
  177. package/kits/builder/skills/learning-review/SKILL.md +51 -0
  178. package/kits/builder/skills/plan-work/SKILL.md +17 -20
  179. package/kits/builder/skills/pull-work/SKILL.md +21 -0
  180. package/kits/builder/skills/release-readiness/SKILL.md +12 -0
  181. package/kits/knowledge/kit.json +9 -0
  182. package/kits/veritas-governance/docs/README.md +35 -7
  183. package/kits/veritas-governance/fixtures/exemption-review/mixed-fresh-stale.DECLARED.json +14 -0
  184. package/kits/veritas-governance/kit.json +14 -0
  185. package/kits/veritas-governance/skills/exemption-usage-review/SKILL.md +128 -0
  186. package/kits/veritas-governance/skills/exemption-usage-review/review-exemptions.mjs +231 -0
  187. package/package.json +2 -2
  188. package/packaging/manifest.json +29 -0
  189. package/schemas/backlog-provider-settings.schema.json +13 -0
  190. package/schemas/workflow-state.schema.json +44 -0
  191. package/scripts/README.md +4 -0
  192. package/scripts/check-content-boundary.cjs +8 -1
  193. package/scripts/ci/trust-reconcile.js +136 -0
  194. package/scripts/hooks/codex-hook-adapter.js +77 -2
  195. package/scripts/hooks/evidence-capture.js +38 -5
  196. package/scripts/hooks/lib/codex-exit-code.js +316 -0
  197. package/scripts/hooks/lib/kit-catalog.js +235 -0
  198. package/scripts/hooks/lib/liveness-write.js +28 -1
  199. package/scripts/hooks/lib/local-artifact-paths.js +97 -1
  200. package/scripts/hooks/lib/runnable-command.js +177 -0
  201. package/scripts/hooks/lib/skill-drift.js +350 -0
  202. package/scripts/hooks/stop-goal-fit.js +278 -48
  203. package/scripts/hooks/workflow-steering.js +121 -21
  204. package/scripts/install-codex-home.sh +97 -47
  205. package/scripts/install-merge.js +72 -14
  206. package/scripts/install-owned-files.js +178 -0
  207. package/scripts/liveness/relay.sh +84 -0
  208. package/scripts/telemetry/economics-record.schema.json +145 -0
  209. package/scripts/telemetry/economics-record.sh +331 -0
  210. package/scripts/telemetry/install-console-config.sh +25 -4
  211. package/scripts/telemetry/learning-review-decide.sh +124 -0
  212. package/scripts/telemetry/learning-review-proposals.schema.json +161 -0
  213. package/scripts/telemetry/learning-review-proposals.sh +484 -0
  214. package/scripts/telemetry/lib/config.sh +102 -12
  215. package/scripts/telemetry/lib/pricing.sh +14 -6
  216. package/scripts/telemetry/lib/session.sh +3 -0
  217. package/scripts/telemetry/lib/transport.sh +133 -15
  218. package/scripts/telemetry/lib/usage.sh +121 -28
  219. package/scripts/telemetry/routing-efficiency.sh +0 -0
  220. package/scripts/telemetry/telemetry.conf +6 -0
  221. package/scripts/telemetry/telemetry.sh +48 -0
  222. package/src/builder-flow-run-adapter.ts +357 -0
  223. package/src/builder-flow-runtime.ts +348 -0
  224. package/src/cli/builder-flow-run-adapter.test.mjs +495 -0
  225. package/src/cli/builder-flow-runtime.test.mjs +213 -0
  226. package/src/cli/builder-run.ts +28 -0
  227. package/src/cli/codex-agent-routing.test.mjs +44 -0
  228. package/src/cli/codex-exit-code.test.mjs +207 -0
  229. package/src/cli/console-connect-options.test.mjs +329 -0
  230. package/src/cli/console-telemetry-validate.test.mjs +157 -0
  231. package/src/cli/effective-backlog-settings.ts +68 -2
  232. package/src/cli/flow-resolver-composition.test.mjs +72 -0
  233. package/src/cli/init.test.mjs +161 -0
  234. package/src/cli/init.ts +407 -62
  235. package/src/cli/kit-metadata-security.test.mjs +443 -0
  236. package/src/cli/kit.ts +50 -12
  237. package/src/cli/pull-work-provider.ts +377 -3
  238. package/src/cli/sidecar-pure-helpers.test.mjs +64 -0
  239. package/src/cli/skill-drift-check.ts +196 -0
  240. package/src/cli/telemetry-doctor.test.mjs +53 -0
  241. package/src/cli/telemetry-doctor.ts +50 -7
  242. package/src/cli/validate-hook-influence.ts +37 -6
  243. package/src/cli/workflow-sidecar.ts +1150 -151
  244. package/src/cli.ts +5 -0
  245. package/src/flow-kit/validate.ts +277 -38
  246. package/src/index.ts +19 -0
  247. package/src/lib/console-connect-options.ts +261 -0
  248. package/src/lib/console-telemetry-validate.ts +88 -0
  249. package/src/lib/flow-resolver.ts +117 -4
  250. package/src/lib/fs.ts +160 -0
  251. package/src/lib/local-artifact-root.ts +129 -3
  252. package/src/runtime-adapters.ts +113 -33
  253. package/src/tools/build-universal-bundles.ts +36 -2
  254. package/src/tools/codex-agent-routing.ts +48 -0
  255. package/src/tools/generate-context-map.ts +1 -0
  256. package/src/tools/validate-source-tree.ts +26 -1
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: "exemption-usage-review"
3
+ description: "Periodic audit of standing delivery/DECLARED no-agent-delivery exemptions (ADR 0022 §3): lists every current exemption's scope, reason, approver, and age since declared_at, flags entries overdue for owner re-confirmation against a configurable staleness threshold, and walks the file's git history for a supplementary commit-level trail. Use when periodically reviewing which no-agent-delivery exemptions are still standing and whether any need re-confirmation."
4
+ ---
5
+
6
+ # Exemption Usage Review
7
+
8
+ Read `delivery/DECLARED`, list every standing exemption with its age, and flag which ones
9
+ are overdue for owner re-confirmation — **process visibility, not enforcement** (ADR 0022
10
+ §3). This skill never changes `delivery/DECLARED` and never changes
11
+ `scripts/ci/trust-reconcile.js`'s reconciliation/exit-code behavior.
12
+
13
+ ## Contract
14
+
15
+ - Read-only against `delivery/DECLARED` and its git history — no write, no mutation, no
16
+ append, no delete, anywhere in this repo.
17
+ - Never gates anything: this is a **skill**, not a flow. It has no `expects[]` claim to
18
+ evaluate and attaches no evidence. There is no pass/fail verdict — only a report.
19
+ - Never influences `scripts/ci/trust-reconcile.js`'s reconciliation decision or exit code.
20
+ The reconciler is unaware this skill exists, exactly as it is unaware of the rest of this
21
+ kit (ADR 0022 §3, "uninstalling `veritas-governance` must never weaken enforcement").
22
+ - Does not reimplement `scripts/ci/trust-reconcile.js`'s `matchesScope` /
23
+ `matchesScopeCondition` / `parseDeclaredMarker` functions. The review lists **every**
24
+ standing entry unconditionally — it has no "does this scope match the current change"
25
+ question to answer (that is the reconciler's job, evaluated per-change at CI time). This
26
+ skill answers a different question — "what exemptions exist at all, and how old are they" —
27
+ so it needs only a much simpler parse-and-age routine, not the reconciler's scope-matching
28
+ engine. See `review-exemptions.mjs`'s header comment for the same note in code.
29
+ - No `.kontourai/flow-agents/<slug>/` session artifact contract applies here: this skill
30
+ produces a standalone report (stdout, human-readable or `--json`), not a workflow sidecar
31
+ artifact. State this plainly rather than inventing an artifact contract this tool does not
32
+ need.
33
+
34
+ ## What this review does and does not verify
35
+
36
+ **Does verify:**
37
+ - Every entry currently present in the live `delivery/DECLARED` file is listed with its
38
+ `scope`, `reason`, `approved_by`, `declared_at`, a computed `age_days` (now, or `--as-of`,
39
+ minus `declared_at`), and a `stale` boolean (`age_days > --stale-days`, default 90).
40
+ - The `git log --follow -- delivery/DECLARED` commit history for the file — optionally
41
+ bounded to a `--window-days` review window — is walked and reported as a supplementary
42
+ commit-level trail (sha, author, date, subject).
43
+
44
+ **Does not verify:**
45
+ - Whether any `approved_by` value names a real, authenticated human approver.
46
+ `approved_by` is free text on `delivery/DECLARED` itself (see this kit's own
47
+ `docs/README.md`, "Human-approval evidence: what is and is not enforced") — this tool
48
+ reports what the field says, it does not authenticate it.
49
+ - Whether any entry's `scope` (`ref:`/`commit:`/`author:`/`branch-prefix:`, or a
50
+ compound-AND combination) currently matches any particular change. That is
51
+ `scripts/ci/trust-reconcile.js`'s job at reconciliation time — a "does this exemption
52
+ apply to THIS change" question this review never asks. This review lists every entry
53
+ regardless of whether it currently matches anything.
54
+ - A full point-in-time reconstruction and diff of every historical version of
55
+ `delivery/DECLARED` (e.g. "entry X was removed then silently re-added with a different
56
+ reason"). `history_commits` gives the commit-level trail a human reviewer can inspect
57
+ further with `git show <sha>:delivery/DECLARED`; this tool does not do that per-commit
58
+ content diffing itself.
59
+ - Anything about whether the exemption is still *needed* — that is an owner judgment call
60
+ this review surfaces evidence for, not a decision it makes.
61
+
62
+ ## Inputs
63
+
64
+ - `delivery/DECLARED` (default; override via `--declared-path <path>` for a fixture or a
65
+ different repo layout).
66
+ - `--repo-root <path>` (default: current working directory) — the git repository whose
67
+ history is walked.
68
+ - `--stale-days <n>` (default: `90`) — the configurable age threshold. Entries with
69
+ `age_days > stale-days` are flagged `stale: true`.
70
+ - `--window-days <n>` (optional; default: full history) — bounds the `git log --follow`
71
+ history walk to commits within the last `<n>` days of `--as-of`. Does not affect which
72
+ *current* entries are listed — the live file's entries are always listed in full; this
73
+ only bounds the supplementary history trail.
74
+ - `--as-of <ISO8601>` (default: `new Date().toISOString()`, i.e. wall-clock now) —
75
+ deterministic override for "now", so a scheduled/periodic invocation or an eval is not
76
+ wall-clock-flaky. Mirrors this repo's `TRUST_RECONCILE_SHA`/`_REF`/`_ACTOR` override
77
+ convention in `scripts/ci/trust-reconcile.js`.
78
+ - `--json` (optional) — emit a machine-readable JSON report instead of the human-readable
79
+ table.
80
+
81
+ ## How to run the review
82
+
83
+ ```bash
84
+ # Human-readable report against this repo's real delivery/DECLARED, default 90-day threshold.
85
+ node kits/veritas-governance/skills/exemption-usage-review/review-exemptions.mjs
86
+
87
+ # Machine-readable, with an explicit deterministic "now" and a tighter threshold.
88
+ node kits/veritas-governance/skills/exemption-usage-review/review-exemptions.mjs \
89
+ --as-of 2026-07-05T00:00:00Z --stale-days 30 --json
90
+
91
+ # Bound the supplementary history walk to the last 180 days.
92
+ node kits/veritas-governance/skills/exemption-usage-review/review-exemptions.mjs \
93
+ --window-days 180
94
+ ```
95
+
96
+ Exit code: `0` on a clean run — even when one or more entries are flagged `stale`. Staleness
97
+ is informational output for a human owner to act on, never a script failure condition. Exit
98
+ `2` on bad arguments; exit `3` when `delivery/DECLARED` is missing or not valid JSON (nothing
99
+ to review — a tool-usage failure, not a staleness finding).
100
+
101
+ ## Output shape
102
+
103
+ Each standing-exemption row: `{scope, reason, approved_by, declared_at, age_days, stale}`.
104
+ `age_days`/`stale` are `null` (never silently coerced to `true`/`false`) when `declared_at`
105
+ is missing or unparsable — an honest "unknown", not a guessed verdict.
106
+
107
+ The history section separately reports `{sha, author, date, subject}` per commit found by
108
+ `git log --follow -- delivery/DECLARED` in the (optionally windowed) range. A git failure
109
+ (no repository, no history, shallow clone) degrades to an empty history list plus a
110
+ diagnostic note — never a crash and never a fabricated commit.
111
+
112
+ ## What happens with a stale entry
113
+
114
+ A `stale: true` entry means: this exemption has been standing longer than the configured
115
+ threshold without a recorded re-confirmation. This review does not renew, revoke, or modify
116
+ the entry itself — it surfaces the finding for an owner to act on: re-confirm the exemption
117
+ is still needed (e.g. re-run the kit's exemption-issuance flow to record a fresh
118
+ `declared_at` with the same or updated `reason`/`approved_by`), or remove it from
119
+ `delivery/DECLARED` by direct, CODEOWNERS-reviewed edit. Both of those actions are outside
120
+ this skill's scope by design — this skill only reports.
121
+
122
+ ## Accepted gap: nothing schedules this review
123
+
124
+ ADR 0022 §3 asks for the review tool to exist, not for automated scheduling. This skill has
125
+ no CI job, cron trigger, or scheduled invocation wired to it — an operator must run it
126
+ periodically themselves (see "How to run the review" above). This is an accepted,
127
+ explicitly-named scope boundary, not a silent gap: building a scheduling/CI-trigger surface
128
+ was not requested and is not part of this slice.
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env node
2
+ // review-exemptions.mjs — Veritas Governance Kit exemption-usage-review helper (ADR 0022 §3).
3
+ //
4
+ // PURPOSE. A periodic audit tool that lists every standing `delivery/DECLARED` exemption
5
+ // (scope, reason, approver, age since declared_at) and flags stale/overdue entries against a
6
+ // configurable threshold, for owner re-confirmation. This is PROCESS VISIBILITY, not
7
+ // enforcement: it is read-only, writes nothing outside its own stdout/report output, and its
8
+ // own findings never feed back into `scripts/ci/trust-reconcile.js`'s exit code or into
9
+ // `delivery/DECLARED` itself. See ADR 0022 §3: "a periodic audit skill/flow that walks
10
+ // delivery/DECLARED history (git log --follow -- delivery/DECLARED) merged in a review window
11
+ // and surfaces every standing exemption (scope, reason, approver, age) for owner
12
+ // re-confirmation — process visibility, not enforcement."
13
+ //
14
+ // WHAT THIS DOES VERIFY:
15
+ // - Every entry currently present in the live `delivery/DECLARED` JSON array/object is
16
+ // listed with its `scope`, `reason`, `approved_by`, `declared_at`, computed `age_days`,
17
+ // and a `stale` boolean (age_days > --stale-days).
18
+ // - The `git log --follow -- delivery/DECLARED` commit history for the file (within
19
+ // --window-days when given, else full history) is walked and reported as
20
+ // `history_commits` (sha, author, date, subject) — this is a supplementary "how did this
21
+ // file change over time" view, separate from the "what's standing today" listing.
22
+ //
23
+ // WHAT THIS DOES NOT VERIFY (state plainly, per this kit's honesty convention):
24
+ // - It does NOT verify that any `approved_by` value corresponds to a real, authenticated
25
+ // human approver — that field is free text in `delivery/DECLARED` itself (see
26
+ // docs/README.md "Human-approval evidence: what is and is not enforced"); this tool only
27
+ // reports what the field says, never authenticates it.
28
+ // - It does NOT re-evaluate whether a scope's `ref:`/`commit:`/`author:`/`branch-prefix:`
29
+ // condition currently matches any particular change — that is
30
+ // `scripts/ci/trust-reconcile.js`'s `matchesScope`/`matchesScopeCondition` job (a
31
+ // "does this exemption apply to THIS change" question). This tool answers a different
32
+ // question — "what exemptions are standing at all, and how old are they" — for every
33
+ // entry, regardless of whether any given entry's scope currently matches anything.
34
+ // - It does NOT reconstruct a full point-in-time list of entries that existed at each
35
+ // historical commit and diff them against each other (e.g. it does not detect "entry X
36
+ // was removed then silently re-added with a different reason") — `history_commits` gives
37
+ // the commit-level trail (who changed the file, when) for a human reviewer to inspect
38
+ // with `git show <sha>:delivery/DECLARED` themselves; this tool does not do that
39
+ // per-commit content diffing itself.
40
+ // - It does NOT mutate `delivery/DECLARED`, does not attach evidence, does not run a Flow
41
+ // gate, and has no exit-code-driven pass/fail: this script's own process exit code is 0
42
+ // on any clean parse, regardless of how many entries are flagged stale — staleness is
43
+ // informational output, not a script failure condition.
44
+ //
45
+ // NO-FORK NOTE: this script intentionally does NOT reuse trust-reconcile.js's
46
+ // `matchesScope`/`matchesScopeCondition`/`parseDeclaredMarker` functions. The review lists ALL
47
+ // entries unconditionally (it has no "does this scope match the current context" question to
48
+ // answer), so it needs only a much simpler "parse the JSON array, compute age" routine, not
49
+ // the reconciler's scope-matching engine. This is a deliberate scope difference, not a missed
50
+ // reuse opportunity — see SKILL.md.
51
+ //
52
+ // Usage:
53
+ // node review-exemptions.mjs [--declared-path <path>] [--repo-root <path>]
54
+ // [--stale-days <n>] [--window-days <n>] [--as-of <ISO8601>] [--json]
55
+ //
56
+ // Exit: 0 on a clean run (parse succeeded, report printed) — even when entries are flagged
57
+ // stale. Exit 2 on bad args. Exit 3 when delivery/DECLARED is missing or not valid JSON
58
+ // (nothing to review — this is a tool-usage failure, not a staleness finding).
59
+
60
+ import { readFileSync } from "node:fs";
61
+ import { spawnSync } from "node:child_process";
62
+ import path from "node:path";
63
+
64
+ const DEFAULT_STALE_DAYS = 90;
65
+ const MS_PER_DAY = 24 * 60 * 60 * 1000;
66
+
67
+ function parseArgs(argv) {
68
+ const out = {
69
+ declaredPath: undefined,
70
+ repoRoot: process.cwd(),
71
+ staleDays: DEFAULT_STALE_DAYS,
72
+ windowDays: undefined,
73
+ asOf: undefined,
74
+ json: false,
75
+ };
76
+ for (let i = 0; i < argv.length; i++) {
77
+ const a = argv[i];
78
+ if (a === "--declared-path") out.declaredPath = argv[++i];
79
+ else if (a === "--repo-root") out.repoRoot = argv[++i];
80
+ else if (a === "--stale-days") out.staleDays = Number(argv[++i]);
81
+ else if (a === "--window-days") out.windowDays = Number(argv[++i]);
82
+ else if (a === "--as-of") out.asOf = argv[++i];
83
+ else if (a === "--json") out.json = true;
84
+ else if (a === "--help" || a === "-h") out.help = true;
85
+ }
86
+ return out;
87
+ }
88
+
89
+ function usage() {
90
+ return "usage: review-exemptions.mjs [--declared-path <path>] [--repo-root <path>] "
91
+ + "[--stale-days <n>] [--window-days <n>] [--as-of <ISO8601>] [--json]\n";
92
+ }
93
+
94
+ /**
95
+ * Read and parse the live delivery/DECLARED file into a flat array of raw entries.
96
+ * Mirrors trust-reconcile.js's tolerant single-object-or-array shape (a single {scope,...}
97
+ * object OR a JSON array of such objects) WITHOUT reusing its parseDeclaredMarker() function
98
+ * (see NO-FORK NOTE above) — this parse is deliberately simpler: no well-formed/malformed
99
+ * bucketing, no scope-matching, just "read the array, keep what looks like an entry object".
100
+ */
101
+ export function readDeclaredEntries(declaredPath) {
102
+ const raw = readFileSync(declaredPath, "utf8");
103
+ const parsed = JSON.parse(raw);
104
+ const entries = Array.isArray(parsed) ? parsed : [parsed];
105
+ return entries.filter((e) => e && typeof e === "object");
106
+ }
107
+
108
+ /** Age in whole days between an ISO8601 `declared_at` and an ISO8601 `asOfIso` "now". */
109
+ export function ageDays(declaredAtIso, asOfIso) {
110
+ const declared = new Date(declaredAtIso).getTime();
111
+ const asOf = new Date(asOfIso).getTime();
112
+ if (Number.isNaN(declared) || Number.isNaN(asOf)) return null;
113
+ return Math.floor((asOf - declared) / MS_PER_DAY);
114
+ }
115
+
116
+ /**
117
+ * Build the standing-exemption report rows: one row per delivery/DECLARED entry, with
118
+ * computed age_days and a stale boolean (age_days > staleDays). Entries with an unparsable
119
+ * declared_at get age_days: null, stale: null (never silently coerced to fresh or stale).
120
+ */
121
+ export function buildReport(entries, { staleDays, asOfIso }) {
122
+ return entries.map((entry) => {
123
+ const declaredAt = typeof entry.declared_at === "string" ? entry.declared_at : null;
124
+ const age = declaredAt ? ageDays(declaredAt, asOfIso) : null;
125
+ return {
126
+ scope: typeof entry.scope === "string" ? entry.scope : null,
127
+ reason: typeof entry.reason === "string" ? entry.reason : null,
128
+ approved_by: typeof entry.approved_by === "string" ? entry.approved_by : null,
129
+ declared_at: declaredAt,
130
+ age_days: age,
131
+ stale: age === null ? null : age > staleDays,
132
+ };
133
+ });
134
+ }
135
+
136
+ /**
137
+ * Walk `git log --follow -- <declaredRelPath>` history for delivery/DECLARED, optionally
138
+ * bounded to a review window (--window-days before asOfIso). Read-only (`git log`, no
139
+ * mutation). Never throws — a git failure (no repo, no history, shallow clone) degrades to
140
+ * an empty list plus a diagnostic note, never a crash.
141
+ */
142
+ export function walkDeclaredHistory(repoRoot, declaredRelPath, { windowDays, asOfIso }) {
143
+ const args = ["log", "--follow", "--date=iso-strict", "--format=%H%x1f%an%x1f%ad%x1f%s"];
144
+ if (windowDays && Number.isFinite(windowDays) && windowDays > 0) {
145
+ const since = new Date(new Date(asOfIso).getTime() - windowDays * MS_PER_DAY).toISOString();
146
+ args.push(`--since=${since}`);
147
+ }
148
+ args.push("--", declaredRelPath);
149
+ try {
150
+ const res = spawnSync("git", args, { cwd: repoRoot, encoding: "utf8" });
151
+ if (!res || res.error || res.status !== 0) {
152
+ return { commits: [], diagnostic: "git log --follow unavailable or failed; history_commits is empty (no historical trail could be walked)" };
153
+ }
154
+ const lines = res.stdout.split("\n").map((l) => l.trim()).filter(Boolean);
155
+ const commits = lines.map((line) => {
156
+ const [sha, author, date, subject] = line.split("\x1f");
157
+ return { sha, author, date, subject };
158
+ });
159
+ return { commits, diagnostic: null };
160
+ } catch (err) {
161
+ return { commits: [], diagnostic: `git log --follow threw: ${err && err.message ? err.message : String(err)}` };
162
+ }
163
+ }
164
+
165
+ function renderHuman(report, history, opts) {
166
+ const lines = [];
167
+ lines.push(`Exemption usage review — as-of ${opts.asOfIso}, stale threshold ${opts.staleDays} day(s)`);
168
+ lines.push(`PROCESS-ONLY: read-only report. Does not modify delivery/DECLARED or scripts/ci/trust-reconcile.js's behavior.`);
169
+ lines.push("");
170
+ lines.push(`Standing exemptions (${report.length}):`);
171
+ if (report.length === 0) lines.push(" (none)");
172
+ for (const row of report) {
173
+ const staleTag = row.stale === null ? "UNKNOWN-AGE" : row.stale ? "STALE — needs owner re-confirmation" : "fresh";
174
+ lines.push(` - scope: ${row.scope ?? "(missing)"}`);
175
+ lines.push(` reason: ${row.reason ?? "(missing)"}`);
176
+ lines.push(` approved_by: ${row.approved_by ?? "(missing)"}`);
177
+ lines.push(` declared_at: ${row.declared_at ?? "(missing)"} | age_days: ${row.age_days ?? "n/a"} | ${staleTag}`);
178
+ }
179
+ lines.push("");
180
+ lines.push(`History (git log --follow -- delivery/DECLARED)${opts.windowDays ? ` — last ${opts.windowDays} day(s)` : " — full history"}:`);
181
+ if (history.diagnostic) lines.push(` NOTE: ${history.diagnostic}`);
182
+ if (history.commits.length === 0 && !history.diagnostic) lines.push(" (no commits found)");
183
+ for (const c of history.commits) {
184
+ lines.push(` - ${c.sha.slice(0, 12)} ${c.date} ${c.author} — ${c.subject}`);
185
+ }
186
+ return lines.join("\n");
187
+ }
188
+
189
+ function main() {
190
+ const args = parseArgs(process.argv.slice(2));
191
+ if (args.help) {
192
+ process.stdout.write(usage());
193
+ return 0;
194
+ }
195
+ const repoRoot = path.resolve(args.repoRoot);
196
+ const declaredPath = args.declaredPath
197
+ ? path.resolve(args.declaredPath)
198
+ : path.join(repoRoot, "delivery", "DECLARED");
199
+ const staleDays = Number.isFinite(args.staleDays) ? args.staleDays : DEFAULT_STALE_DAYS;
200
+ const asOfIso = args.asOf || new Date().toISOString();
201
+
202
+ let entries;
203
+ try {
204
+ entries = readDeclaredEntries(declaredPath);
205
+ } catch (err) {
206
+ process.stderr.write(`cannot read/parse delivery/DECLARED at ${declaredPath}: ${err && err.message ? err.message : String(err)}\n`);
207
+ return 3;
208
+ }
209
+
210
+ const report = buildReport(entries, { staleDays, asOfIso });
211
+ const declaredRelPath = path.relative(repoRoot, declaredPath) || "delivery/DECLARED";
212
+ const history = walkDeclaredHistory(repoRoot, declaredRelPath, { windowDays: args.windowDays, asOfIso });
213
+
214
+ if (args.json) {
215
+ process.stdout.write(`${JSON.stringify({
216
+ as_of: asOfIso,
217
+ stale_days: staleDays,
218
+ window_days: args.windowDays ?? null,
219
+ declared_path: declaredPath,
220
+ entries: report,
221
+ history_commits: history.commits,
222
+ history_diagnostic: history.diagnostic,
223
+ }, null, 2)}\n`);
224
+ } else {
225
+ process.stdout.write(`${renderHuman(report, history, { asOfIso, staleDays, windowDays: args.windowDays })}\n`);
226
+ }
227
+ return 0;
228
+ }
229
+
230
+ const isEntry = process.argv[1] && import.meta.url === `file://${process.argv[1]}`;
231
+ if (isEntry) process.exitCode = main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontourai/flow-agents",
3
- "version": "3.3.0",
3
+ "version": "3.4.0",
4
4
  "description": "Flow Agents — a Kontour product that applies Flow and Veritas discipline as a portable process layer inside the agent tools you already use: Claude Code, Codex, Kiro, opencode, pi, and GitHub Actions — with framework adapters (AWS Strands preview) on the same policy-engine contract.",
5
5
  "keywords": [
6
6
  "agents",
@@ -149,7 +149,7 @@
149
149
  "typescript": "^6.0.3"
150
150
  },
151
151
  "dependencies": {
152
- "@kontourai/flow": "^2.0.0"
152
+ "@kontourai/flow": "^3.0.0"
153
153
  },
154
154
  "optionalDependencies": {
155
155
  "@kontourai/surface": "^2.0.0",
@@ -56,6 +56,13 @@
56
56
  "excluded_agents": [
57
57
  "dev"
58
58
  ],
59
+ "allowed_agent_models": [
60
+ "gpt-5.3-codex-spark",
61
+ "gpt-5.4-mini",
62
+ "gpt-5.5",
63
+ "gpt-5.6-sol",
64
+ "gpt-5.6-terra"
65
+ ],
59
66
  "profiles": {
60
67
  "builder": {
61
68
  "model": "gpt-5.5",
@@ -114,6 +121,28 @@
114
121
  "agi-nova-beta-1m": "medium",
115
122
  "default": "medium"
116
123
  },
124
+ "codex_agent_map": {
125
+ "tool-planner": {
126
+ "model": "gpt-5.6-sol",
127
+ "reasoning_effort": "high"
128
+ },
129
+ "tool-worker": {
130
+ "model": "gpt-5.6-terra",
131
+ "reasoning_effort": "high"
132
+ },
133
+ "tool-code-reviewer": {
134
+ "model": "gpt-5.6-sol",
135
+ "reasoning_effort": "high"
136
+ },
137
+ "tool-security-reviewer": {
138
+ "model": "gpt-5.6-sol",
139
+ "reasoning_effort": "high"
140
+ },
141
+ "tool-verifier": {
142
+ "model": "gpt-5.6-sol",
143
+ "reasoning_effort": "high"
144
+ }
145
+ },
117
146
  "target_substitutions": {
118
147
  "common": [],
119
148
  "claude_code": [
@@ -8,6 +8,7 @@
8
8
  "properties": {
9
9
  "$schema": { "type": "string" },
10
10
  "schema_version": { "const": "1.0" },
11
+ "workspace": { "$ref": "#/$defs/workspace" },
11
12
  "defaults": { "$ref": "#/$defs/backlog_settings" },
12
13
  "projects": {
13
14
  "type": "array",
@@ -22,6 +23,18 @@
22
23
  { "type": "object", "required": ["project"] }
23
24
  ]
24
25
  },
26
+ "workspace": {
27
+ "type": "object",
28
+ "additionalProperties": false,
29
+ "required": ["repos"],
30
+ "properties": {
31
+ "repos": {
32
+ "type": "array",
33
+ "items": { "type": "string", "minLength": 1 },
34
+ "uniqueItems": true
35
+ }
36
+ }
37
+ },
25
38
  "backlog_settings": {
26
39
  "type": "object",
27
40
  "additionalProperties": false,
@@ -59,6 +59,36 @@
59
59
  },
60
60
  "uniqueItems": true
61
61
  },
62
+ "work_item_refs": {
63
+ "type": "array",
64
+ "items": {
65
+ "type": "string",
66
+ "minLength": 1
67
+ },
68
+ "minItems": 1,
69
+ "uniqueItems": true,
70
+ "description": "Provider-neutral work item references anchoring this workflow session."
71
+ },
72
+ "flow_run": {
73
+ "type": "object",
74
+ "additionalProperties": false,
75
+ "required": ["run_id", "definition_id", "definition_version", "status", "current_step", "run_ref", "open_gate_ids"],
76
+ "properties": {
77
+ "run_id": { "type": "string", "minLength": 1 },
78
+ "definition_id": { "type": "string", "minLength": 1 },
79
+ "definition_version": { "type": "string", "minLength": 1 },
80
+ "status": { "type": "string", "minLength": 1 },
81
+ "current_step": { "type": "string", "minLength": 1 },
82
+ "run_ref": { "type": "string", "minLength": 1 },
83
+ "open_gate_ids": {
84
+ "type": "array",
85
+ "items": { "type": "string", "minLength": 1 },
86
+ "uniqueItems": true
87
+ },
88
+ "route_back_attempt": { "type": "integer", "minimum": 1 },
89
+ "route_back_max_attempts": { "type": "integer", "minimum": 1 }
90
+ }
91
+ },
62
92
  "next_action": {
63
93
  "type": "object",
64
94
  "additionalProperties": false,
@@ -78,6 +108,20 @@
78
108
  },
79
109
  "target_artifact": {
80
110
  "type": "string"
111
+ },
112
+ "skills": {
113
+ "type": "array",
114
+ "items": { "type": "string", "minLength": 1 },
115
+ "uniqueItems": true
116
+ },
117
+ "operations": {
118
+ "type": "array",
119
+ "items": { "type": "string", "minLength": 1 },
120
+ "uniqueItems": true
121
+ },
122
+ "command": {
123
+ "type": "string",
124
+ "minLength": 1
81
125
  }
82
126
  }
83
127
  }
package/scripts/README.md CHANGED
@@ -69,9 +69,11 @@ renamed, or changes category, update the table and the validator together.
69
69
  | `desktop-notify.sh` | local notification helper | `evals/integration/test_hook_category_behaviors.sh` | Optional local desktop notification helper. |
70
70
  | `lib/actor-identity.js` | shared hook library | `evals/integration/test_actor_identity.sh` | Shared runtime-agnostic actor identity resolver (`resolveActor`) consumed by `workflow-steering.js` and `workflow-sidecar.js` to retire the shared `"local"` liveness-actor default (issue #287). |
71
71
  | `lib/audit-transport.sh` | shared hook library | `evals/integration/test_hook_category_behaviors.sh`, `evals/integration/test_telemetry.sh` | Shared audit event transport functions. |
72
+ | `lib/codex-exit-code.js` | shared hook library | `evals/acceptance/prove-capture-teeth.sh` | Shared codex-only host-banner exit-code extraction (`extractExitCodeFromBanner`, `readExitCodeFromRollout`) — parses the `Process exited with code N` prose the codex CLI writes into a `function_call_output` payload or session rollout so `codex-hook-adapter.js` can inject a structured exit code before `evidence-capture.js` observes (issue #470). |
72
73
  | `lib/config-protection-remedies.js` | shared hook library | `evals/integration/test_hook_category_behaviors.sh` | Sanctioned-remedy table for `config-protection.js` (WS8/ADR 0020 extraction). |
73
74
  | `lib/current-pointer.js` | shared hook library | `evals/integration/test_current_json_per_actor.sh` | Shared per-actor "current" pointer reader/writer (`readCurrentPointer`, `writePerActorCurrent`) — the compat-shim fallback (per-actor `current/<actor>.json` first, legacy global `current.json` fallback) every actor-aware consumer routes through (issue #291). |
74
75
  | `lib/hook-flags.js` | shared hook library | `evals/integration/test_hook_category_behaviors.sh` | Shared profile/disable flag parsing. |
76
+ | `lib/kit-catalog.js` | shared hook library | `evals/integration/test_workflow_steering_hook.sh` | Build-free kit catalog reader used by `workflow-steering.js` to load kit-declared workflow triggers while hooks remain fail-open and independent of compiled TypeScript output. |
75
77
  | `lib/liveness-heartbeat.js` | shared hook library | `evals/integration/test_liveness_heartbeat.sh` | Shared tool-activity liveness heartbeat (`maybeEmitHeartbeat`); rides `postToolUse` across all four telemetry hook wrappers, throttled and fail-open (issue #288). |
76
78
  | `lib/liveness-policy.js` | shared hook library | `evals/integration/test_workflow_sidecar_writer.sh`, `evals/integration/test_liveness_heartbeat.sh` | Shared liveness on/off predicate (`isLivenessEnabled`, default-on/opt-out) and TTL/heartbeat-throttle default resolution (issue #288). |
77
79
  | `lib/liveness-read.js` | shared hook library | `evals/integration/test_session_resume_roundtrip.sh` | Shared liveness event reader + freshness check (`readLivenessEvents`, `freshHolders`); consumed by the reground hook and `workflow-sidecar liveness status`. |
@@ -79,6 +81,8 @@ renamed, or changes category, update the table and the validator together.
79
81
  | `lib/local-artifact-paths.js` | shared hook library | `evals/integration/test_migrate_local_artifacts.sh`, `evals/integration/test_workflow_sidecar_writer.sh` | Shared `.kontourai/flow-agents` artifact-root helpers for CJS hooks. |
80
82
  | `lib/patterns.sh` | shared hook library | `evals/integration/test_hook_category_behaviors.sh`, `evals/integration/test_telemetry.sh` | Shared shell pattern constants. |
81
83
  | `lib/resolve-formatter.js` | shared hook library | `evals/integration/test_hook_category_behaviors.sh` | Shared formatter resolution helper. |
84
+ | `lib/runnable-command.js` | shared hook library | `evals/integration/test_goal_fit_hook.sh` | Shared runnable-command-text heuristic (`isRunnableCommandText`) consumed by `stop-goal-fit.js` and `workflow-sidecar.js` so record-time and Stop-time checks never drift (issue #412). |
85
+ | `lib/skill-drift.js` | shared hook library | `evals/integration/test_skill_drift_check.sh` | Shared installed-Claude-Code-skill manifest builder and drift classifier (`buildManifest`, `compareSkillDrift`) — the single choke point the `init --global` manifest writer, the `flow-agents skill-drift-check` CLI, and the SessionStart advisory all route through so they never disagree on drift classification (issue #439). |
82
86
 
83
87
  ## Telemetry
84
88
 
@@ -37,6 +37,11 @@ function trackedFiles() {
37
37
  return output.split("\0").filter(Boolean);
38
38
  }
39
39
 
40
+ function untrackedFiles() {
41
+ const output = execFileSync("git", ["ls-files", "--others", "--exclude-standard", "-z"], { encoding: "utf8" });
42
+ return output.split("\0").filter(Boolean);
43
+ }
44
+
40
45
  function isIgnoredPath(filePath) {
41
46
  return filePath === SELF || ignoredPathPatterns.some((pattern) => pattern.test(filePath));
42
47
  }
@@ -51,7 +56,9 @@ function lineNumberFor(content, index) {
51
56
 
52
57
  const findings = [];
53
58
 
54
- for (const filePath of trackedFiles()) {
59
+ const allFiles = new Set([...trackedFiles(), ...untrackedFiles()]);
60
+
61
+ for (const filePath of allFiles) {
55
62
  if (isWorkflowRuntimeArtifact(filePath)) {
56
63
  findings.push({
57
64
  filePath,