@kontourai/flow-agents 0.1.1

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 (418) hide show
  1. package/.githooks/pre-push +11 -0
  2. package/.github/workflows/ci.yml +210 -0
  3. package/.github/workflows/docs-pages.yml +52 -0
  4. package/.github/workflows/publish-npm.yml +104 -0
  5. package/AGENTS.md +26 -0
  6. package/CHANGELOG.md +66 -0
  7. package/CODE_OF_CONDUCT.md +25 -0
  8. package/CONTEXT.md +300 -0
  9. package/CONTRIBUTING.md +44 -0
  10. package/LICENSE +201 -0
  11. package/README.md +129 -0
  12. package/SECURITY.md +33 -0
  13. package/agent-cards/dev.json +19 -0
  14. package/agents/dev.json +127 -0
  15. package/agents/tool-code-reviewer.json +61 -0
  16. package/agents/tool-dependencies-updater.json +118 -0
  17. package/agents/tool-explore-config.json +92 -0
  18. package/agents/tool-explore-deps.json +92 -0
  19. package/agents/tool-explore-entry.json +92 -0
  20. package/agents/tool-explore-patterns.json +92 -0
  21. package/agents/tool-explore-structure.json +92 -0
  22. package/agents/tool-explore-tests.json +92 -0
  23. package/agents/tool-planner.json +57 -0
  24. package/agents/tool-playwright.json +145 -0
  25. package/agents/tool-security-reviewer.json +56 -0
  26. package/agents/tool-verifier.json +61 -0
  27. package/agents/tool-worker.json +58 -0
  28. package/build/src/cli/console-learning-projection.js +123 -0
  29. package/build/src/cli/docs-preview.js +39 -0
  30. package/build/src/cli/effective-backlog-settings.js +102 -0
  31. package/build/src/cli/export-bookmarks.js +38 -0
  32. package/build/src/cli/fixture-retirement-audit.js +140 -0
  33. package/build/src/cli/flow-kit.js +138 -0
  34. package/build/src/cli/import-bookmarks.js +50 -0
  35. package/build/src/cli/init.js +239 -0
  36. package/build/src/cli/instinct-cli.js +93 -0
  37. package/build/src/cli/promote-workflow-artifact.js +63 -0
  38. package/build/src/cli/publish-change-helper.js +154 -0
  39. package/build/src/cli/pull-work-provider.js +469 -0
  40. package/build/src/cli/runtime-adapter.js +23 -0
  41. package/build/src/cli/telemetry-doctor.js +221 -0
  42. package/build/src/cli/usage-feedback.js +443 -0
  43. package/build/src/cli/validate-hook-influence.js +152 -0
  44. package/build/src/cli/validate-source-tree.js +31 -0
  45. package/build/src/cli/validate-workflow-artifacts.js +486 -0
  46. package/build/src/cli/veritas-governance.js +262 -0
  47. package/build/src/cli/workflow-artifact-cleanup-audit.js +272 -0
  48. package/build/src/cli/workflow-sidecar.js +816 -0
  49. package/build/src/cli.js +89 -0
  50. package/build/src/flow-kit/validate.js +75 -0
  51. package/build/src/lib/args.js +45 -0
  52. package/build/src/lib/fs.js +62 -0
  53. package/build/src/lib/workflow-learning-projection.js +334 -0
  54. package/build/src/runtime-adapters.js +146 -0
  55. package/build/src/tools/build-universal-bundles.js +397 -0
  56. package/build/src/tools/common.js +56 -0
  57. package/build/src/tools/filter-installed-packs.js +132 -0
  58. package/build/src/tools/generate-context-map.js +198 -0
  59. package/build/src/tools/validate-package.js +64 -0
  60. package/build/src/tools/validate-source-tree.js +622 -0
  61. package/console.telemetry.json +176 -0
  62. package/context/base-rules.md +17 -0
  63. package/context/code-review-standards.md +62 -0
  64. package/context/coding-standards.md +42 -0
  65. package/context/common/orchestrators.md +12 -0
  66. package/context/common/subagents.md +28 -0
  67. package/context/contracts/artifact-contract.md +182 -0
  68. package/context/contracts/builder-kit-workflow-state-contract.md +319 -0
  69. package/context/contracts/delivery-contract.md +69 -0
  70. package/context/contracts/execution-contract.md +53 -0
  71. package/context/contracts/governance-adapter-contract.md +67 -0
  72. package/context/contracts/planning-contract.md +85 -0
  73. package/context/contracts/review-contract.md +104 -0
  74. package/context/contracts/sandbox-policy.md +52 -0
  75. package/context/contracts/verification-contract.md +134 -0
  76. package/context/contracts/work-item-contract.md +215 -0
  77. package/context/deferred/demo-mode.md +33 -0
  78. package/context/deferred/languages/go.md +31 -0
  79. package/context/deferred/languages/python.md +31 -0
  80. package/context/deferred/languages/typescript.md +34 -0
  81. package/context/deferred/parallelization.md +35 -0
  82. package/context/deferred/worktree-isolation.md +24 -0
  83. package/context/development-workflow.md +50 -0
  84. package/context/scripts/context-budget/budget-scan.sh +166 -0
  85. package/context/scripts/detect-tools.sh +3 -0
  86. package/context/scripts/discover-agents.sh +28 -0
  87. package/context/scripts/git-status.sh +49 -0
  88. package/context/scripts/hooks/config-protection.js +79 -0
  89. package/context/scripts/hooks/desktop-notify.sh +39 -0
  90. package/context/scripts/hooks/governance-audit.sh +135 -0
  91. package/context/scripts/hooks/lib/audit-transport.sh +40 -0
  92. package/context/scripts/hooks/lib/hook-flags.js +49 -0
  93. package/context/scripts/hooks/lib/patterns.sh +57 -0
  94. package/context/scripts/hooks/lib/resolve-formatter.js +80 -0
  95. package/context/scripts/hooks/post-edit-accumulator.js +66 -0
  96. package/context/scripts/hooks/pre-commit-quality.js +194 -0
  97. package/context/scripts/hooks/quality-gate.js +93 -0
  98. package/context/scripts/hooks/report-only-guard.js +21 -0
  99. package/context/scripts/hooks/run-hook.js +136 -0
  100. package/context/scripts/hooks/stop-format-typecheck.js +141 -0
  101. package/context/scripts/hooks/stop-goal-fit.js +337 -0
  102. package/context/scripts/hooks/workflow-steering.js +250 -0
  103. package/context/scripts/telemetry/console-presets.sh +14 -0
  104. package/context/scripts/telemetry/install-console-config.sh +214 -0
  105. package/context/scripts/telemetry/lib/config.sh +85 -0
  106. package/context/scripts/telemetry/lib/enrich.sh +115 -0
  107. package/context/scripts/telemetry/lib/redact.sh +22 -0
  108. package/context/scripts/telemetry/lib/session.sh +63 -0
  109. package/context/scripts/telemetry/lib/transport.sh +183 -0
  110. package/context/scripts/telemetry/lib/usage.sh +29 -0
  111. package/context/scripts/telemetry/sync-agents.sh +173 -0
  112. package/context/scripts/telemetry/telemetry.conf +23 -0
  113. package/context/scripts/telemetry/telemetry.sh +387 -0
  114. package/context/scripts/validate-package.sh +89 -0
  115. package/context/settings/backlog-provider-settings.json +54 -0
  116. package/context/templates/core/identity.md +26 -0
  117. package/context/templates/core/user.md +15 -0
  118. package/docs/_config.yml +15 -0
  119. package/docs/_layouts/default.html +87 -0
  120. package/docs/adr/0001-flow-agents-consumes-flow.md +77 -0
  121. package/docs/adr/0002-flow-kits-as-extension-unit.md +13 -0
  122. package/docs/adr/0003-flow-agents-coordinates-kits-and-adapters.md +13 -0
  123. package/docs/adr/0004-gates-expect-surface-claims.md +15 -0
  124. package/docs/adr/0005-kubernetes-inspired-resource-contracts.md +48 -0
  125. package/docs/adr/0006-typescript-first-source-policy.md +98 -0
  126. package/docs/agent-system-guidebook.md +391 -0
  127. package/docs/agent-usage-feedback-loop.md +351 -0
  128. package/docs/assets/favicon.svg +13 -0
  129. package/docs/assets/og-image.png +0 -0
  130. package/docs/assets/site.css +774 -0
  131. package/docs/assets/site.js +139 -0
  132. package/docs/configurable-workflow-routing.md +174 -0
  133. package/docs/context-map.md +145 -0
  134. package/docs/developer-architecture.md +145 -0
  135. package/docs/developer-hook-setup.md +61 -0
  136. package/docs/fixture-ownership.md +44 -0
  137. package/docs/flow-kit-repository-contract.md +180 -0
  138. package/docs/index.md +129 -0
  139. package/docs/kontour-resource-contract.md +358 -0
  140. package/docs/migrations.md +64 -0
  141. package/docs/north-star.md +322 -0
  142. package/docs/operating-layers.md +110 -0
  143. package/docs/repository-structure.md +132 -0
  144. package/docs/sandbox-policy.md +56 -0
  145. package/docs/skills-map.md +203 -0
  146. package/docs/standards-register.md +96 -0
  147. package/docs/veritas-integration.md +165 -0
  148. package/docs/work-item-adapters.md +72 -0
  149. package/docs/workflow-artifact-lifecycle.md +141 -0
  150. package/docs/workflow-eval-strategy.md +295 -0
  151. package/docs/workflow-shared-contracts.md +51 -0
  152. package/docs/workflow-usage-guide.md +443 -0
  153. package/evals/ARCHITECTURE.md +143 -0
  154. package/evals/CONVENTIONS.md +58 -0
  155. package/evals/README.md +128 -0
  156. package/evals/acceptance/run.sh +29 -0
  157. package/evals/acceptance/test_claude_harness.sh +242 -0
  158. package/evals/acceptance/test_codex_harness.sh +108 -0
  159. package/evals/acceptance/test_kiro_harness.sh +128 -0
  160. package/evals/cases/dev/404.html +97 -0
  161. package/evals/cases/dev/code-review.yaml +44 -0
  162. package/evals/cases/dev/dashboard.html +300 -0
  163. package/evals/cases/dev/deliver.yaml +66 -0
  164. package/evals/cases/dev/dependency-update.yaml +16 -0
  165. package/evals/cases/dev/explore.yaml +20 -0
  166. package/evals/cases/dev/index.html +370 -0
  167. package/evals/cases/dev/package-lock.json +28 -0
  168. package/evals/cases/dev/package.json +16 -0
  169. package/evals/cases/dev/plan-work.yaml +20 -0
  170. package/evals/cases/dev/promptfooconfig.yaml +666 -0
  171. package/evals/cases/dev/search-first.yaml +20 -0
  172. package/evals/cases/dev/tdd-workflow.yaml +48 -0
  173. package/evals/cases/dev/verify-work.yaml +44 -0
  174. package/evals/cases/dev/workflow.yaml +34 -0
  175. package/evals/ci/run-baseline.sh +283 -0
  176. package/evals/fixtures/backlog-provider-settings/global-default.json +44 -0
  177. package/evals/fixtures/backlog-provider-settings/project-override.json +53 -0
  178. package/evals/fixtures/builder-kit-workflow-state/baseline-freshness-resolution-hint.json +139 -0
  179. package/evals/fixtures/builder-kit-workflow-state/direct-primitive-stop.json +59 -0
  180. package/evals/fixtures/builder-kit-workflow-state/empty-board-route-shape.json +55 -0
  181. package/evals/fixtures/builder-kit-workflow-state/happy-path.json +71 -0
  182. package/evals/fixtures/builder-kit-workflow-state/mid-work-resume.json +80 -0
  183. package/evals/fixtures/builder-kit-workflow-state/missing-prestep-recovery.json +65 -0
  184. package/evals/fixtures/builder-kit-workflow-state/product-build-chaining.json +60 -0
  185. package/evals/fixtures/builder-kit-workflow-state/stale-continuation-requires-new-probe.json +57 -0
  186. package/evals/fixtures/console-learning-projection/artifacts/console-learning-correction/learning.json +50 -0
  187. package/evals/fixtures/console-learning-projection/artifacts/console-learning-open-route/learning.json +41 -0
  188. package/evals/fixtures/flow-kit-repository/invalid-absolute-path/kit.json +8 -0
  189. package/evals/fixtures/flow-kit-repository/invalid-asset-section/flows/review.flow.json +6 -0
  190. package/evals/fixtures/flow-kit-repository/invalid-asset-section/kit.json +11 -0
  191. package/evals/fixtures/flow-kit-repository/invalid-duplicate-flow/flows/review.flow.json +6 -0
  192. package/evals/fixtures/flow-kit-repository/invalid-duplicate-flow/kit.json +9 -0
  193. package/evals/fixtures/flow-kit-repository/invalid-id/flows/review.flow.json +6 -0
  194. package/evals/fixtures/flow-kit-repository/invalid-id/kit.json +8 -0
  195. package/evals/fixtures/flow-kit-repository/invalid-malformed-json/kit.json +8 -0
  196. package/evals/fixtures/flow-kit-repository/invalid-missing-flow/kit.json +8 -0
  197. package/evals/fixtures/flow-kit-repository/invalid-missing-id/flows/review.flow.json +6 -0
  198. package/evals/fixtures/flow-kit-repository/invalid-missing-id/kit.json +7 -0
  199. package/evals/fixtures/flow-kit-repository/invalid-missing-schema-version/flows/review.flow.json +6 -0
  200. package/evals/fixtures/flow-kit-repository/invalid-missing-schema-version/kit.json +7 -0
  201. package/evals/fixtures/flow-kit-repository/invalid-name/flows/review.flow.json +6 -0
  202. package/evals/fixtures/flow-kit-repository/invalid-name/kit.json +8 -0
  203. package/evals/fixtures/flow-kit-repository/invalid-schema-version/flows/review.flow.json +6 -0
  204. package/evals/fixtures/flow-kit-repository/invalid-schema-version/kit.json +8 -0
  205. package/evals/fixtures/flow-kit-repository/invalid-traversal/kit.json +8 -0
  206. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/adapters/example.json +3 -0
  207. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/assets/example.txt +1 -0
  208. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/docs/README.md +3 -0
  209. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/flows/runtime.flow.json +26 -0
  210. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/kit-evals/example.json +3 -0
  211. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/kit-skills/mixed/SKILL.md +3 -0
  212. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/kit.json +44 -0
  213. package/evals/fixtures/flow-kit-repository/valid-local-kit/docs/README.md +3 -0
  214. package/evals/fixtures/flow-kit-repository/valid-local-kit/flows/review.flow.json +26 -0
  215. package/evals/fixtures/flow-kit-repository/valid-local-kit/kit.json +20 -0
  216. package/evals/fixtures/hook-influence/cases.json +336 -0
  217. package/evals/fixtures/pull-work-provider/github-issues.json +170 -0
  218. package/evals/fixtures/pull-work-wip-shepherding/global-wip-informs.json +43 -0
  219. package/evals/fixtures/pull-work-wip-shepherding/personal-wip-blocks.json +42 -0
  220. package/evals/fixtures/surface-trust/accepted-claim-trust-report.json +31 -0
  221. package/evals/fixtures/surface-trust/artifact-absent.json +19 -0
  222. package/evals/fixtures/surface-trust/integrity-mismatch-trust-report.json +32 -0
  223. package/evals/fixtures/surface-trust/missing-authority-trust-report.json +27 -0
  224. package/evals/fixtures/surface-trust/provider-absent.json +19 -0
  225. package/evals/fixtures/surface-trust/rejected-claim-trust-report.json +30 -0
  226. package/evals/fixtures/surface-trust/stale-claim-trust-snapshot.json +31 -0
  227. package/evals/fixtures/usage-feedback/sample-full.jsonl +11 -0
  228. package/evals/fixtures/usage-feedback/sample-outcomes.jsonl +1 -0
  229. package/evals/fixtures/veritas-governance-adapter/fake-veritas-pass.sh +18 -0
  230. package/evals/fixtures/veritas-governance-adapter/fake-veritas-secret-fail.sh +10 -0
  231. package/evals/fixtures/veritas-governance-adapter/fake-veritas-unconfigured.sh +4 -0
  232. package/evals/integration/test_bundle_install.sh +541 -0
  233. package/evals/integration/test_console_learning_projection.sh +192 -0
  234. package/evals/integration/test_context_map.sh +65 -0
  235. package/evals/integration/test_effective_backlog_settings.sh +58 -0
  236. package/evals/integration/test_fixture_retirement_audit.sh +58 -0
  237. package/evals/integration/test_flow_agents_statusline.sh +93 -0
  238. package/evals/integration/test_flow_kit_repository.sh +90 -0
  239. package/evals/integration/test_goal_fit_hook.sh +482 -0
  240. package/evals/integration/test_hook_category_behaviors.sh +190 -0
  241. package/evals/integration/test_hook_influence_cases.sh +69 -0
  242. package/evals/integration/test_local_flow_kit_install.sh +145 -0
  243. package/evals/integration/test_publish_change_helper.sh +176 -0
  244. package/evals/integration/test_pull_work_provider.sh +140 -0
  245. package/evals/integration/test_runtime_adapter_activation.sh +106 -0
  246. package/evals/integration/test_telemetry.sh +485 -0
  247. package/evals/integration/test_telemetry_doctor.sh +193 -0
  248. package/evals/integration/test_usage_feedback_dashboard.sh +169 -0
  249. package/evals/integration/test_usage_feedback_global.sh +117 -0
  250. package/evals/integration/test_usage_feedback_import.sh +227 -0
  251. package/evals/integration/test_usage_feedback_outcomes.sh +165 -0
  252. package/evals/integration/test_usage_feedback_report.sh +263 -0
  253. package/evals/integration/test_veritas_governance_adapter.sh +235 -0
  254. package/evals/integration/test_workflow_artifact_cleanup_audit.sh +287 -0
  255. package/evals/integration/test_workflow_artifacts.sh +1247 -0
  256. package/evals/integration/test_workflow_sidecar_writer.sh +2112 -0
  257. package/evals/integration/test_workflow_steering_hook.sh +337 -0
  258. package/evals/lib/assertions/delegated-to.js +40 -0
  259. package/evals/lib/assertions/max-tool-calls.js +15 -0
  260. package/evals/lib/assertions/no-write-tools.js +27 -0
  261. package/evals/lib/assertions/pass-at-k.js +39 -0
  262. package/evals/lib/assertions/telemetry-utils.js +105 -0
  263. package/evals/lib/assertions/tool-called.js +39 -0
  264. package/evals/lib/assertions/verify-after-fix.js +61 -0
  265. package/evals/lib/claude-judge.sh +40 -0
  266. package/evals/lib/claude-provider.sh +74 -0
  267. package/evals/lib/codex-judge.sh +39 -0
  268. package/evals/lib/codex-provider.sh +81 -0
  269. package/evals/lib/eval-dev.sh +5 -0
  270. package/evals/lib/eval-judge.sh +22 -0
  271. package/evals/lib/eval-provider.sh +26 -0
  272. package/evals/lib/eval-report.sh +73 -0
  273. package/evals/lib/kiro-dev.sh +4 -0
  274. package/evals/lib/kiro-judge.sh +17 -0
  275. package/evals/lib/kiro-provider.sh +62 -0
  276. package/evals/lib/node.sh +111 -0
  277. package/evals/promptfooconfig.yaml +70 -0
  278. package/evals/run.sh +309 -0
  279. package/evals/static/test_evidence_refs.sh +141 -0
  280. package/evals/static/test_package.sh +407 -0
  281. package/evals/static/test_repo_hooks.sh +68 -0
  282. package/evals/static/test_universal_bundles.sh +274 -0
  283. package/evals/static/test_workflow_skills.sh +1207 -0
  284. package/install.sh +64 -0
  285. package/integrations/veritas/flow-agents.adapter.json +138 -0
  286. package/integrations/veritas/flow-agents.authority-settings.json +26 -0
  287. package/integrations/veritas/flow-agents.repo-standards.json +82 -0
  288. package/kits/builder/flows/build.flow.json +218 -0
  289. package/kits/builder/flows/shape.flow.json +127 -0
  290. package/kits/builder/kit.json +19 -0
  291. package/kits/catalog.json +11 -0
  292. package/package.json +130 -0
  293. package/packaging/README.md +60 -0
  294. package/packaging/manifest.json +173 -0
  295. package/packaging/packs.json +69 -0
  296. package/powers/dependency-checker/POWER.md +20 -0
  297. package/powers/dependency-checker/mcp.json +20 -0
  298. package/powers/playwright/POWER.md +25 -0
  299. package/powers/playwright/mcp.json +12 -0
  300. package/prompts/code-audit.md +123 -0
  301. package/prompts/kcommit.md +88 -0
  302. package/schemas/backlog-provider-settings.schema.json +138 -0
  303. package/schemas/workflow-acceptance.schema.json +216 -0
  304. package/schemas/workflow-critique.schema.json +113 -0
  305. package/schemas/workflow-evidence.schema.json +357 -0
  306. package/schemas/workflow-handoff.schema.json +52 -0
  307. package/schemas/workflow-learning.schema.json +223 -0
  308. package/schemas/workflow-release.schema.json +172 -0
  309. package/schemas/workflow-state.schema.json +80 -0
  310. package/scripts/README.md +111 -0
  311. package/scripts/build-universal-bundles.js +3 -0
  312. package/scripts/check-content-boundary.cjs +99 -0
  313. package/scripts/context-budget/budget-scan.sh +166 -0
  314. package/scripts/detect-tools.sh +3 -0
  315. package/scripts/discover-agents.sh +28 -0
  316. package/scripts/effective-backlog-settings.js +2 -0
  317. package/scripts/filter-installed-packs.js +2 -0
  318. package/scripts/flow-kit.js +2 -0
  319. package/scripts/generate-context-map.js +2 -0
  320. package/scripts/git-status.sh +49 -0
  321. package/scripts/hooks/claude-hook-adapter.js +174 -0
  322. package/scripts/hooks/claude-telemetry-hook.js +115 -0
  323. package/scripts/hooks/codex-hook-adapter.js +176 -0
  324. package/scripts/hooks/codex-telemetry-hook.js +95 -0
  325. package/scripts/hooks/config-protection.js +79 -0
  326. package/scripts/hooks/desktop-notify.sh +39 -0
  327. package/scripts/hooks/governance-audit.sh +135 -0
  328. package/scripts/hooks/lib/audit-transport.sh +40 -0
  329. package/scripts/hooks/lib/hook-flags.js +49 -0
  330. package/scripts/hooks/lib/patterns.sh +57 -0
  331. package/scripts/hooks/lib/resolve-formatter.js +80 -0
  332. package/scripts/hooks/post-edit-accumulator.js +66 -0
  333. package/scripts/hooks/pre-commit-quality.js +194 -0
  334. package/scripts/hooks/quality-gate.js +93 -0
  335. package/scripts/hooks/report-only-guard.js +21 -0
  336. package/scripts/hooks/run-hook.js +136 -0
  337. package/scripts/hooks/stop-format-typecheck.js +141 -0
  338. package/scripts/hooks/stop-goal-fit.js +337 -0
  339. package/scripts/hooks/workflow-steering.js +250 -0
  340. package/scripts/install-codex-home.sh +106 -0
  341. package/scripts/package.json +3 -0
  342. package/scripts/promote-workflow-artifact.js +2 -0
  343. package/scripts/publish-change-helper.js +2 -0
  344. package/scripts/pull-work-provider.js +2 -0
  345. package/scripts/setup-repo-hooks.sh +8 -0
  346. package/scripts/statusline/flow-agents-statusline.js +157 -0
  347. package/scripts/telemetry/console-presets.sh +14 -0
  348. package/scripts/telemetry/install-console-config.sh +214 -0
  349. package/scripts/telemetry/lib/config.sh +85 -0
  350. package/scripts/telemetry/lib/enrich.sh +115 -0
  351. package/scripts/telemetry/lib/redact.sh +22 -0
  352. package/scripts/telemetry/lib/session.sh +63 -0
  353. package/scripts/telemetry/lib/transport.sh +183 -0
  354. package/scripts/telemetry/lib/usage.sh +29 -0
  355. package/scripts/telemetry/sync-agents.sh +173 -0
  356. package/scripts/telemetry/telemetry.conf +23 -0
  357. package/scripts/telemetry/telemetry.sh +387 -0
  358. package/scripts/usage-feedback.js +2 -0
  359. package/scripts/validate-hook-influence-cases.js +2 -0
  360. package/scripts/validate-package.sh +89 -0
  361. package/scripts/validate-source-tree.js +9 -0
  362. package/skills/agentic-engineering/SKILL.md +62 -0
  363. package/skills/browser-test/SKILL.md +51 -0
  364. package/skills/builder-shape/SKILL.md +76 -0
  365. package/skills/context-budget/SKILL.md +40 -0
  366. package/skills/deliver/SKILL.md +241 -0
  367. package/skills/dependency-update/SKILL.md +68 -0
  368. package/skills/design-probe/SKILL.md +107 -0
  369. package/skills/eval-rebuild/SKILL.md +39 -0
  370. package/skills/evidence-gate/SKILL.md +186 -0
  371. package/skills/execute-plan/SKILL.md +110 -0
  372. package/skills/explore/SKILL.md +137 -0
  373. package/skills/feedback-loop/SKILL.md +87 -0
  374. package/skills/fix-bug/SKILL.md +133 -0
  375. package/skills/frontend-design/SKILL.md +80 -0
  376. package/skills/github-cli/SKILL.md +63 -0
  377. package/skills/idea-to-backlog/SKILL.md +267 -0
  378. package/skills/knowledge-capture/SKILL.md +55 -0
  379. package/skills/learning-review/SKILL.md +115 -0
  380. package/skills/pickup-probe/SKILL.md +114 -0
  381. package/skills/plan-work/SKILL.md +176 -0
  382. package/skills/pull-work/SKILL.md +309 -0
  383. package/skills/release-readiness/SKILL.md +121 -0
  384. package/skills/review-work/SKILL.md +161 -0
  385. package/skills/search-first/SKILL.md +66 -0
  386. package/skills/tdd-workflow/SKILL.md +140 -0
  387. package/skills/verify-work/SKILL.md +109 -0
  388. package/src/cli/console-learning-projection.ts +140 -0
  389. package/src/cli/effective-backlog-settings.ts +99 -0
  390. package/src/cli/fixture-retirement-audit.ts +154 -0
  391. package/src/cli/flow-kit.ts +139 -0
  392. package/src/cli/init.ts +248 -0
  393. package/src/cli/promote-workflow-artifact.ts +64 -0
  394. package/src/cli/publish-change-helper.ts +143 -0
  395. package/src/cli/pull-work-provider.ts +481 -0
  396. package/src/cli/runtime-adapter.ts +24 -0
  397. package/src/cli/telemetry-doctor.ts +243 -0
  398. package/src/cli/usage-feedback.ts +418 -0
  399. package/src/cli/validate-hook-influence.ts +119 -0
  400. package/src/cli/validate-source-tree.ts +30 -0
  401. package/src/cli/validate-workflow-artifacts.ts +411 -0
  402. package/src/cli/veritas-governance.ts +322 -0
  403. package/src/cli/workflow-artifact-cleanup-audit.ts +281 -0
  404. package/src/cli/workflow-sidecar.ts +676 -0
  405. package/src/cli.ts +95 -0
  406. package/src/flow-kit/validate.ts +74 -0
  407. package/src/lib/args.ts +43 -0
  408. package/src/lib/fs.ts +62 -0
  409. package/src/lib/workflow-learning-projection.ts +491 -0
  410. package/src/runtime-adapters.ts +154 -0
  411. package/src/tools/build-universal-bundles.ts +366 -0
  412. package/src/tools/common.ts +61 -0
  413. package/src/tools/filter-installed-packs.ts +129 -0
  414. package/src/tools/generate-context-map.ts +199 -0
  415. package/src/tools/validate-package.ts +57 -0
  416. package/src/tools/validate-source-tree.ts +488 -0
  417. package/tsconfig.json +19 -0
  418. package/veritas.claims.json +6 -0
@@ -0,0 +1,119 @@
1
+ import * as fs from "node:fs";
2
+
3
+ const validTiers = new Set(["adapter", "design-target", "installed-command", "live-acceptance", "documented-runtime-gap"]);
4
+ const validRuntimes = new Set(["codex", "claude-code", "kiro-cli"]);
5
+ const validHooks = new Set(["workflow-steering", "stop-goal-fit"]);
6
+ const validEvents = new Set(["UserPromptSubmit", "PostToolUse", "Stop"]);
7
+ const requiredCases: Record<string, { description: string; tier: string; hook?: string; event?: string; must_include?: string[]; agent_must_do?: string[] }> = {
8
+ "dev-builder-build-requires-pickup-probe-before-plan": { description: "missing pickup Probe before plan", tier: "design-target", must_include: ["design-probe", "accepted gaps", "provider_state", "conflict_risks"], agent_must_do: ["route decision_gap back to design-probe/pickup Probe"] },
9
+ "dev-builder-review-before-verify-after-execute": { description: "review-before-verify after execute", tier: "adapter", event: "PostToolUse", must_include: ["Next: review", "then verify", "report only"], agent_must_do: ["review-work for report-only critique before verify-work", "not count critique.json as verification evidence"] },
10
+ "dev-verify-fail-preserves-trace-before-rework": { description: "verify failure route-back with preserved FAIL evidence", tier: "design-target", must_include: ["If verdict=FAIL: record the FAIL artifact"], agent_must_do: ["write the FAIL artifact before routing back", "route to plan"] },
11
+ "codex-claude-strict-stop-adapter-contract": { description: "Goal Fit stop adapter behavior", tier: "adapter", hook: "stop-goal-fit", event: "Stop", must_include: ["Goal Fit warning"], agent_must_do: ["treat strict Stop guidance as a blocker"] },
12
+ "codex-live-context-gap": { description: "Codex documented runtime gap", tier: "documented-runtime-gap", must_include: ["STATE:", "not_verified"], agent_must_do: ["record NOT_VERIFIED for live influence"] },
13
+ "installed-command-protocol-guidance": { description: "installed-command evidence lane", tier: "installed-command", must_include: ["STATE:", "CRITIQUE:", "CONTEXT MAP:"], agent_must_do: ["treat hook guidance as additional context"] },
14
+ };
15
+
16
+ function fail(message: string): never {
17
+ throw new Error(message);
18
+ }
19
+
20
+ function stringList(caseId: string, payload: Record<string, unknown>, key: string, minimum = 1): string[] {
21
+ const value = payload[key];
22
+ if (!Array.isArray(value) || value.length < minimum) fail(`${caseId}: ${key} must contain at least ${minimum} item(s)`);
23
+ value.forEach((item, index) => {
24
+ if (typeof item !== "string" || !item.trim()) fail(`${caseId}: ${key}[${index}] must be a non-empty string`);
25
+ });
26
+ return value as string[];
27
+ }
28
+
29
+ function commandTarget(command: string): string | undefined {
30
+ const parts = command.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map((part) => part.replace(/^['"]|['"]$/g, "")) ?? [];
31
+ for (let i = 0; i < parts.length; i += 1) {
32
+ if (parts[i] === "bash" && parts[i + 1]) return parts[i + 1];
33
+ if (parts[i].startsWith("evals/") || parts[i].startsWith("scripts/")) return parts[i];
34
+ }
35
+ return undefined;
36
+ }
37
+
38
+ function validateEvidenceCommand(caseId: string, runtimes: string[], evidence: Record<string, unknown>): void {
39
+ const command = String(evidence.command);
40
+ const tier = String(evidence.tier);
41
+ const target = commandTarget(command);
42
+ if (!target) fail(`${caseId}: evidence.command must reference a local eval or script path`);
43
+ if (!fs.existsSync(target)) fail(`${caseId}: evidence.command target does not exist: ${target}`);
44
+ const runtimeSet = new Set(runtimes);
45
+ if (tier === "live-acceptance") {
46
+ if (target.includes("evals/acceptance/test_kiro_harness.sh") && (runtimeSet.size !== 1 || !runtimeSet.has("kiro-cli"))) fail(`${caseId}: Kiro live acceptance cannot prove runtimes: ${runtimes.sort().join(", ")}`);
47
+ if (target.includes("evals/acceptance/test_claude_harness.sh") && (runtimeSet.size !== 1 || !runtimeSet.has("claude-code"))) fail(`${caseId}: Claude live acceptance cannot prove runtimes: ${runtimes.sort().join(", ")}`);
48
+ if (target.includes("evals/acceptance/test_codex_harness.sh") && (runtimeSet.size !== 1 || !runtimeSet.has("codex"))) fail(`${caseId}: Codex live acceptance cannot prove runtimes: ${runtimes.sort().join(", ")}`);
49
+ if (!target.includes("evals/acceptance/") && !target.includes("/acceptance/")) fail(`${caseId}: live-acceptance evidence must point to an acceptance harness`);
50
+ } else if (tier === "installed-command" && !target.includes("evals/integration/test_bundle_install.sh")) fail(`${caseId}: installed-command evidence must point to bundle install integration coverage`);
51
+ else if (tier === "adapter" && !target.includes("evals/integration/")) fail(`${caseId}: adapter evidence must point to an integration adapter test`);
52
+ else if (tier === "documented-runtime-gap" && !target.includes("acceptance") && !target.includes("integration")) fail(`${caseId}: documented runtime gaps must still point to an eval boundary`);
53
+ }
54
+
55
+ export function main(argv = process.argv.slice(2)): number {
56
+ try {
57
+ const file = argv[0] ?? "evals/fixtures/hook-influence/cases.json";
58
+ const data = JSON.parse(fs.readFileSync(file, "utf8")) as Record<string, unknown>;
59
+ if (data.schema_version !== "1.0") fail("schema_version must be 1.0");
60
+ if (!Array.isArray(data.cases) || !data.cases.length) fail("cases must be a non-empty list");
61
+ const ids = new Set<string>();
62
+ const casesById = new Map<string, Record<string, unknown>>();
63
+ const byTier = new Set<string>();
64
+ const runtimeCoverage = new Set<string>();
65
+ data.cases.forEach((raw, index) => {
66
+ if (typeof raw !== "object" || raw === null) fail(`cases[${index}] must be an object`);
67
+ const item = raw as Record<string, unknown>;
68
+ const caseId = typeof item.id === "string" ? item.id : "";
69
+ if (!caseId.trim()) fail(`cases[${index}]: id must be a non-empty string`);
70
+ if (ids.has(caseId)) fail(`${caseId}: duplicate id`);
71
+ ids.add(caseId);
72
+ casesById.set(caseId, item);
73
+ const runtimes = stringList(caseId, item, "runtime_scope");
74
+ const unknownRuntimes = runtimes.filter((runtime) => !validRuntimes.has(runtime)).sort();
75
+ if (unknownRuntimes.length) fail(`${caseId}: unknown runtime_scope value(s): ${unknownRuntimes.join(", ")}`);
76
+ runtimes.forEach((runtime) => runtimeCoverage.add(runtime));
77
+ if (!validHooks.has(String(item.hook))) fail(`${caseId}: hook must be one of ${[...validHooks].sort().join(", ")}`);
78
+ if (!validEvents.has(String(item.event))) fail(`${caseId}: event must be one of ${[...validEvents].sort().join(", ")}`);
79
+ if (typeof item.fixture_state !== "object" || item.fixture_state === null || !Object.keys(item.fixture_state).length) fail(`${caseId}: fixture_state must be a non-empty object`);
80
+ const guidance = stringList(caseId, item, "guidance_must_include", 2);
81
+ const actions = stringList(caseId, item, "agent_must_do", 2);
82
+ if (typeof item.evidence !== "object" || item.evidence === null) fail(`${caseId}: evidence must be an object`);
83
+ const evidence = item.evidence as Record<string, unknown>;
84
+ const tier = String(evidence.tier);
85
+ if (!validTiers.has(tier)) fail(`${caseId}: evidence.tier must be one of ${[...validTiers].sort().join(", ")}`);
86
+ byTier.add(tier);
87
+ ["command", "status"].forEach((key) => { if (typeof evidence[key] !== "string" || !String(evidence[key]).trim()) fail(`${caseId}: evidence.${key} must be a non-empty string`); });
88
+ validateEvidenceCommand(caseId, runtimes, evidence);
89
+ const req = requiredCases[caseId];
90
+ if (req) {
91
+ if (tier !== req.tier) fail(`${caseId}: ${req.description} must use ${req.tier} evidence tier`);
92
+ if (req.hook && item.hook !== req.hook) fail(`${caseId}: ${req.description} must use ${req.hook} hook`);
93
+ if (req.event && item.event !== req.event) fail(`${caseId}: ${req.description} must use ${req.event} event`);
94
+ const guidanceText = guidance.join("\n");
95
+ const actionText = actions.join("\n");
96
+ const missingGuidance = (req.must_include ?? []).filter((needle) => !guidanceText.includes(needle));
97
+ const missingActions = (req.agent_must_do ?? []).filter((needle) => !actionText.includes(needle));
98
+ if (missingGuidance.length) fail(`${caseId}: missing required guidance text for ${req.description}: ${missingGuidance.join(", ")}`);
99
+ if (missingActions.length) fail(`${caseId}: missing required agent action for ${req.description}: ${missingActions.join(", ")}`);
100
+ }
101
+ });
102
+ const missingRuntimes = [...validRuntimes].filter((runtime) => !runtimeCoverage.has(runtime)).sort();
103
+ if (missingRuntimes.length) fail(`missing runtime coverage: ${missingRuntimes.join(", ")}`);
104
+ for (const tier of ["adapter", "installed-command", "live-acceptance", "documented-runtime-gap"]) if (!byTier.has(tier)) fail(`missing required #62 evidence tier(s): ${tier}`);
105
+ for (const id of Object.keys(requiredCases)) if (!casesById.has(id)) fail(`missing required #62 case for ${requiredCases[id].description}: ${id}`);
106
+ const codexGap = casesById.get("codex-live-context-gap")!;
107
+ const codexRuntimes = codexGap.runtime_scope as string[];
108
+ const codexEvidence = codexGap.evidence as Record<string, unknown>;
109
+ if (codexRuntimes.length !== 1 || codexRuntimes[0] !== "codex") fail("codex-live-context-gap must be scoped only to codex");
110
+ if (!String(codexEvidence.status).includes("acceptance-covers-routing-not-live-hook-influence")) fail("codex-live-context-gap must not overclaim live model-context influence");
111
+ console.log(`Hook influence cases valid: ${data.cases.length} case(s).`);
112
+ return 0;
113
+ } catch (error) {
114
+ console.error(error instanceof Error ? error.message : String(error));
115
+ return 1;
116
+ }
117
+ }
118
+
119
+ if (import.meta.url === `file://${process.argv[1]}`) process.exit(main());
@@ -0,0 +1,30 @@
1
+ import * as path from "node:path";
2
+ import { parseArgs } from "../lib/args.js";
3
+ import { validateKitRepository } from "../flow-kit/validate.js";
4
+
5
+ export function main(argv = process.argv.slice(2)): number {
6
+ const args = parseArgs(argv);
7
+ const kit = args.flags.kit;
8
+ if (typeof kit === "string") {
9
+ const errors = validateKitRepository(path.resolve(kit));
10
+ if (errors.length) {
11
+ console.log("Flow Kit repository validation failed:");
12
+ for (const error of errors) console.log(` - ${error}`);
13
+ return 1;
14
+ }
15
+ console.log("Flow Kit repository validation passed");
16
+ return 0;
17
+ }
18
+ const root = path.resolve(".");
19
+ const builder = path.join(root, "kits", "builder");
20
+ const errors = validateKitRepository(builder);
21
+ if (errors.length) {
22
+ console.log("Source tree validation failed:");
23
+ for (const error of errors) console.log(` - ${error}`);
24
+ return 1;
25
+ }
26
+ console.log("Source tree validation passed");
27
+ return 0;
28
+ }
29
+
30
+ if (import.meta.url === `file://${process.argv[1]}`) process.exit(main());
@@ -0,0 +1,411 @@
1
+ #!/usr/bin/env node
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+
5
+ type Issue = { path: string; message: string };
6
+
7
+ const root = path.resolve(".");
8
+ const statusRe = /\[(PASS|FAIL|NOT_VERIFIED|SKIP|PARTIAL)\]/i;
9
+ const dateTimeRe = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
10
+ const sidecarSchemas: Record<string, string> = {
11
+ "state.json": "schemas/workflow-state.schema.json",
12
+ "acceptance.json": "schemas/workflow-acceptance.schema.json",
13
+ "evidence.json": "schemas/workflow-evidence.schema.json",
14
+ "handoff.json": "schemas/workflow-handoff.schema.json",
15
+ "critique.json": "schemas/workflow-critique.schema.json",
16
+ "release.json": "schemas/workflow-release.schema.json",
17
+ "learning.json": "schemas/workflow-learning.schema.json",
18
+ };
19
+
20
+ function readText(file: string): string {
21
+ return fs.readFileSync(file, "utf8");
22
+ }
23
+
24
+ function section(text: string, heading: string): string {
25
+ const re = new RegExp(`^(?<marks>##+)\\s+${heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`, "m");
26
+ const match = re.exec(text);
27
+ if (!match?.groups) return "";
28
+ const level = match.groups.marks.length;
29
+ const start = match.index + match[0].length;
30
+ const rest = text.slice(start);
31
+ const next = new RegExp(`^#{2,${level}}\\s+`, "m").exec(rest);
32
+ return rest.slice(0, next ? next.index : undefined).trim();
33
+ }
34
+
35
+ function field(text: string, name: string): string {
36
+ const re = new RegExp(`^${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*(.+)$`, "m");
37
+ return re.exec(text)?.[1]?.trim() ?? "";
38
+ }
39
+
40
+ function frontmatterField(text: string, name: string): string {
41
+ if (!text.startsWith("---")) return "";
42
+ const end = text.indexOf("\n---", 3);
43
+ if (end < 0) return "";
44
+ return field(text.slice(0, end), name);
45
+ }
46
+
47
+ function checkboxes(text: string): string[] {
48
+ return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => /^-\s+\[[ xX]\]/.test(line));
49
+ }
50
+
51
+ function unchecked(text: string): string[] {
52
+ return checkboxes(text).filter((line) => /^-\s+\[\s\]/.test(line));
53
+ }
54
+
55
+ function statusLines(text: string): string[] {
56
+ return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => statusRe.test(line));
57
+ }
58
+
59
+ function acceptanceLines(text: string): string[] {
60
+ const body = section(text, "Acceptance Criteria") || section(text, "Definition Of Done");
61
+ return body.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.startsWith("-"));
62
+ }
63
+
64
+ function definitionAcceptanceCriteria(text: string): string[] {
65
+ const body = section(text, "Definition Of Done");
66
+ const found: string[] = [];
67
+ let inAcceptance = false;
68
+ for (const raw of body.split(/\r?\n/)) {
69
+ const line = raw.trim();
70
+ if (/^-\s+\*\*Acceptance criteria:\*\*/i.test(line)) {
71
+ inAcceptance = true;
72
+ continue;
73
+ }
74
+ if (inAcceptance && /^-\s+\*\*(Usefulness checks|Stop-short risks|Durable docs target|Scope|User outcome):\*\*/i.test(line)) break;
75
+ if (inAcceptance && /^-\s+\[[ xX]\]/.test(line)) found.push(line);
76
+ }
77
+ return found;
78
+ }
79
+
80
+ function hasEvidence(text: string): boolean {
81
+ return /\bEvidence:\s*\S|\bevidence\b.+\S/i.test(text);
82
+ }
83
+
84
+ function hasExplicitAcceptance(text: string): boolean {
85
+ return /explicitly accepted|accepted by user|user accepted|accepted gap/i.test(text);
86
+ }
87
+
88
+ function walk(dir: string): string[] {
89
+ const out: string[] = [];
90
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
91
+ const p = path.join(dir, entry.name);
92
+ if (entry.isDirectory()) out.push(...walk(p));
93
+ else out.push(p);
94
+ }
95
+ return out;
96
+ }
97
+
98
+ function artifactPaths(pathsIn: string[]): string[] {
99
+ const found = new Set<string>();
100
+ for (const p of pathsIn) {
101
+ if (fs.existsSync(p) && fs.statSync(p).isFile() && p.endsWith(".md")) found.add(path.resolve(p));
102
+ else if (fs.existsSync(p) && fs.statSync(p).isDirectory()) {
103
+ for (const f of walk(p)) if (f.endsWith(".md")) found.add(path.resolve(f));
104
+ }
105
+ }
106
+ return [...found].sort();
107
+ }
108
+
109
+ function sidecarPaths(pathsIn: string[]): string[] {
110
+ const names = new Set(Object.keys(sidecarSchemas));
111
+ const found = new Set<string>();
112
+ for (const p of pathsIn) {
113
+ if (fs.existsSync(p) && fs.statSync(p).isFile() && names.has(path.basename(p))) found.add(path.resolve(p));
114
+ else if (fs.existsSync(p) && fs.statSync(p).isDirectory()) {
115
+ for (const f of walk(p)) if (names.has(path.basename(f))) found.add(path.resolve(f));
116
+ }
117
+ }
118
+ return [...found].sort();
119
+ }
120
+
121
+ function schemaMatches(value: unknown, schema: any, rootSchema: any): boolean {
122
+ const issues: Issue[] = [];
123
+ validateSchemaValue("<schema-match>", value, schema, "<value>", issues, rootSchema);
124
+ return issues.length === 0;
125
+ }
126
+
127
+ function validateSchemaCondition(value: unknown, schema: any, rootSchema: any): boolean {
128
+ const issues: Issue[] = [];
129
+ validateSchemaValue("<schema-condition>", value, schema, "<value>", issues, rootSchema);
130
+ return issues.length === 0;
131
+ }
132
+
133
+ function resolveSchemaRef(ref: string, rootSchema: any): any {
134
+ if (!ref.startsWith("#/$defs/")) return undefined;
135
+ return rootSchema?.$defs?.[ref.slice("#/$defs/".length)];
136
+ }
137
+
138
+ function validateSchemaValue(file: string, value: unknown, schema: any, loc: string, issues: Issue[], rootSchema = schema): void {
139
+ if (schema.anyOf) {
140
+ if (!schema.anyOf.some((sub: any) => schemaMatches(value, sub, rootSchema))) {
141
+ issues.push({ path: file, message: `${loc} must match at least one allowed schema` });
142
+ }
143
+ }
144
+ if (schema.oneOf) {
145
+ const matches = schema.oneOf.filter((sub: any) => schemaMatches(value, sub, rootSchema)).length;
146
+ if (matches !== 1) issues.push({ path: file, message: `${loc} must match exactly one allowed schema` });
147
+ }
148
+ if (schema.$ref) {
149
+ const resolved = resolveSchemaRef(schema.$ref, rootSchema);
150
+ if (!resolved) issues.push({ path: file, message: `${loc} has unsupported schema ref ${schema.$ref}` });
151
+ else validateSchemaValue(file, value, resolved, loc, issues, rootSchema);
152
+ }
153
+ for (const sub of schema.allOf ?? []) validateSchemaValue(file, value, sub, loc, issues, rootSchema);
154
+ if (schema.if && schema.then && validateSchemaCondition(value, schema.if, rootSchema)) validateSchemaValue(file, value, schema.then, loc, issues, rootSchema);
155
+ if (schema.const !== undefined && value !== schema.const) issues.push({ path: file, message: `${loc} must be ${schema.const}` });
156
+ if (schema.enum && !schema.enum.includes(value)) issues.push({ path: file, message: `${loc} must be one of: ${schema.enum.join(", ")}` });
157
+ const t = schema.type;
158
+ if (!t && (schema.required || schema.properties)) {
159
+ if (!value || typeof value !== "object" || Array.isArray(value)) { issues.push({ path: file, message: `${loc} must be object` }); return; }
160
+ const obj = value as Record<string, unknown>;
161
+ for (const key of schema.required ?? []) if (!(key in obj)) issues.push({ path: file, message: `${loc}.${key} is required` });
162
+ for (const [key, sub] of Object.entries<any>(schema.properties ?? {})) if (key in obj) validateSchemaValue(file, obj[key], sub, `${loc}.${key}`, issues, rootSchema);
163
+ }
164
+ if (t === "string") {
165
+ if (typeof value !== "string") { issues.push({ path: file, message: `${loc} must be string` }); return; }
166
+ if (typeof schema.minLength === "number" && value.length < schema.minLength) issues.push({ path: file, message: `${loc} must not be empty` });
167
+ if (schema.format === "date-time") {
168
+ const d = Date.parse(value);
169
+ if (!dateTimeRe.test(value) || Number.isNaN(d)) issues.push({ path: file, message: `${loc} must be date-time` });
170
+ }
171
+ return;
172
+ }
173
+ if (t === "boolean" && typeof value !== "boolean") { issues.push({ path: file, message: `${loc} must be boolean` }); return; }
174
+ if (t === "integer") {
175
+ if (!Number.isInteger(value)) { issues.push({ path: file, message: `${loc} must be integer` }); return; }
176
+ if (typeof schema.minimum === "number" && (value as number) < schema.minimum) issues.push({ path: file, message: `${loc} must be at least ${schema.minimum}` });
177
+ return;
178
+ }
179
+ if (t === "number" && typeof value !== "number") { issues.push({ path: file, message: `${loc} must be number` }); return; }
180
+ if (t === "array") {
181
+ if (!Array.isArray(value)) { issues.push({ path: file, message: `${loc} must be array` }); return; }
182
+ if (typeof schema.minItems === "number" && value.length < schema.minItems) issues.push({ path: file, message: `${loc} must contain at least ${schema.minItems} item(s)` });
183
+ if (schema.uniqueItems && new Set(value.map((v) => JSON.stringify(v))).size !== value.length) issues.push({ path: file, message: `${loc} must contain unique items` });
184
+ if (schema.items) value.forEach((item, i) => validateSchemaValue(file, item, schema.items, `${loc}[${i}]`, issues, rootSchema));
185
+ return;
186
+ }
187
+ if (t === "object") {
188
+ if (!value || typeof value !== "object" || Array.isArray(value)) { issues.push({ path: file, message: `${loc} must be object` }); return; }
189
+ const obj = value as Record<string, unknown>;
190
+ for (const key of schema.required ?? []) if (!(key in obj)) issues.push({ path: file, message: `${loc}.${key} is required` });
191
+ const props = schema.properties ?? {};
192
+ if (schema.additionalProperties === false) {
193
+ for (const key of Object.keys(obj).filter((k) => !(k in props)).sort()) issues.push({ path: file, message: `${loc}.${key} is not allowed` });
194
+ }
195
+ for (const [key, sub] of Object.entries<any>(props)) if (key in obj) validateSchemaValue(file, obj[key], sub, `${loc}.${key}`, issues, rootSchema);
196
+ }
197
+ }
198
+
199
+ function artifactKind(file: string, text: string): string {
200
+ const base = path.basename(file);
201
+ return (frontmatterField(text, "role") || field(text, "type") || (base.endsWith("-plan.md") ? "plan" : base.endsWith("-review.md") ? "review" : base.includes("deliver") ? "deliver" : "artifact")).toLowerCase();
202
+ }
203
+
204
+ function requireSection(file: string, text: string, heading: string, issues: Issue[]): string {
205
+ const body = section(text, heading);
206
+ if (!body) issues.push({ path: file, message: `missing section: ## ${heading}` });
207
+ return body;
208
+ }
209
+
210
+ function validateDefinitionOfDone(file: string, text: string, issues: Issue[]): void {
211
+ const dod = requireSection(file, text, "Definition Of Done", issues);
212
+ if (!dod) return;
213
+ const modes = new Set(["local-read-only", "local-edit", "worktree", "container", "cloud-sandbox", "privileged-integration"]);
214
+ for (const [label, pattern] of [
215
+ ["User outcome", /User outcome/i],
216
+ ["Acceptance criteria", /Acceptance criteria/i],
217
+ ["Evidence", /Evidence:|\bevidence\b/i],
218
+ ["Stop-short risks", /Stop-short risks/i],
219
+ ["Durable docs target", /Durable docs target/i],
220
+ ["Sandbox mode", /Sandbox mode:\*\*/i],
221
+ ] as const) if (!pattern.test(dod)) issues.push({ path: file, message: `Definition Of Done missing ${label}` });
222
+ const match = /Sandbox mode:\*\*\s*`?([a-z-]+)`?/i.exec(dod);
223
+ if (match && !modes.has(match[1].toLowerCase())) issues.push({ path: file, message: `Definition Of Done has invalid Sandbox mode; expected one of: ${[...modes].sort().join(", ")}` });
224
+ }
225
+
226
+ function validateArtifact(file: string): Issue[] {
227
+ const issues: Issue[] = [];
228
+ const text = readText(file);
229
+ const kind = artifactKind(file, text);
230
+ if (kind === "plan") {
231
+ requireSection(file, text, "Plan", issues);
232
+ validateDefinitionOfDone(file, text, issues);
233
+ if (!/^###\s+Wave\s+\d+/m.test(text)) issues.push({ path: file, message: "plan artifact missing implementation waves" });
234
+ } else if (kind === "review") {
235
+ requireSection(file, text, "Verification Report", issues);
236
+ const verdict = frontmatterField(text, "verdict") || /###\s+Verdict:\s*(\w+)/i.exec(text)?.[1] || "";
237
+ if (!verdict) issues.push({ path: file, message: "review artifact missing verdict" });
238
+ if (/PASS/i.test(verdict) && statusLines(text).some((line) => /\[NOT_VERIFIED\]/i.test(line)) && !hasExplicitAcceptance(text)) issues.push({ path: file, message: "PASS review contains NOT_VERIFIED without explicit acceptance or routing" });
239
+ if (/PASS/i.test(verdict) && !hasEvidence(text)) issues.push({ path: file, message: "PASS review missing evidence" });
240
+ } else if (kind === "deliver") {
241
+ requireSection(file, text, "Plan", issues);
242
+ validateDefinitionOfDone(file, text, issues);
243
+ requireSection(file, text, "Verification Report", issues);
244
+ requireSection(file, text, "Goal Fit Gate", issues);
245
+ requireSection(file, text, "Final Acceptance", issues);
246
+ const delivered = /status:\s*(delivered|accepted|archived)/i.test(text);
247
+ if (delivered && unchecked(section(text, "Goal Fit Gate")).length) issues.push({ path: file, message: "Goal Fit Gate has unchecked items" });
248
+ if (delivered && unchecked(section(text, "Final Acceptance")).length) issues.push({ path: file, message: "Final Acceptance has unchecked items" });
249
+ const verification = section(text, "Verification Report");
250
+ if (/Verdict:\s*PASS/i.test(verification) && !acceptanceLines(verification).some((line) => /\[PASS\]/i.test(line) && hasEvidence(line))) issues.push({ path: file, message: "green build is not enough; acceptance evidence is missing" });
251
+ if (delivered && /NOT_VERIFIED/i.test(text) && !hasExplicitAcceptance(text)) issues.push({ path: file, message: "NOT_VERIFIED requires explicit acceptance or routing" });
252
+ if (statusLines(verification).length && !hasEvidence(verification)) issues.push({ path: file, message: "verification statuses require evidence" });
253
+ }
254
+ return issues;
255
+ }
256
+
257
+ function readJson(file: string): { value: any | undefined; issues: Issue[] } {
258
+ try { return { value: JSON.parse(readText(file)), issues: [] }; }
259
+ catch (error) { return { value: undefined, issues: [{ path: file, message: `invalid JSON: ${(error as Error).message}` }] }; }
260
+ }
261
+
262
+ function validateSidecar(file: string): Issue[] {
263
+ const { value, issues } = readJson(file);
264
+ if (value === undefined) return issues;
265
+ const schemaFile = sidecarSchemas[path.basename(file)];
266
+ if (schemaFile) {
267
+ const schema = JSON.parse(readText(path.join(root, schemaFile)));
268
+ validateSchemaValue(file, value, schema, path.basename(file), issues, schema);
269
+ }
270
+ if (path.basename(file) === "evidence.json") {
271
+ const checks = Array.isArray(value.checks) ? value.checks : [];
272
+ if (value.verdict === "pass" && checks.some((c: any) => c.status !== "pass" && c.status !== "skip")) issues.push({ path: file, message: "pass verdict requires all non-skipped checks to pass" });
273
+ if (value.verdict === "pass" && checks.length === 0) issues.push({ path: file, message: "checks must contain at least 1 item" });
274
+ }
275
+ if (path.basename(file) === "critique.json") {
276
+ const critiques = Array.isArray(value.critiques) ? value.critiques : [];
277
+ if (value.status === "pass" && critiques.some((c: any) => Array.isArray(c.findings) && c.findings.some((f: any) => f.status === "open"))) issues.push({ path: file, message: "critique pass cannot have open findings" });
278
+ if (value.required && value.status !== "pass") issues.push({ path: file, message: "required critique must pass" });
279
+ }
280
+ if (path.basename(file) === "learning.json") {
281
+ const records = Array.isArray(value.records) ? value.records : [];
282
+ if (value.status === "learned" && records.length === 0) issues.push({ path: file, message: "records must contain at least 1 item" });
283
+ if (value.status === "learned" && records.some((r: any) => Array.isArray(r.routing) && r.routing.some((x: any) => x.status === "open"))) issues.push({ path: file, message: "learning status learned cannot have open routing" });
284
+ validateLearningCorrections(file, value.status, records, issues);
285
+ }
286
+ if (path.basename(file) === "release.json") {
287
+ const gates = Array.isArray(value.gates) ? value.gates : [];
288
+ if (["merge", "release", "deploy"].includes(value.decision)) {
289
+ if (gates.some((g: any) => g.status !== "pass" && g.status !== "not_required")) issues.push({ path: file, message: "positive release decision requires all required gates to pass" });
290
+ if (!gates.some((g: any) => g.name === value.decision && g.status === "pass")) issues.push({ path: file, message: `positive release decision requires ${value.decision} gate to pass` });
291
+ }
292
+ if (value.decision === "deploy") {
293
+ if (value.rollback_plan?.status !== "ready") issues.push({ path: file, message: "deploy decision requires rollback_plan status ready" });
294
+ if (value.observability_plan?.status !== "ready") issues.push({ path: file, message: "deploy decision requires observability_plan status ready" });
295
+ if (!Array.isArray(value.post_deploy_checks) || value.post_deploy_checks.length === 0) issues.push({ path: file, message: "deploy decision requires post_deploy_checks" });
296
+ if (Array.isArray(value.post_deploy_checks) && value.post_deploy_checks.some((c: any) => !["planned", "pass"].includes(c.status))) issues.push({ path: file, message: "deploy decision requires post_deploy_checks to be planned or pass" });
297
+ }
298
+ }
299
+ return issues;
300
+ }
301
+
302
+ function validateLearningCorrections(file: string, status: unknown, records: any[], issues: Issue[]): void {
303
+ records.forEach((record, index) => {
304
+ const correction = record?.correction;
305
+ const loc = `learning.json.records[${index}].correction`;
306
+ if (correction === undefined) {
307
+ if (status === "learned") issues.push({ path: file, message: `${loc}.needed is required when learning status is learned` });
308
+ return;
309
+ }
310
+ if (correction?.needed === false) {
311
+ if (typeof correction.evidence !== "string" || correction.evidence.length === 0) {
312
+ issues.push({ path: file, message: `${loc}.evidence is required when correction.needed is false` });
313
+ }
314
+ return;
315
+ }
316
+ if (correction?.needed !== true) return;
317
+ for (const key of ["type", "recurrence_key", "intended_behavior", "observed_behavior", "gap"]) {
318
+ if (typeof correction[key] !== "string" || correction[key].length === 0) issues.push({ path: file, message: `${loc}.${key} is required when correction.needed is true` });
319
+ }
320
+ const prevention = correction.prevention;
321
+ if (prevention !== undefined) validateLearningPrevention(file, `${loc}.prevention`, prevention, issues);
322
+ const hasPrevention = prevention !== undefined;
323
+ const hasNoChange = typeof correction.no_change_rationale === "string" && correction.no_change_rationale.length > 0;
324
+ if (!hasPrevention && !hasNoChange) {
325
+ issues.push({ path: file, message: `${loc} requires prevention route or no_change_rationale when correction.needed is true` });
326
+ }
327
+ });
328
+ }
329
+
330
+ function validateLearningPrevention(file: string, loc: string, prevention: any, issues: Issue[]): void {
331
+ if (!prevention || typeof prevention !== "object" || Array.isArray(prevention)) {
332
+ issues.push({ path: file, message: `${loc} must be an object` });
333
+ return;
334
+ }
335
+ if (typeof prevention.target !== "string" || prevention.target.length === 0) {
336
+ issues.push({ path: file, message: `${loc}.target is required` });
337
+ } else if (!["rule", "skill", "power", "agent", "eval", "doc", "backlog", "knowledge", "none"].includes(prevention.target)) {
338
+ issues.push({ path: file, message: `${loc}.target must be one of: rule, skill, power, agent, eval, doc, backlog, knowledge, none` });
339
+ }
340
+ if (typeof prevention.action !== "string" || prevention.action.length === 0) issues.push({ path: file, message: `${loc}.action is required` });
341
+ if (typeof prevention.status !== "string" || prevention.status.length === 0) {
342
+ issues.push({ path: file, message: `${loc}.status is required` });
343
+ } else if (!["open", "completed", "accepted", "deferred", "rejected"].includes(prevention.status)) {
344
+ issues.push({ path: file, message: `${loc}.status must be one of: open, completed, accepted, deferred, rejected` });
345
+ }
346
+ }
347
+
348
+ function validateSidecarGroup(inputs: string[], markdown: string[], requireSidecars: boolean, requireCritique: boolean): Issue[] {
349
+ const issues: Issue[] = [];
350
+ const sidecars = sidecarPaths(inputs);
351
+ const byDir = new Map<string, Map<string, any>>();
352
+ for (const file of sidecars) {
353
+ const { value } = readJson(file);
354
+ if (value !== undefined) {
355
+ const dir = path.dirname(file);
356
+ if (!byDir.has(dir)) byDir.set(dir, new Map());
357
+ byDir.get(dir)!.set(path.basename(file), value);
358
+ }
359
+ }
360
+ for (const [dir, payloads] of byDir) {
361
+ const slugs = new Set([...payloads.values()].map((p: any) => p.task_slug).filter(Boolean));
362
+ if (slugs.size > 1) issues.push({ path: dir, message: "sidecar task_slug mismatch" });
363
+ const evidence = payloads.get("evidence.json");
364
+ if (evidence?.verdict === "pass" && Array.isArray(evidence.checks) && evidence.checks.some((c: any) => c.status !== "pass" && c.status !== "skip")) issues.push({ path: path.join(dir, "evidence.json"), message: "pass verdict requires all non-skipped checks to pass" });
365
+ }
366
+ if (requireSidecars) {
367
+ const dirs = new Set<string>(markdown.map((p) => path.dirname(p)));
368
+ for (const dir of dirs) {
369
+ const deliver = markdown.find((p) => path.dirname(p) === dir && p.includes("deliver") && !p.includes("plan") && !p.includes("review"));
370
+ const delivered = deliver ? /status:\s*(delivered|accepted|archived)/i.test(readText(deliver)) : true;
371
+ for (const name of ["state.json", "acceptance.json", ...(delivered ? ["evidence.json"] : []), "handoff.json"]) {
372
+ if (!fs.existsSync(path.join(dir, name))) issues.push({ path: path.join(dir, name), message: "required sidecar is missing" });
373
+ }
374
+ if (requireCritique && !fs.existsSync(path.join(dir, "critique.json"))) issues.push({ path: path.join(dir, "critique.json"), message: "required sidecar is missing" });
375
+ const acceptance = path.join(dir, "acceptance.json");
376
+ if (deliver && fs.existsSync(acceptance)) {
377
+ const expected = definitionAcceptanceCriteria(readText(deliver)).length;
378
+ const payload = JSON.parse(readText(acceptance));
379
+ const actual = Array.isArray(payload.criteria) ? payload.criteria.length : 0;
380
+ if (expected && actual !== expected) issues.push({ path: acceptance, message: `acceptance.json has ${actual} criteria but Markdown defines ${expected}` });
381
+ }
382
+ }
383
+ }
384
+ return issues;
385
+ }
386
+
387
+ function main(): number {
388
+ const args = process.argv.slice(2);
389
+ const requireSidecars = args.includes("--require-sidecars");
390
+ const requireCritique = args.includes("--require-critique");
391
+ const skipMarkdown = args.includes("--skip-markdown-validation");
392
+ const pathsIn = args.filter((a) => !a.startsWith("--"));
393
+ if (!pathsIn.length) {
394
+ console.error("usage: validate-workflow-artifacts [--require-sidecars] [--require-critique] [--skip-markdown-validation] paths...");
395
+ return 2;
396
+ }
397
+ const markdown = artifactPaths(pathsIn);
398
+ const sidecars = sidecarPaths(pathsIn);
399
+ const issues: Issue[] = [];
400
+ if (!skipMarkdown) for (const file of markdown) issues.push(...validateArtifact(file));
401
+ for (const file of sidecars) issues.push(...validateSidecar(file));
402
+ issues.push(...validateSidecarGroup(pathsIn, markdown, requireSidecars, requireCritique));
403
+ if (issues.length) {
404
+ for (const issue of issues) console.error(`${issue.path}: ${issue.message}`);
405
+ return 1;
406
+ }
407
+ console.log(`Validated ${markdown.length} artifact(s) and ${sidecars.length} sidecar(s).`);
408
+ return 0;
409
+ }
410
+
411
+ process.exit(main());