@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,816 @@
1
+ #!/usr/bin/env node
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { execFileSync } from "node:child_process";
5
+ const statuses = new Set(["new", "planning", "planned", "in_progress", "blocked", "verifying", "verified", "needs_decision", "not_verified", "failed", "delivered", "accepted", "archived"]);
6
+ const phases = ["idea", "backlog", "pickup", "planning", "execution", "verification", "goal_fit", "evidence", "release", "learning", "done"];
7
+ const checkKinds = new Set(["build", "types", "lint", "test", "security", "diff", "browser", "runtime", "policy", "external"]);
8
+ const checkStatuses = new Set(["pass", "fail", "not_verified", "skip"]);
9
+ const verdicts = new Set(["pass", "partial", "fail", "not_verified"]);
10
+ function now() { return new Date().toISOString().replace(/\.\d{3}Z$/, "Z"); }
11
+ function read(file) { return fs.readFileSync(file, "utf8"); }
12
+ function writeJson(file, payload) { fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, `${JSON.stringify(payload, null, 2)}\n`); }
13
+ function printJson(payload) { console.log(JSON.stringify(payload).replace(/":/g, '": ').replace(/,"/g, ', "')); }
14
+ function loadJson(file, fallback = {}) { return fs.existsSync(file) ? JSON.parse(read(file)) : { ...fallback }; }
15
+ function appendJsonl(file, payload) {
16
+ fs.mkdirSync(path.dirname(file), { recursive: true });
17
+ const line = JSON.stringify(payload, Object.keys(payload).sort()).replace(/":/g, '": ').replace(/,"/g, ', "');
18
+ fs.appendFileSync(file, `${line}\n`);
19
+ }
20
+ function die(message) { throw new Error(message); }
21
+ function slugify(value, fallback) { return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || fallback; }
22
+ function safeRepoIdentifier(value) {
23
+ const trimmed = value.trim().replace(/\.git$/, "");
24
+ if (!trimmed || trimmed.length > 120)
25
+ return "";
26
+ if (path.isAbsolute(trimmed) || trimmed.includes("\\") || /[\x00-\x1F\x7F]/.test(trimmed))
27
+ return "";
28
+ const parts = trimmed.split("/");
29
+ if (parts.length > 2 || parts.some((part) => !part || part === "." || part === ".."))
30
+ return "";
31
+ if (!parts.every((part) => /^[A-Za-z0-9_.-]+$/.test(part)))
32
+ return "";
33
+ return parts.join("/");
34
+ }
35
+ function parseRepoRemote(value) {
36
+ const trimmed = value.trim().replace(/\.git$/, "");
37
+ const ssh = /^git@[^:]+:(?<owner>[^/]+)\/(?<repo>[^/]+)$/.exec(trimmed);
38
+ if (ssh?.groups)
39
+ return safeRepoIdentifier(`${ssh.groups.owner}/${ssh.groups.repo}`);
40
+ try {
41
+ const url = new URL(trimmed);
42
+ if (!["https:", "http:", "ssh:", "git:"].includes(url.protocol))
43
+ return "";
44
+ const parts = url.pathname.split("/").filter(Boolean);
45
+ if (parts.length >= 2)
46
+ return safeRepoIdentifier(`${parts.at(-2)}/${parts.at(-1)}`);
47
+ }
48
+ catch {
49
+ // Non-URL remotes fall back to repository directory name below.
50
+ }
51
+ return "";
52
+ }
53
+ function repoIdentifier() {
54
+ const explicit = safeRepoIdentifier(process.env.FLOW_AGENTS_REPO ?? "");
55
+ if (explicit)
56
+ return explicit;
57
+ try {
58
+ const remote = execFileSync("git", ["config", "--get", "remote.origin.url"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
59
+ const parsed = parseRepoRemote(remote);
60
+ if (parsed)
61
+ return parsed;
62
+ }
63
+ catch {
64
+ // Keep sidecar writing independent of Git availability.
65
+ }
66
+ try {
67
+ const top = execFileSync("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
68
+ if (top)
69
+ return safeRepoIdentifier(path.basename(top)) || "workspace";
70
+ }
71
+ catch {
72
+ // Fall through to cwd basename for non-Git workspaces.
73
+ }
74
+ return safeRepoIdentifier(path.basename(process.cwd())) || "workspace";
75
+ }
76
+ function sidecarBase(slug) {
77
+ return { schema_version: "1.0", task_slug: slug, repo: repoIdentifier() };
78
+ }
79
+ function parseArgs(argv) {
80
+ const [command, ...rest] = argv;
81
+ const positional = [];
82
+ const opts = {};
83
+ const flags = new Set();
84
+ for (let i = 0; i < rest.length; i += 1) {
85
+ const arg = rest[i];
86
+ if (!arg.startsWith("--")) {
87
+ positional.push(arg);
88
+ continue;
89
+ }
90
+ const key = arg.slice(2);
91
+ const next = rest[i + 1];
92
+ if (!next || next.startsWith("--"))
93
+ flags.add(key);
94
+ else {
95
+ (opts[key] ??= []).push(next);
96
+ i += 1;
97
+ }
98
+ }
99
+ return { command: command ?? "", positional, opts, flags };
100
+ }
101
+ function opt(parsed, key, fallback = "") { return parsed.opts[key]?.at(-1) ?? fallback; }
102
+ function opts(parsed, key) { return parsed.opts[key] ?? []; }
103
+ function isUnderDir(dir, root) {
104
+ const resolvedRoot = fs.realpathSync(root);
105
+ const resolvedDir = fs.realpathSync(dir);
106
+ const relative = path.relative(resolvedRoot, resolvedDir);
107
+ return !!relative && !relative.startsWith("..") && !path.isAbsolute(relative);
108
+ }
109
+ function explicitArtifactRoot(p) {
110
+ const explicit = opt(p, "artifact-dir");
111
+ const configuredRoot = opt(p, "artifact-root");
112
+ if (!explicit)
113
+ return path.resolve(configuredRoot || ".flow-agents");
114
+ const dir = path.resolve(explicit);
115
+ if (!fs.existsSync(dir))
116
+ die(`artifact directory does not exist: ${dir}`);
117
+ if (fs.lstatSync(dir).isSymbolicLink())
118
+ die(`artifact directory must not be a symlink: ${dir}`);
119
+ if (configuredRoot) {
120
+ const root = path.resolve(configuredRoot);
121
+ if (!isUnderDir(dir, root))
122
+ die(`artifact directory must be under artifact root: ${dir} is outside ${root}`);
123
+ return root;
124
+ }
125
+ return path.dirname(dir);
126
+ }
127
+ function requireArtifactDirUnderRoot(dir, root) {
128
+ if (!dir || !fs.existsSync(dir))
129
+ die("artifact directory does not exist");
130
+ if (fs.lstatSync(dir).isSymbolicLink())
131
+ die(`artifact directory must not be a symlink: ${dir}`);
132
+ if (!isUnderDir(dir, root))
133
+ die(`artifact directory must be under artifact root: ${dir} is outside ${root}`);
134
+ }
135
+ function isPermissionDeniedLockError(error) {
136
+ return error.code === "EPERM" || error.code === "EACCES";
137
+ }
138
+ function lockAcquisitionFailureMessage(command, lockDir, error) {
139
+ const original = error.message || error.code || String(error);
140
+ if (!isPermissionDeniedLockError(error)) {
141
+ return `failed to acquire workflow sidecar lock for ${command}: ${lockDir}: ${original}`;
142
+ }
143
+ return [
144
+ `failed to acquire workflow sidecar lock for ${command}: ${lockDir}: ${original}`,
145
+ "Likely cause: local directory permissions, ownership, or sandbox restrictions prevented creating the lock directory.",
146
+ "Safe next step: fix permissions or ownership on the artifact directory, or rerun in an approved writable workspace.",
147
+ "If still blocked: manually write schema-valid sidecars and run workflow artifact validation rather than bypassing locks.",
148
+ ].join(" ");
149
+ }
150
+ async function withLock(dir, create, command, body) {
151
+ if (create)
152
+ fs.mkdirSync(dir, { recursive: true });
153
+ if (!fs.existsSync(dir))
154
+ return body();
155
+ const lockDir = path.join(dir, ".workflow-sidecar.lockdir");
156
+ const staleMs = Number(process.env.FLOW_AGENTS_WORKFLOW_SIDECAR_STALE_LOCK_MS ?? 5 * 60 * 1000);
157
+ const deadline = Date.now() + 30000;
158
+ while (true) {
159
+ try {
160
+ fs.mkdirSync(lockDir);
161
+ break;
162
+ }
163
+ catch (error) {
164
+ const lockError = error;
165
+ if (lockError.code !== "EEXIST") {
166
+ die(lockAcquisitionFailureMessage(command, lockDir, lockError));
167
+ }
168
+ try {
169
+ const stat = fs.statSync(lockDir);
170
+ if (staleMs > 0 && Date.now() - stat.mtimeMs > staleMs) {
171
+ fs.rmSync(lockDir, { recursive: true, force: true });
172
+ continue;
173
+ }
174
+ }
175
+ catch (error) {
176
+ if (error.code === "ENOENT")
177
+ continue;
178
+ throw error;
179
+ }
180
+ if (Date.now() > deadline)
181
+ die(`timed out waiting for workflow sidecar lock for ${command}: ${dir}`);
182
+ await new Promise((resolve) => setTimeout(resolve, 20));
183
+ }
184
+ }
185
+ try {
186
+ const delay = process.env.FLOW_AGENTS_WORKFLOW_SIDECAR_LOCK_DELAY;
187
+ if (delay)
188
+ await new Promise((resolve) => setTimeout(resolve, Number(delay) * 1000));
189
+ return body();
190
+ }
191
+ finally {
192
+ fs.rmSync(lockDir, { recursive: true, force: true });
193
+ }
194
+ }
195
+ function section(text, heading) {
196
+ const match = new RegExp(`^(?<marks>##+)\\s+${heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`, "m").exec(text);
197
+ if (!match?.groups)
198
+ return "";
199
+ const start = match.index + match[0].length;
200
+ const rest = text.slice(start);
201
+ const next = new RegExp(`^#{2,${match.groups.marks.length}}\\s+`, "m").exec(rest);
202
+ return rest.slice(0, next?.index).trim();
203
+ }
204
+ function definitionAcceptanceLines(markdown) {
205
+ const out = [];
206
+ let active = false;
207
+ for (const raw of section(markdown, "Definition Of Done").split(/\r?\n/)) {
208
+ const line = raw.trim();
209
+ if (/^-\s+\*\*Acceptance criteria:\*\*/i.test(line)) {
210
+ active = true;
211
+ continue;
212
+ }
213
+ if (active && /^-\s+\*\*(Usefulness checks|Stop-short risks|Durable docs target|Scope|User outcome):\*\*/i.test(line))
214
+ break;
215
+ if (active && /^-\s+\[[ xX]\]/.test(line))
216
+ out.push(line);
217
+ }
218
+ return out;
219
+ }
220
+ function parseCriterion(line, index) {
221
+ let text = line.replace(/^-\s+\[[ xX]\]\s*/, "").trim();
222
+ const m = /\s+-\s+Evidence:\s*(.+)$/i.exec(text);
223
+ const evidence = m?.[1]?.trim().replace(/\.$/, "");
224
+ if (m)
225
+ text = text.slice(0, m.index).trim().replace(/\.$/, "");
226
+ const item = { id: slugify(text, `criterion-${index + 1}`), description: text, status: "pending" };
227
+ if (evidence)
228
+ item.evidence_refs = [evidenceRef("command", { excerpt: evidence })];
229
+ return item;
230
+ }
231
+ function artifactDirFrom(value) { return path.extname(value) ? path.dirname(value) : value; }
232
+ function taskSlugFor(dir, explicit = "") { return explicit || path.basename(dir); }
233
+ function relArtifacts(dir) { return fs.existsSync(dir) ? fs.readdirSync(dir).filter((n) => n.endsWith(".md") || n.endsWith(".json")).sort() : []; }
234
+ function sessionDirFor(root, slug) {
235
+ if (!slug)
236
+ die("--task-slug is required");
237
+ if (path.isAbsolute(slug))
238
+ die("--task-slug must be a relative slug");
239
+ if (slug.includes(".."))
240
+ die("--task-slug must not contain '..'");
241
+ if (slug.includes("/") || slug.includes("\\") || path.basename(slug) !== slug)
242
+ die("--task-slug must not contain path separators");
243
+ const dir = path.resolve(root, slug);
244
+ const relative = path.relative(root, dir);
245
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative))
246
+ die("session directory must stay under artifact root");
247
+ return dir;
248
+ }
249
+ function validateAgentId(agent) {
250
+ if (!agent)
251
+ die("--agent-id is required");
252
+ if (path.isAbsolute(agent))
253
+ die("--agent-id must be a relative slug");
254
+ if (agent.includes(".."))
255
+ die("--agent-id must not contain '..'");
256
+ if (agent.includes("/") || agent.includes("\\") || path.basename(agent) !== agent)
257
+ die("--agent-id must not contain path separators");
258
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(agent))
259
+ die("--agent-id must be a conservative slug");
260
+ return agent;
261
+ }
262
+ function writeCurrent(root, dir, timestamp, owner, source) {
263
+ writeJson(path.join(root, "current.json"), {
264
+ schema_version: "1.0",
265
+ active_slug: path.basename(dir),
266
+ artifact_dir: path.relative(root, dir) || ".",
267
+ updated_at: timestamp,
268
+ owner,
269
+ source,
270
+ active_agents: [],
271
+ });
272
+ }
273
+ function loadCurrent(root) {
274
+ const file = path.join(root, "current.json");
275
+ if (!fs.existsSync(file))
276
+ return null;
277
+ return JSON.parse(read(file));
278
+ }
279
+ function currentDir(root) {
280
+ const c = loadCurrent(root);
281
+ if (!c)
282
+ return null;
283
+ const dir = path.resolve(root, c.artifact_dir ?? c.active_slug ?? "");
284
+ return fs.existsSync(dir) ? dir : null;
285
+ }
286
+ function updateCurrentAgent(root, dir, agentId, status, timestamp) {
287
+ const cur = loadCurrent(root);
288
+ if (!cur || path.resolve(root, cur.artifact_dir ?? "") !== path.resolve(dir))
289
+ return;
290
+ const active = Array.isArray(cur.active_agents) ? cur.active_agents.filter((a) => a.agent_id !== agentId) : [];
291
+ if (status === "active" || status === "blocked")
292
+ active.push({ agent_id: agentId, status, updated_at: timestamp });
293
+ cur.active_agents = active;
294
+ cur.updated_at = timestamp;
295
+ writeJson(path.join(root, "current.json"), cur);
296
+ }
297
+ function initSidecars(dir, slug, sourceRequest, summary, nextAction, timestamp, markdown) {
298
+ const criteria = markdown ? definitionAcceptanceLines(markdown).map(parseCriterion) : [];
299
+ writeJson(path.join(dir, "state.json"), {
300
+ ...sidecarBase(slug), status: "planned", phase: "planning", created_at: timestamp, updated_at: timestamp,
301
+ artifact_paths: relArtifacts(dir),
302
+ next_action: { status: "continue", summary: nextAction || summary },
303
+ });
304
+ writeJson(path.join(dir, "acceptance.json"), {
305
+ ...sidecarBase(slug), source_request: sourceRequest,
306
+ criteria,
307
+ goal_fit: { status: "pending", summary: "Goal fit has not been verified yet." },
308
+ });
309
+ writeJson(path.join(dir, "handoff.json"), {
310
+ ...sidecarBase(slug), summary, current_state_ref: "state.json", next_steps: nextAction ? [nextAction] : [], blockers: [], warnings: [],
311
+ });
312
+ }
313
+ function ensureSession(p) {
314
+ const root = path.resolve(opt(p, "artifact-root", ".flow-agents"));
315
+ const slug = opt(p, "task-slug") || die("--task-slug is required");
316
+ const dir = sessionDirFor(root, slug);
317
+ fs.mkdirSync(dir, { recursive: true });
318
+ const timestamp = opt(p, "timestamp", now());
319
+ let md = fs.existsSync(path.join(dir, `${slug}--deliver.md`)) ? read(path.join(dir, `${slug}--deliver.md`)) : "";
320
+ if (!md) {
321
+ md = `# ${opt(p, "title", slug)}\n\nbranch: main\nworktree: main\ncreated: ${timestamp}\nstatus: planning\ntype: deliver\niteration: 1\n\n## Plan\n\n${opt(p, "summary", "")}\n\n## Definition Of Done\n\n- **User outcome:** ${opt(p, "summary", "Workflow session is durable.")}\n- **Scope:** Workflow session artifacts and sidecars.\n- **Acceptance criteria:**\n${opts(p, "criterion").map((c) => ` - [ ] ${c} - Evidence: pending.`).join("\n")}\n- **Usefulness checks:**\n - [ ] User-facing workflow is documented or discoverable\n- **Stop-short risks:** Workflow artifacts could drift.\n- **Durable docs target:** not needed\n- **Sandbox mode:** local-edit\n\n## Execution Progress\n\n- [ ] Session initialized.\n\n## Verification Report\n\nBuild: [NOT_VERIFIED] Verification has not run yet.\n\n### Acceptance Criteria\n- [NOT_VERIFIED] Verification has not run yet - Evidence: pending workflow execution and checks.\n\n### Verdict: NOT_VERIFIED\n\n## Goal Fit Gate\n\n- [ ] Original user goal restated\n\n## Final Acceptance\n\n- [ ] CI/relevant checks passed or local equivalent recorded\n`;
322
+ fs.writeFileSync(path.join(dir, `${slug}--deliver.md`), md);
323
+ }
324
+ if (!fs.existsSync(path.join(dir, "state.json")) || !fs.existsSync(path.join(dir, "acceptance.json")) || !fs.existsSync(path.join(dir, "handoff.json"))) {
325
+ initSidecars(dir, slug, opt(p, "source-request"), opt(p, "summary"), opt(p, "next-action", "Continue."), timestamp, md);
326
+ }
327
+ writeCurrent(root, dir, timestamp, "workflow-sidecar", "ensure-session");
328
+ console.log(dir);
329
+ return 0;
330
+ }
331
+ function current(p) {
332
+ const root = path.resolve(opt(p, "artifact-root", ".flow-agents"));
333
+ const dir = currentDir(root);
334
+ if (!dir)
335
+ die("no current workflow session is recorded");
336
+ const format = opt(p, "format", "path");
337
+ console.log(format === "slug" ? path.basename(dir) : dir);
338
+ return 0;
339
+ }
340
+ function recordAgentEvent(p) {
341
+ const hasExplicitRoot = !!opt(p, "artifact-root");
342
+ const root = explicitArtifactRoot(p);
343
+ const explicit = opt(p, "artifact-dir");
344
+ const dir = explicit ? path.resolve(explicit) : currentDir(root);
345
+ if (!dir || !fs.existsSync(dir))
346
+ die("artifact directory does not exist");
347
+ if (explicit && fs.lstatSync(dir).isSymbolicLink())
348
+ die(`artifact directory must not be a symlink: ${dir}`);
349
+ if (hasExplicitRoot)
350
+ requireArtifactDirUnderRoot(dir, root);
351
+ const timestamp = opt(p, "timestamp", now());
352
+ const agent = validateAgentId(opt(p, "agent-id"));
353
+ const event = { timestamp, agent_id: agent, kind: opt(p, "kind", "note"), status: opt(p, "status", "info"), summary: opt(p, "summary"), ...(opt(p, "ref") ? { ref: opt(p, "ref") } : {}) };
354
+ appendJsonl(path.join(dir, "agents", agent, "events.jsonl"), event);
355
+ updateCurrentAgent(root, dir, agent, event.status, timestamp);
356
+ return 0;
357
+ }
358
+ function initPlan(p) {
359
+ const artifact = p.positional[0] || die("artifact path is required");
360
+ const dir = artifactDirFrom(artifact);
361
+ const slug = taskSlugFor(dir, opt(p, "task-slug"));
362
+ initSidecars(dir, slug, opt(p, "source-request"), opt(p, "summary"), opt(p, "next-action"), opt(p, "timestamp", now()), read(artifact));
363
+ return 0;
364
+ }
365
+ function parseJson(value, label) {
366
+ try {
367
+ const raw = JSON.parse(value);
368
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
369
+ throw new Error("object expected");
370
+ return raw;
371
+ }
372
+ catch {
373
+ die(`${label} must be valid JSON object`);
374
+ }
375
+ }
376
+ function evidenceRef(kind, fields) {
377
+ return { kind, ...fields };
378
+ }
379
+ function hasNonEmptyString(value) {
380
+ return typeof value === "string" && value.length > 0;
381
+ }
382
+ function hasPositiveInteger(value) {
383
+ return Number.isInteger(value) && Number(value) >= 1;
384
+ }
385
+ function validateEvidenceRef(ref, label) {
386
+ if (!["source", "command", "artifact", "provider", "external"].includes(ref.kind))
387
+ die(`${label} entry kind must be one of: source, command, artifact, provider, external`);
388
+ for (const key of Object.keys(ref))
389
+ if (!["kind", "url", "file", "line_start", "line_end", "excerpt", "summary"].includes(key))
390
+ die(`${label} entries contain unsupported field: ${key}`);
391
+ if (ref.url !== undefined && !hasNonEmptyString(ref.url))
392
+ die(`${label} entry url must be a non-empty string`);
393
+ if (ref.file !== undefined && !hasNonEmptyString(ref.file))
394
+ die(`${label} entry file must be a non-empty string`);
395
+ if (ref.excerpt !== undefined && !hasNonEmptyString(ref.excerpt))
396
+ die(`${label} entry excerpt must be a non-empty string`);
397
+ if (ref.summary !== undefined && !hasNonEmptyString(ref.summary))
398
+ die(`${label} entry summary must be a non-empty string`);
399
+ if (ref.line_start !== undefined && !hasPositiveInteger(ref.line_start))
400
+ die(`${label} entry line_start must be a positive integer`);
401
+ if (ref.line_end !== undefined && !hasPositiveInteger(ref.line_end))
402
+ die(`${label} entry line_end must be a positive integer`);
403
+ if (ref.kind === "source" && (!hasNonEmptyString(ref.file) || !hasPositiveInteger(ref.line_start) || !hasPositiveInteger(ref.line_end) || !hasNonEmptyString(ref.excerpt)))
404
+ die(`${label} source refs require file, line_start, line_end, and excerpt`);
405
+ if (ref.kind === "artifact" && (!hasNonEmptyString(ref.file) && !hasNonEmptyString(ref.url)))
406
+ die(`${label} artifact refs require file or url`);
407
+ if (ref.kind === "artifact" && (!hasNonEmptyString(ref.summary) && !hasNonEmptyString(ref.excerpt)))
408
+ die(`${label} artifact refs require summary or excerpt`);
409
+ if (ref.kind === "command" && (!hasNonEmptyString(ref.summary) && !hasNonEmptyString(ref.excerpt) && !hasNonEmptyString(ref.url)))
410
+ die(`${label} command refs require summary, excerpt, or url`);
411
+ if ((ref.kind === "provider" || ref.kind === "external") && !hasNonEmptyString(ref.url))
412
+ die(`${label} ${ref.kind} refs require url`);
413
+ return ref;
414
+ }
415
+ function normalizeEvidenceRefs(raw, label) {
416
+ if (!Array.isArray(raw))
417
+ die(`${label} must be an array`);
418
+ return raw.map((ref) => {
419
+ if (typeof ref === "string")
420
+ die(`${label} entries must be structured evidence reference objects; legacy string refs are not supported`);
421
+ if (!ref || typeof ref !== "object" || Array.isArray(ref))
422
+ die(`${label} entries must be objects`);
423
+ return validateEvidenceRef({ ...ref }, label);
424
+ });
425
+ }
426
+ function normalizeCheck(raw) {
427
+ const check = { ...raw };
428
+ if (!check.id || !check.kind || !check.status || !check.summary)
429
+ die("check requires id, kind, status, and summary");
430
+ if (!checkKinds.has(check.kind))
431
+ die("kind must be one of: build, types, lint, test, security, diff, browser, runtime, policy, external");
432
+ if (!checkStatuses.has(check.status))
433
+ die("status must be one of: pass, fail, not_verified, skip");
434
+ if (Array.isArray(check.standard_refs))
435
+ for (const ref of check.standard_refs)
436
+ if (!["junit", "sarif", "coverage", "veritas"].includes(ref.standard))
437
+ die("standard must be one of");
438
+ if (check.artifact_refs)
439
+ check.artifact_refs = normalizeEvidenceRefs(check.artifact_refs, "artifact_refs");
440
+ if (check.surface_trust_refs)
441
+ check.surface_trust_refs = normalizeSurfaceRefs(check.surface_trust_refs);
442
+ return check;
443
+ }
444
+ function normalizeSurfaceRefs(refs) {
445
+ if (!Array.isArray(refs))
446
+ die("surface_trust_refs must be an array");
447
+ return refs.map((ref) => {
448
+ const keys = JSON.stringify(ref).match(/"([^"]+)":/g) ?? [];
449
+ for (const key of keys.map((k) => k.slice(1, -2)))
450
+ if (key.toLowerCase().includes("veritas"))
451
+ die(`unsupported field in Surface trust ref: ${key}`);
452
+ const out = { ...ref };
453
+ if (!["TrustReport", "Trust Snapshot"].includes(out.artifact_kind))
454
+ die("artifact_kind must be one of");
455
+ const status = deriveSurfaceStatus(out);
456
+ if (out.status === "pass" && status !== "pass")
457
+ die("surface_trust_refs contradicts Surface trust facts");
458
+ return out;
459
+ });
460
+ }
461
+ function deriveSurfaceStatus(ref) {
462
+ if (ref.claim_status !== "accepted")
463
+ return "fail";
464
+ if (ref.freshness?.status !== "fresh")
465
+ return "not_verified";
466
+ if (!ref.authority || ref.authority.producer === "unknown")
467
+ return "fail";
468
+ if (ref.integrity?.status !== "matched")
469
+ return "fail";
470
+ return "pass";
471
+ }
472
+ function surfaceCheckFromArtifact(file, index) {
473
+ const raw = JSON.parse(read(file));
474
+ const lower = JSON.stringify(raw).toLowerCase();
475
+ let ref;
476
+ if (lower.includes("provider") && lower.includes("absent")) {
477
+ ref = { artifact_kind: "TrustReport", artifact_ref: file, gate_id: "provider.unavailable", claim_type: "surface.claim", claim_status: "unknown", subject: "builder-kit", freshness: { status: "unknown", summary: "No trust provider is configured" }, authority: { producer: "unknown", summary: "No trust provider is configured" }, integrity: { status: "unknown", summary: "Unknown" }, status: "not_verified", summary: "No trust provider is configured" };
478
+ }
479
+ else if (lower.includes("artifact") && lower.includes("absent")) {
480
+ ref = { artifact_kind: "TrustReport", artifact_ref: file, gate_id: "artifact.unavailable", claim_type: "surface.claim", claim_status: "unknown", subject: "builder-kit", freshness: { status: "unknown", summary: "Artifact not readable" }, authority: { producer: "unknown", summary: "Artifact not readable" }, integrity: { status: "unknown", summary: "Artifact not readable" }, status: "not_verified", summary: "artifact not readable" };
481
+ }
482
+ else {
483
+ const claimStatus = lower.includes("rejected") ? "rejected" : "accepted";
484
+ const freshness = lower.includes("stale") ? "stale" : "fresh";
485
+ const producer = lower.includes("missing-authority") ? "unknown" : "surface-local";
486
+ const integrity = lower.includes("mismatch") ? "mismatch" : "matched";
487
+ ref = { artifact_kind: file.includes("snapshot") ? "Trust Snapshot" : "TrustReport", artifact_ref: file, gate_id: "builder.surface.claim", claim_type: "surface.claim", claim_status: claimStatus, subject: "builder-kit", freshness: { status: freshness, summary: freshness === "fresh" ? "fresh" : "not currently verifiable" }, authority: { producer, summary: producer === "unknown" ? "missing authority" : "Local Surface trust producer." }, integrity: { status: integrity, summary: integrity === "matched" ? "matched" : "integrity mismatch" } };
488
+ ref.status = deriveSurfaceStatus(ref);
489
+ ref.summary = ref.status === "pass" ? "accepted" : ref.status === "not_verified" ? "not currently verifiable" : (claimStatus === "rejected" ? "rejected" : producer === "unknown" ? "missing authority" : "integrity mismatch");
490
+ }
491
+ return { id: `surface-trust-${index + 1}`, kind: "policy", status: ref.status, summary: ref.summary, surface_trust_refs: [ref] };
492
+ }
493
+ function updateAcceptance(dir, verdict) {
494
+ const file = path.join(dir, "acceptance.json");
495
+ if (!fs.existsSync(file))
496
+ return;
497
+ const data = loadJson(file);
498
+ const status = verdict === "pass" ? "pass" : verdict === "fail" ? "fail" : "not_verified";
499
+ if (Array.isArray(data.criteria))
500
+ data.criteria = data.criteria.map((c) => ({ ...c, status }));
501
+ data.goal_fit = { ...(data.goal_fit ?? {}), status, summary: verdict === "pass" ? "Evidence passed." : "Evidence requires follow-up." };
502
+ writeJson(file, data);
503
+ }
504
+ function validateAcceptanceEvidenceRefs(dir) {
505
+ const file = path.join(dir, "acceptance.json");
506
+ if (!fs.existsSync(file))
507
+ return;
508
+ const data = loadJson(file);
509
+ if (!Array.isArray(data.criteria))
510
+ return;
511
+ data.criteria.forEach((criterion, index) => {
512
+ if (criterion.evidence_refs !== undefined)
513
+ normalizeEvidenceRefs(criterion.evidence_refs, `acceptance.criteria[${index}].evidence_refs`);
514
+ });
515
+ }
516
+ function writeState(dir, slug, status, phase, timestamp, summary, next = "continue") {
517
+ writeJson(path.join(dir, "state.json"), { ...loadJson(path.join(dir, "state.json")), ...sidecarBase(slug), status, phase, updated_at: timestamp, artifact_paths: relArtifacts(dir), next_action: { status: next, summary } });
518
+ }
519
+ function recordEvidence(p) {
520
+ const dir = artifactDirFrom(p.positional[0] || die("artifact directory is required"));
521
+ const verdict = opt(p, "verdict") || die("--verdict is required");
522
+ if (!verdicts.has(verdict))
523
+ die("verdict must be one of: pass, partial, fail, not_verified");
524
+ const slug = taskSlugFor(dir, opt(p, "task-slug"));
525
+ const checks = [...opts(p, "check-json").map((v) => normalizeCheck(parseJson(v, "--check-json"))), ...opts(p, "surface-trust-json").map(surfaceCheckFromArtifact)];
526
+ if (!checks.length && opts(p, "surface-trust-json").length === 0)
527
+ die("record-evidence requires at least one --check-json or --surface-trust-json");
528
+ validateAcceptanceEvidenceRefs(dir);
529
+ const payload = { ...sidecarBase(slug), verdict, checks, not_verified_gaps: opts(p, "gap") };
530
+ writeJson(path.join(dir, "evidence.json"), payload);
531
+ updateAcceptance(dir, verdict);
532
+ const stateStatus = verdict === "pass" ? "verified" : verdict === "fail" ? "failed" : "not_verified";
533
+ writeState(dir, slug, stateStatus, "verification", opt(p, "timestamp", now()), "Evidence recorded.");
534
+ return 0;
535
+ }
536
+ function diagnostic(dir, code, summary) {
537
+ const payload = { timestamp: now(), code, summary };
538
+ appendJsonl(path.join(dir, "transition-diagnostics.jsonl"), payload);
539
+ die(`${code}: ${summary}`);
540
+ }
541
+ function advanceState(p) {
542
+ const dir = artifactDirFrom(p.positional[0] || die("artifact directory is required"));
543
+ const status = opt(p, "status");
544
+ const phase = opt(p, "phase");
545
+ const target = opt(p, "target-phase");
546
+ if (!statuses.has(status))
547
+ die(`status must be one of: ${[...statuses].join(", ")}`);
548
+ if (!phases.includes(phase))
549
+ die(`phase must be one of: ${phases.join(", ")}`);
550
+ if (target && !phases.includes(target))
551
+ die(`target phase must be one of: ${phases.join(", ")}`);
552
+ const prev = loadJson(path.join(dir, "state.json"));
553
+ if ((status === "archived" || status === "accepted") && prev.phase !== "learning")
554
+ diagnostic(dir, "terminal_jump_rejected", "Terminal workflow states require release and learning gates.");
555
+ const flow = opt(p, "flow-definition");
556
+ if (flow === "builder.build" && prev.phase === "verification" && phase === "execution") {
557
+ const reason = opt(p, "route-back-reason");
558
+ if (!reason)
559
+ diagnostic(dir, "route_back_reason_required", "Builder Kit route-back requires implementation_defect or equivalent reason.");
560
+ const file = path.join(dir, "transition-attempts.json");
561
+ const attempts = loadJson(file);
562
+ const key = `verification->execution:${reason}`;
563
+ const count = attempts[key]?.count ?? 0;
564
+ if (count >= 3)
565
+ diagnostic(dir, "route_back_attempts_exceeded", "Builder Kit route-back attempts exceeded.");
566
+ attempts[key] = { count: count + 1, reason, updated_at: opt(p, "timestamp", now()) };
567
+ writeJson(file, attempts);
568
+ }
569
+ const slug = taskSlugFor(dir, opt(p, "task-slug"));
570
+ const timestamp = opt(p, "timestamp", now());
571
+ writeState(dir, slug, status, phase, timestamp, opt(p, "summary"));
572
+ writeJson(path.join(dir, "handoff.json"), { ...loadJson(path.join(dir, "handoff.json")), ...sidecarBase(slug), summary: opt(p, "summary"), current_state_ref: "state.json", next_steps: [opt(p, "next-action")].filter(Boolean), blockers: [], warnings: [] });
573
+ return 0;
574
+ }
575
+ function normalizeFinding(raw) {
576
+ if (raw.file_refs !== undefined && !Array.isArray(raw.file_refs))
577
+ die("file_refs must be an array");
578
+ return raw;
579
+ }
580
+ function critiqueStatus(critiques, required) {
581
+ if (!required && critiques.length === 0)
582
+ return "not_required";
583
+ if (critiques.some((c) => c.verdict === "fail" || (Array.isArray(c.findings) && c.findings.some((f) => f.status === "open"))))
584
+ return "fail";
585
+ return "pass";
586
+ }
587
+ function recordCritique(p) {
588
+ const dir = artifactDirFrom(p.positional[0] || die("artifact directory is required"));
589
+ const slug = taskSlugFor(dir, opt(p, "task-slug"));
590
+ const existing = loadJson(path.join(dir, "critique.json"), { critiques: [] });
591
+ const critique = { id: opt(p, "id") || "review", reviewer: opt(p, "reviewer", "tool-code-reviewer"), reviewed_at: opt(p, "timestamp", now()), verdict: opt(p, "verdict", "pass"), summary: opt(p, "summary"), artifact_refs: opts(p, "artifact-ref"), findings: opts(p, "finding-json").map((v) => normalizeFinding(parseJson(v, "--finding-json"))) };
592
+ const critiques = [...(Array.isArray(existing.critiques) ? existing.critiques : []), critique];
593
+ if (critique.verdict === "pass" && critique.findings.some((f) => f.status === "open"))
594
+ die("required critique must pass");
595
+ writeJson(path.join(dir, "critique.json"), { ...sidecarBase(slug), status: critiqueStatus(critiques, true), required: true, updated_at: critique.reviewed_at, critiques });
596
+ return 0;
597
+ }
598
+ function frontmatter(text, key) {
599
+ if (!text.startsWith("---"))
600
+ return "";
601
+ const end = text.indexOf("\n---", 3);
602
+ if (end < 0)
603
+ return "";
604
+ return new RegExp(`^${key}:\\s*(.+)$`, "m").exec(text.slice(0, end))?.[1]?.trim() ?? "";
605
+ }
606
+ function importCritique(p) {
607
+ const dir = artifactDirFrom(p.positional[0] || die("artifact directory is required"));
608
+ const review = p.positional[1] || die("review artifact is required");
609
+ const text = read(review);
610
+ const role = frontmatter(text, "role");
611
+ if (!["review", "code-review"].includes(role))
612
+ die("review artifact must declare role");
613
+ const verdictRaw = (frontmatter(text, "verdict") || /###\s+Verdict:\s*([A-Z_]+)/i.exec(text)?.[1] || "PASS").toUpperCase();
614
+ const verdict = verdictRaw === "CHANGES_REQUESTED" || verdictRaw === "FAIL" ? "fail" : verdictRaw === "NOT_VERIFIED" ? "not_verified" : "pass";
615
+ const findings = [];
616
+ const re = /^####\s+\[(?<severity>[A-Z]+)\]\s+(?<target>.+?)\s+-\s+(?<title>.+)$/gm;
617
+ for (let m; (m = re.exec(text));) {
618
+ const title = m.groups?.title ?? "finding";
619
+ findings.push({ id: slugify(title, `finding-${findings.length + 1}`), severity: (m.groups?.severity ?? "info").toLowerCase(), status: opt(p, "finding-status", verdict === "pass" ? "fixed" : "open"), description: title, file_refs: [m.groups?.target ?? review] });
620
+ }
621
+ const parsed = { ...p, positional: [dir], opts: { ...p.opts, id: [slugify(path.basename(review).replace(/\.md$/, ""), "review")], reviewer: ["tool-code-reviewer"], verdict: [verdict], summary: [`Imported critique from ${path.basename(review)}`], "finding-json": findings.map((f) => JSON.stringify(f)) }, flags: p.flags };
622
+ const result = recordCritique(parsed);
623
+ if (verdict !== "pass")
624
+ die("required critique must pass");
625
+ return result;
626
+ }
627
+ function recordRelease(p) {
628
+ const dir = artifactDirFrom(p.positional[0] || die("artifact directory is required"));
629
+ const slug = taskSlugFor(dir, opt(p, "task-slug"));
630
+ const decision = opt(p, "decision");
631
+ if (!["merge", "release", "deploy", "hold", "rollback_required"].includes(decision))
632
+ die("decision must be one of: merge, release, deploy, hold, rollback_required");
633
+ const gates = opts(p, "gate-json").map((v) => parseJson(v, "--gate-json"));
634
+ if (["merge", "release", "deploy"].includes(decision) && !gates.some((g) => g.name === decision && g.status === "pass"))
635
+ die(`positive release decision requires ${decision} gate to pass`);
636
+ const payload = { ...sidecarBase(slug), decision: opt(p, "decision"), updated_at: opt(p, "timestamp", now()), scope: opt(p, "scope"), evidence_ref: opt(p, "evidence-ref"), gates: opts(p, "gate-json").map((v) => parseJson(v, "--gate-json")), rollback_plan: parseJson(opt(p, "rollback-json", '{"status":"not_required","summary":"Not required.","owner":"maintainer"}'), "--rollback-json"), observability_plan: parseJson(opt(p, "observability-json", '{"status":"not_required","summary":"Not required."}'), "--observability-json"), post_deploy_checks: opts(p, "post-deploy-json").map((v) => parseJson(v, "--post-deploy-json")), docs: parseJson(opt(p, "docs-json", '{"status":"not_needed","summary":"Not needed."}'), "--docs-json") };
637
+ const stateSummary = opt(p, "summary").trim() || `Release readiness recorded for ${decision}.`;
638
+ writeJson(path.join(dir, "release.json"), payload);
639
+ writeState(dir, slug, "delivered", "release", payload.updated_at, stateSummary);
640
+ return 0;
641
+ }
642
+ function validateLearningCorrection(record) {
643
+ const correction = record.correction;
644
+ if (correction === undefined)
645
+ return;
646
+ if (!correction || typeof correction !== "object" || Array.isArray(correction))
647
+ die("correction must be an object");
648
+ if (typeof correction.needed !== "boolean")
649
+ die("correction.needed must be boolean");
650
+ if (correction.needed === false) {
651
+ if (typeof correction.evidence !== "string" || correction.evidence.length === 0)
652
+ die("correction.evidence is required when correction.needed is false");
653
+ return;
654
+ }
655
+ for (const key of ["type", "recurrence_key", "intended_behavior", "observed_behavior", "gap"]) {
656
+ if (typeof correction[key] !== "string" || correction[key].length === 0)
657
+ die(`correction.${key} is required when correction.needed is true`);
658
+ }
659
+ if (!["workflow", "skill", "agent", "tooling", "test", "doc", "process", "product", "provider", "none"].includes(correction.type)) {
660
+ die("correction.type must be one of: workflow, skill, agent, tooling, test, doc, process, product, provider, none");
661
+ }
662
+ const prevention = correction.prevention;
663
+ if (prevention !== undefined)
664
+ validateLearningPrevention(prevention);
665
+ const hasPrevention = prevention !== undefined;
666
+ const hasNoChange = typeof correction.no_change_rationale === "string" && correction.no_change_rationale.length > 0;
667
+ if (!hasPrevention && !hasNoChange)
668
+ die("correction requires prevention route or no_change_rationale when correction.needed is true");
669
+ }
670
+ function validateLearningPrevention(prevention) {
671
+ if (!prevention || typeof prevention !== "object" || Array.isArray(prevention))
672
+ die("correction.prevention must be an object");
673
+ const value = prevention;
674
+ if (typeof value.target !== "string" || value.target.length === 0)
675
+ die("correction.prevention.target is required");
676
+ if (!["rule", "skill", "power", "agent", "eval", "doc", "backlog", "knowledge", "none"].includes(value.target))
677
+ die("correction.prevention.target must be one of: rule, skill, power, agent, eval, doc, backlog, knowledge, none");
678
+ if (typeof value.action !== "string" || value.action.length === 0)
679
+ die("correction.prevention.action is required");
680
+ if (typeof value.status !== "string" || value.status.length === 0)
681
+ die("correction.prevention.status is required");
682
+ if (!["open", "completed", "accepted", "deferred", "rejected"].includes(value.status))
683
+ die("correction.prevention.status must be one of: open, completed, accepted, deferred, rejected");
684
+ }
685
+ function normalizeLearning(raw, timestamp) {
686
+ if (!Array.isArray(raw.source_refs))
687
+ die("source_refs must be an array");
688
+ if (!Array.isArray(raw.facts))
689
+ die("facts must be an array");
690
+ if (!Array.isArray(raw.routing))
691
+ die("routing must be an array");
692
+ if (!["success", "failure", "mixed", "unknown"].includes(raw.outcome))
693
+ die("learning outcome must be one of: success, failure, mixed, unknown");
694
+ validateLearningCorrection(raw);
695
+ return { recorded_at: timestamp, ...raw };
696
+ }
697
+ function recordLearning(p) {
698
+ const dir = artifactDirFrom(p.positional[0] || die("artifact directory is required"));
699
+ const slug = taskSlugFor(dir, opt(p, "task-slug"));
700
+ const timestamp = opt(p, "timestamp", now());
701
+ const records = opts(p, "record-json").map((v) => normalizeLearning(parseJson(v, "--record-json"), timestamp));
702
+ const status = opt(p, "status", "learned");
703
+ if (status === "learned" && records.some((r) => r.routing.some((x) => x.status === "open")))
704
+ die("learning status learned cannot have open routing");
705
+ if (status === "learned" && records.some((r) => r.correction === undefined))
706
+ die("learning status learned requires every record to include correction.needed");
707
+ writeJson(path.join(dir, "learning.json"), { ...sidecarBase(slug), status, updated_at: timestamp, records });
708
+ writeState(dir, slug, "accepted", "learning", timestamp, opt(p, "summary"));
709
+ return 0;
710
+ }
711
+ function evidenceClean(dir) {
712
+ const e = loadJson(path.join(dir, "evidence.json"), {});
713
+ return e.verdict === "pass" && Array.isArray(e.checks) && e.checks.length > 0 && e.checks.every((c) => {
714
+ if (!(c.status === "pass" || c.status === "skip"))
715
+ return false;
716
+ return !Array.isArray(c.standard_refs) || c.standard_refs.every((r) => ["junit", "sarif", "coverage", "veritas"].includes(r.standard));
717
+ });
718
+ }
719
+ function critiqueClean(dir) {
720
+ const c = loadJson(path.join(dir, "critique.json"), {});
721
+ return c.status === "pass" && Array.isArray(c.critiques) && c.critiques.every((x) => x.verdict !== "fail" && (!Array.isArray(x.findings) || x.findings.every((f) => f.status !== "open" && (f.file_refs === undefined || Array.isArray(f.file_refs)))));
722
+ }
723
+ function assertExistingLearningValid(dir) {
724
+ const file = path.join(dir, "learning.json");
725
+ if (!fs.existsSync(file))
726
+ return;
727
+ const data = loadJson(file);
728
+ if (!Array.isArray(data.records))
729
+ die("learning records must be an array");
730
+ for (const record of data.records) {
731
+ if (!Array.isArray(record.source_refs))
732
+ die("source_refs must be an array");
733
+ if (!Array.isArray(record.facts))
734
+ die("facts must be an array");
735
+ if (!Array.isArray(record.routing))
736
+ die("routing must be an array");
737
+ validateLearningCorrection(record);
738
+ if (data.status === "learned" && record.correction === undefined)
739
+ die("learning status learned requires every record to include correction.needed");
740
+ }
741
+ }
742
+ function dogfoodPass(p) {
743
+ const root = path.resolve(opt(p, "artifact-root", ".flow-agents"));
744
+ const dir = path.resolve(opt(p, "artifact-dir") || currentDir(root) || "");
745
+ requireArtifactDirUnderRoot(dir, root);
746
+ assertExistingLearningValid(dir);
747
+ const verdict = opt(p, "verdict");
748
+ if (verdict === "pass") {
749
+ const checks = opts(p, "check-json").map((v) => normalizeCheck(parseJson(v, "--check-json")));
750
+ if (checks.some((c) => c.status !== "pass" && c.status !== "skip"))
751
+ die("clean evidence requires all non-skipped checks to pass");
752
+ if (fs.existsSync(path.join(dir, "evidence.json")) && !evidenceClean(dir))
753
+ die("cannot mark clean without passing evidence");
754
+ if (!fs.existsSync(path.join(dir, "evidence.json")) && checks.length === 0)
755
+ die("cannot mark clean without passing evidence");
756
+ if (p.flags.has("require-critique") || opt(p, "release-decision")) {
757
+ const newCritiqueVerdict = opt(p, "critique-verdict", "pass");
758
+ for (const value of opts(p, "finding-json"))
759
+ normalizeFinding(parseJson(value, "--finding-json"));
760
+ if (newCritiqueVerdict !== "pass")
761
+ die(opt(p, "release-decision") ? "requires clean critique" : "requires clean critique before recording pass evidence");
762
+ if (!opt(p, "critique-id") && !critiqueClean(dir))
763
+ die("requires passing critique");
764
+ if (fs.existsSync(path.join(dir, "critique.json")) && !critiqueClean(dir))
765
+ die(opt(p, "release-decision") ? "requires clean critique" : "requires clean critique before recording pass evidence");
766
+ }
767
+ }
768
+ const learningRecords = opts(p, "learning-record-json").map((v) => normalizeLearning(parseJson(v, "--learning-record-json"), opt(p, "timestamp", now())));
769
+ if (opt(p, "learning-status") === "learned" && learningRecords.some((r) => r.routing.some((x) => x.status === "open")))
770
+ die("learned status cannot have open learning routing");
771
+ if (opt(p, "learning-status") === "learned" && learningRecords.some((r) => r.correction === undefined))
772
+ die("learned status requires every learning record to include correction.needed");
773
+ if (opts(p, "check-json").length)
774
+ recordEvidence({ ...p, positional: [dir], opts: { ...p.opts, verdict: [verdict] }, flags: p.flags });
775
+ if (p.flags.has("require-critique") && opt(p, "critique-id"))
776
+ recordCritique({ ...p, positional: [dir], opts: { ...p.opts, id: [opt(p, "critique-id")], verdict: [opt(p, "critique-verdict", "pass")], summary: [opt(p, "critique-summary", opt(p, "summary"))] }, flags: p.flags });
777
+ if (learningRecords.length)
778
+ recordLearning({ ...p, positional: [dir], opts: { ...p.opts, status: [opt(p, "learning-status", "learned")], "record-json": opts(p, "learning-record-json"), summary: [opt(p, "learning-summary", opt(p, "summary"))] }, flags: p.flags });
779
+ if (opt(p, "release-decision")) {
780
+ recordRelease({ ...p, positional: [dir], opts: { ...p.opts, decision: [opt(p, "release-decision")], scope: [opt(p, "release-scope")], summary: [opt(p, "release-summary", opt(p, "summary"))], "gate-json": ['{"name":"merge","status":"pass","summary":"Dogfood release gate passed."}'], "evidence-ref": ["evidence.json"], "docs-json": [`{"status":"updated","summary":"Docs updated.","refs":["${opt(p, "release-doc-ref", "docs/workflow-usage-guide.md")}"]}`] }, flags: p.flags });
781
+ printJson({ release_decision: opt(p, "release-decision") });
782
+ return 0;
783
+ }
784
+ const stateStatus = verdict === "pass" ? "verified" : verdict === "fail" ? "failed" : "not_verified";
785
+ const handoff = loadJson(path.join(dir, "handoff.json"));
786
+ if (verdict === "fail") {
787
+ handoff.blockers = ["Required dogfood critique is not passing"];
788
+ writeJson(path.join(dir, "handoff.json"), handoff);
789
+ }
790
+ writeState(dir, taskSlugFor(dir, opt(p, "task-slug")), stateStatus, "verification", opt(p, "timestamp", now()), opt(p, "summary"), verdict === "pass" ? "continue" : "blocked");
791
+ printJson({ state_status: stateStatus });
792
+ return 0;
793
+ }
794
+ async function main() {
795
+ const p = parseArgs(process.argv.slice(2));
796
+ if (!p.command)
797
+ die("workflow-sidecar command is required");
798
+ const lockRoot = ["ensure-session", "current", "dogfood-pass"].includes(p.command) ? path.resolve(opt(p, "artifact-root", ".flow-agents")) : p.command === "record-agent-event" ? explicitArtifactRoot(p) : p.positional[0] ? artifactDirFrom(p.positional[0]) : "";
799
+ return withLock(lockRoot, ["ensure-session", "record-agent-event", "dogfood-pass"].includes(p.command), p.command, () => {
800
+ switch (p.command) {
801
+ case "ensure-session": return ensureSession(p);
802
+ case "current": return current(p);
803
+ case "record-agent-event": return recordAgentEvent(p);
804
+ case "init-plan": return initPlan(p);
805
+ case "record-evidence": return recordEvidence(p);
806
+ case "advance-state": return advanceState(p);
807
+ case "record-critique": return recordCritique(p);
808
+ case "import-critique": return importCritique(p);
809
+ case "record-release": return recordRelease(p);
810
+ case "record-learning": return recordLearning(p);
811
+ case "dogfood-pass": return dogfoodPass(p);
812
+ default: die(`unknown command: ${p.command}`);
813
+ }
814
+ });
815
+ }
816
+ main().then((code) => process.exit(code)).catch((error) => { console.error(error instanceof Error ? error.message : String(error)); process.exit(1); });