@nathapp/nax 0.18.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 (459) hide show
  1. package/.gitlab-ci.yml +96 -0
  2. package/BRIEF.md +140 -0
  3. package/CHANGELOG.md +60 -0
  4. package/CLAUDE.md +159 -0
  5. package/README.md +373 -0
  6. package/US-007-IMPLEMENTATION.md +139 -0
  7. package/bin/nax.ts +930 -0
  8. package/biome.json +14 -0
  9. package/bun.lock +168 -0
  10. package/bunfig.toml +11 -0
  11. package/docs/20260216-fix-plan-context-review.md +56 -0
  12. package/docs/20260216-relentless-vs-ngent-comparison.md +208 -0
  13. package/docs/20260216-v02-plan.md +136 -0
  14. package/docs/20260216-v02-review.md +685 -0
  15. package/docs/20260217-dogfood-findings.md +56 -0
  16. package/docs/20260217-p2-plus-plan.md +117 -0
  17. package/docs/20260217-partial-fixes-plan.md +62 -0
  18. package/docs/20260217-plan-analyze-spec.md +117 -0
  19. package/docs/20260217-post-impl-review.md +1137 -0
  20. package/docs/20260217-quick-wins-plan.md +66 -0
  21. package/docs/20260217-split-runner-plan.md +75 -0
  22. package/docs/20260217-v03-impl-plan.md +80 -0
  23. package/docs/20260217-v03-post-impl-review.md +589 -0
  24. package/docs/20260217-v04-impl-plan.md +86 -0
  25. package/docs/20260217-v05-post-impl-review.md +850 -0
  26. package/docs/20260217-v06-post-impl-review.md +817 -0
  27. package/docs/20260218-adr003-port-plan.md +151 -0
  28. package/docs/20260218-review-adr003-verification.md +175 -0
  29. package/docs/20260219-fix-plan-bug16-19.md +79 -0
  30. package/docs/20260219-fix-plan-bug20-22.md +114 -0
  31. package/docs/20260219-plan-llm-routing.md +116 -0
  32. package/docs/20260219-review-bug20-22-fixes.md +135 -0
  33. package/docs/20260219-routing-baseline-keyword.md +63 -0
  34. package/docs/20260220-plan-structured-logging-p1.md +80 -0
  35. package/docs/20260220-plan-structured-logging-p2.md +37 -0
  36. package/docs/20260220-review-llm-routing.md +180 -0
  37. package/docs/20260220-review-post-fix-llm-routing.md +70 -0
  38. package/docs/20260221-fix-plan-relevantfiles-split.md +101 -0
  39. package/docs/20260221-fix-plan-routing-mode.md +125 -0
  40. package/docs/20260221-review-v0.9-implementation.md +379 -0
  41. package/docs/20260222-fix-plan-v091-routing-isolation.md +197 -0
  42. package/docs/20260223-fix-plan-prompt-audit.md +62 -0
  43. package/docs/20260224-nax-roadmap-phases.md +189 -0
  44. package/docs/20260225-phase2-llm-service-layer.md +401 -0
  45. package/docs/20260225-review-v0.10.1.md +187 -0
  46. package/docs/20260303-v010-implementation-plan.md +165 -0
  47. package/docs/CLAUDE.md.bak +191 -0
  48. package/docs/ROADMAP.md +165 -0
  49. package/docs/SPEC-rectification.md +0 -0
  50. package/docs/SPEC.md +324 -0
  51. package/docs/US-001-plugin-loading-verification.md +152 -0
  52. package/docs/architecture-analysis.md +1076 -0
  53. package/docs/bugs/BUG-21-escalation-null-attempts.md +48 -0
  54. package/docs/bugs-from-dogfood-run-c.md +243 -0
  55. package/docs/code-review-20260228.md +612 -0
  56. package/docs/code-review-v0.15.0.md +629 -0
  57. package/docs/hook-lifecycle-test-plan.md +149 -0
  58. package/docs/releases/v0.11.0-and-earlier.md +20 -0
  59. package/docs/releases/v0.12.0.md +15 -0
  60. package/docs/releases/v0.13.0.md +14 -0
  61. package/docs/releases/v0.14.0.md +20 -0
  62. package/docs/releases/v0.14.1.md +36 -0
  63. package/docs/releases/v0.14.2.md +51 -0
  64. package/docs/releases/v0.14.3.md +174 -0
  65. package/docs/releases/v0.14.4.md +94 -0
  66. package/docs/releases/v0.15.0.md +502 -0
  67. package/docs/releases/v0.15.1.md +170 -0
  68. package/docs/releases/v0.15.3.md +193 -0
  69. package/docs/specs/status-file-v0.10.1.md +812 -0
  70. package/docs/v0.10-global-config.md +206 -0
  71. package/docs/v0.10-plugin-system.md +415 -0
  72. package/docs/v0.10-prompt-optimizer.md +234 -0
  73. package/docs/v0.3-spec.md +244 -0
  74. package/docs/v0.4-spec.md +140 -0
  75. package/docs/v0.5-spec.md +237 -0
  76. package/docs/v0.6-spec.md +371 -0
  77. package/docs/v0.7-spec.md +177 -0
  78. package/docs/v0.8-llm-routing.md +206 -0
  79. package/docs/v0.8-structured-logging.md +132 -0
  80. package/docs/v0.9.3-prompt-audit.md +112 -0
  81. package/examples/plugins/console-reporter/index.test.ts +207 -0
  82. package/examples/plugins/console-reporter/index.ts +110 -0
  83. package/nax/config.json +147 -0
  84. package/nax/features/bugfix-v0171/prd.json +52 -0
  85. package/nax/features/config-management/prd.json +108 -0
  86. package/nax/features/config-management/progress.txt +5 -0
  87. package/nax/features/diagnose/acceptance.test.ts +412 -0
  88. package/nax/features/diagnose/prd.json +41 -0
  89. package/nax/features/orchestration-fixes/prd.json +89 -0
  90. package/nax/features/orchestration-fixes/progress.txt +1 -0
  91. package/nax/features/plugin-integration/US-007-VERIFICATION.md +259 -0
  92. package/nax/features/plugin-integration/prd.json +208 -0
  93. package/nax/features/plugin-integration/progress.txt +5 -0
  94. package/nax/features/precheck/prd.json +205 -0
  95. package/nax/features/precheck/progress.txt +15 -0
  96. package/nax/features/structured-logging/prd.json +199 -0
  97. package/nax/features/unlock/prd.json +36 -0
  98. package/package.json +47 -0
  99. package/src/acceptance/fix-generator.ts +348 -0
  100. package/src/acceptance/generator.ts +282 -0
  101. package/src/acceptance/index.ts +30 -0
  102. package/src/acceptance/types.ts +79 -0
  103. package/src/agents/claude-decompose.ts +169 -0
  104. package/src/agents/claude-plan.ts +139 -0
  105. package/src/agents/claude.ts +324 -0
  106. package/src/agents/cost.ts +268 -0
  107. package/src/agents/index.ts +13 -0
  108. package/src/agents/registry.ts +48 -0
  109. package/src/agents/types-extended.ts +133 -0
  110. package/src/agents/types.ts +113 -0
  111. package/src/agents/validation.ts +69 -0
  112. package/src/analyze/classifier.ts +305 -0
  113. package/src/analyze/index.ts +16 -0
  114. package/src/analyze/scanner.ts +175 -0
  115. package/src/analyze/types.ts +51 -0
  116. package/src/cli/accept.ts +108 -0
  117. package/src/cli/analyze-parser.ts +284 -0
  118. package/src/cli/analyze.ts +207 -0
  119. package/src/cli/config.ts +561 -0
  120. package/src/cli/constitution.ts +109 -0
  121. package/src/cli/diagnose-analysis.ts +159 -0
  122. package/src/cli/diagnose-formatter.ts +87 -0
  123. package/src/cli/diagnose.ts +203 -0
  124. package/src/cli/generate.ts +127 -0
  125. package/src/cli/index.ts +37 -0
  126. package/src/cli/init.ts +188 -0
  127. package/src/cli/interact.ts +295 -0
  128. package/src/cli/plan.ts +198 -0
  129. package/src/cli/plugins.ts +111 -0
  130. package/src/cli/prompts.ts +295 -0
  131. package/src/cli/runs.ts +174 -0
  132. package/src/cli/status-cost.ts +151 -0
  133. package/src/cli/status-features.ts +338 -0
  134. package/src/cli/status.ts +13 -0
  135. package/src/commands/common.ts +171 -0
  136. package/src/commands/diagnose.ts +17 -0
  137. package/src/commands/index.ts +8 -0
  138. package/src/commands/logs.ts +384 -0
  139. package/src/commands/precheck.ts +86 -0
  140. package/src/commands/unlock.ts +96 -0
  141. package/src/config/defaults.ts +160 -0
  142. package/src/config/index.ts +22 -0
  143. package/src/config/loader.ts +121 -0
  144. package/src/config/merger.ts +147 -0
  145. package/src/config/path-security.ts +121 -0
  146. package/src/config/paths.ts +27 -0
  147. package/src/config/schema.ts +56 -0
  148. package/src/config/schemas.ts +286 -0
  149. package/src/config/types.ts +423 -0
  150. package/src/config/validate.ts +103 -0
  151. package/src/constitution/generator.ts +191 -0
  152. package/src/constitution/generators/aider.ts +41 -0
  153. package/src/constitution/generators/claude.ts +35 -0
  154. package/src/constitution/generators/cursor.ts +36 -0
  155. package/src/constitution/generators/opencode.ts +38 -0
  156. package/src/constitution/generators/types.ts +33 -0
  157. package/src/constitution/generators/windsurf.ts +36 -0
  158. package/src/constitution/index.ts +10 -0
  159. package/src/constitution/loader.ts +133 -0
  160. package/src/constitution/types.ts +31 -0
  161. package/src/context/auto-detect.ts +227 -0
  162. package/src/context/builder.ts +246 -0
  163. package/src/context/elements.ts +83 -0
  164. package/src/context/formatter.ts +107 -0
  165. package/src/context/generator.ts +129 -0
  166. package/src/context/generators/aider.ts +34 -0
  167. package/src/context/generators/claude.ts +28 -0
  168. package/src/context/generators/cursor.ts +28 -0
  169. package/src/context/generators/opencode.ts +30 -0
  170. package/src/context/generators/windsurf.ts +28 -0
  171. package/src/context/greenfield.ts +114 -0
  172. package/src/context/index.ts +33 -0
  173. package/src/context/injector.ts +279 -0
  174. package/src/context/test-scanner.ts +370 -0
  175. package/src/context/types.ts +98 -0
  176. package/src/errors.ts +67 -0
  177. package/src/execution/batching.ts +157 -0
  178. package/src/execution/crash-recovery.ts +373 -0
  179. package/src/execution/escalation/escalation.ts +44 -0
  180. package/src/execution/escalation/index.ts +13 -0
  181. package/src/execution/escalation/tier-escalation.ts +295 -0
  182. package/src/execution/escalation/tier-outcome.ts +158 -0
  183. package/src/execution/helpers.ts +38 -0
  184. package/src/execution/index.ts +45 -0
  185. package/src/execution/lifecycle/acceptance-loop.ts +272 -0
  186. package/src/execution/lifecycle/headless-formatter.ts +85 -0
  187. package/src/execution/lifecycle/index.ts +12 -0
  188. package/src/execution/lifecycle/parallel-lifecycle.ts +101 -0
  189. package/src/execution/lifecycle/precheck-runner.ts +140 -0
  190. package/src/execution/lifecycle/run-cleanup.ts +81 -0
  191. package/src/execution/lifecycle/run-completion.ts +129 -0
  192. package/src/execution/lifecycle/run-initialization.ts +141 -0
  193. package/src/execution/lifecycle/run-lifecycle.ts +312 -0
  194. package/src/execution/lifecycle/run-setup.ts +204 -0
  195. package/src/execution/lifecycle/story-hooks.ts +38 -0
  196. package/src/execution/lifecycle/story-size-prompts.ts +123 -0
  197. package/src/execution/lock.ts +115 -0
  198. package/src/execution/parallel-executor.ts +216 -0
  199. package/src/execution/parallel.ts +400 -0
  200. package/src/execution/pid-registry.ts +280 -0
  201. package/src/execution/pipeline-result-handler.ts +388 -0
  202. package/src/execution/post-verify-rectification.ts +188 -0
  203. package/src/execution/post-verify.ts +274 -0
  204. package/src/execution/progress.ts +25 -0
  205. package/src/execution/prompts.ts +127 -0
  206. package/src/execution/queue-handler.ts +109 -0
  207. package/src/execution/rectification.ts +13 -0
  208. package/src/execution/runner.ts +377 -0
  209. package/src/execution/sequential-executor.ts +388 -0
  210. package/src/execution/status-file.ts +264 -0
  211. package/src/execution/status-writer.ts +139 -0
  212. package/src/execution/story-context.ts +229 -0
  213. package/src/execution/test-output-parser.ts +14 -0
  214. package/src/execution/verification.ts +72 -0
  215. package/src/hooks/index.ts +2 -0
  216. package/src/hooks/runner.ts +286 -0
  217. package/src/hooks/types.ts +67 -0
  218. package/src/interaction/chain.ts +154 -0
  219. package/src/interaction/index.ts +60 -0
  220. package/src/interaction/init.ts +83 -0
  221. package/src/interaction/plugins/auto.ts +217 -0
  222. package/src/interaction/plugins/cli.ts +300 -0
  223. package/src/interaction/plugins/telegram.ts +384 -0
  224. package/src/interaction/plugins/webhook.ts +258 -0
  225. package/src/interaction/state.ts +171 -0
  226. package/src/interaction/triggers.ts +229 -0
  227. package/src/interaction/types.ts +163 -0
  228. package/src/logger/formatters.ts +84 -0
  229. package/src/logger/index.ts +16 -0
  230. package/src/logger/logger.ts +298 -0
  231. package/src/logger/types.ts +48 -0
  232. package/src/logging/formatter.ts +355 -0
  233. package/src/logging/index.ts +22 -0
  234. package/src/logging/types.ts +93 -0
  235. package/src/metrics/aggregator.ts +190 -0
  236. package/src/metrics/index.ts +14 -0
  237. package/src/metrics/tracker.ts +200 -0
  238. package/src/metrics/types.ts +109 -0
  239. package/src/optimizer/index.ts +62 -0
  240. package/src/optimizer/noop.optimizer.ts +24 -0
  241. package/src/optimizer/rule-based.optimizer.ts +248 -0
  242. package/src/optimizer/types.ts +53 -0
  243. package/src/pipeline/events.ts +130 -0
  244. package/src/pipeline/index.ts +19 -0
  245. package/src/pipeline/runner.ts +161 -0
  246. package/src/pipeline/stages/acceptance.ts +197 -0
  247. package/src/pipeline/stages/completion.ts +99 -0
  248. package/src/pipeline/stages/constitution.ts +63 -0
  249. package/src/pipeline/stages/context.ts +117 -0
  250. package/src/pipeline/stages/execution.ts +194 -0
  251. package/src/pipeline/stages/index.ts +62 -0
  252. package/src/pipeline/stages/optimizer.ts +74 -0
  253. package/src/pipeline/stages/prompt.ts +57 -0
  254. package/src/pipeline/stages/queue-check.ts +103 -0
  255. package/src/pipeline/stages/review.ts +181 -0
  256. package/src/pipeline/stages/routing.ts +81 -0
  257. package/src/pipeline/stages/verify.ts +100 -0
  258. package/src/pipeline/types.ts +167 -0
  259. package/src/plugins/index.ts +31 -0
  260. package/src/plugins/loader.ts +287 -0
  261. package/src/plugins/registry.ts +168 -0
  262. package/src/plugins/types.ts +327 -0
  263. package/src/plugins/validator.ts +352 -0
  264. package/src/prd/index.ts +172 -0
  265. package/src/prd/types.ts +202 -0
  266. package/src/precheck/checks-blockers.ts +391 -0
  267. package/src/precheck/checks-warnings.ts +142 -0
  268. package/src/precheck/checks.ts +30 -0
  269. package/src/precheck/index.ts +247 -0
  270. package/src/precheck/story-size-gate.ts +144 -0
  271. package/src/precheck/types.ts +31 -0
  272. package/src/queue/index.ts +2 -0
  273. package/src/queue/manager.ts +254 -0
  274. package/src/queue/types.ts +54 -0
  275. package/src/review/index.ts +8 -0
  276. package/src/review/runner.ts +172 -0
  277. package/src/review/types.ts +66 -0
  278. package/src/routing/builder.ts +81 -0
  279. package/src/routing/chain.ts +74 -0
  280. package/src/routing/index.ts +16 -0
  281. package/src/routing/loader.ts +58 -0
  282. package/src/routing/router.ts +303 -0
  283. package/src/routing/strategies/adaptive.ts +215 -0
  284. package/src/routing/strategies/index.ts +8 -0
  285. package/src/routing/strategies/keyword.ts +163 -0
  286. package/src/routing/strategies/llm-prompts.ts +209 -0
  287. package/src/routing/strategies/llm.ts +235 -0
  288. package/src/routing/strategies/manual.ts +50 -0
  289. package/src/routing/strategy.ts +99 -0
  290. package/src/tdd/cleanup.ts +111 -0
  291. package/src/tdd/index.ts +23 -0
  292. package/src/tdd/isolation.ts +123 -0
  293. package/src/tdd/orchestrator.ts +383 -0
  294. package/src/tdd/prompts.ts +270 -0
  295. package/src/tdd/rectification-gate.ts +183 -0
  296. package/src/tdd/session-runner.ts +179 -0
  297. package/src/tdd/types.ts +81 -0
  298. package/src/tdd/verdict.ts +271 -0
  299. package/src/tui/App.tsx +265 -0
  300. package/src/tui/components/AgentPanel.tsx +75 -0
  301. package/src/tui/components/CostOverlay.tsx +118 -0
  302. package/src/tui/components/HelpOverlay.tsx +107 -0
  303. package/src/tui/components/StatusBar.tsx +63 -0
  304. package/src/tui/components/StoriesPanel.tsx +177 -0
  305. package/src/tui/hooks/useKeyboard.ts +142 -0
  306. package/src/tui/hooks/useLayout.ts +137 -0
  307. package/src/tui/hooks/usePipelineEvents.ts +183 -0
  308. package/src/tui/hooks/usePty.ts +194 -0
  309. package/src/tui/index.tsx +38 -0
  310. package/src/tui/types.ts +76 -0
  311. package/src/utils/git.ts +83 -0
  312. package/src/utils/queue-writer.ts +54 -0
  313. package/src/verification/executor.ts +235 -0
  314. package/src/verification/gate.ts +207 -0
  315. package/src/verification/index.ts +12 -0
  316. package/src/verification/parser.ts +230 -0
  317. package/src/verification/rectification.ts +108 -0
  318. package/src/verification/types.ts +113 -0
  319. package/src/worktree/dispatcher.ts +65 -0
  320. package/src/worktree/index.ts +2 -0
  321. package/src/worktree/manager.ts +187 -0
  322. package/src/worktree/merge.ts +301 -0
  323. package/src/worktree/types.ts +4 -0
  324. package/test/TEST_COVERAGE_US001.md +217 -0
  325. package/test/TEST_COVERAGE_US003.md +84 -0
  326. package/test/TEST_COVERAGE_US005.md +86 -0
  327. package/test/US-002-orchestrator.test.ts +246 -0
  328. package/test/acceptance/cm-003-default-view.test.ts +194 -0
  329. package/test/execution/pid-registry.test.ts +240 -0
  330. package/test/execution/post-verify.test.ts +224 -0
  331. package/test/helpers/timeout.ts +42 -0
  332. package/test/integration/US-002-TEST-SUMMARY.md +107 -0
  333. package/test/integration/US-003-TEST-SUMMARY.md +149 -0
  334. package/test/integration/US-004-TEST-SUMMARY.md +106 -0
  335. package/test/integration/US-005-TEST-SUMMARY.md +138 -0
  336. package/test/integration/US-007-TEST-SUMMARY.md +100 -0
  337. package/test/integration/agent-validation.test.ts +439 -0
  338. package/test/integration/analyze-integration.test.ts +261 -0
  339. package/test/integration/analyze-scanner.test.ts +131 -0
  340. package/test/integration/cli-config-default-edge-cases.test.ts +222 -0
  341. package/test/integration/cli-config-default-view.test.ts +229 -0
  342. package/test/integration/cli-config-diff.test.ts +460 -0
  343. package/test/integration/cli-config.test.ts +736 -0
  344. package/test/integration/cli-diagnose.test.ts +592 -0
  345. package/test/integration/cli-logs.test.ts +314 -0
  346. package/test/integration/cli-plugins.test.ts +678 -0
  347. package/test/integration/cli-precheck.test.ts +371 -0
  348. package/test/integration/cli-run-headless.test.ts +173 -0
  349. package/test/integration/cli.test.ts +75 -0
  350. package/test/integration/config/merger.test.ts +465 -0
  351. package/test/integration/config/paths.test.ts +51 -0
  352. package/test/integration/config-loader.test.ts +265 -0
  353. package/test/integration/config.test.ts +444 -0
  354. package/test/integration/context-integration.test.ts +702 -0
  355. package/test/integration/context-provider-injection.test.ts +506 -0
  356. package/test/integration/context-verification-integration.test.ts +295 -0
  357. package/test/integration/e2e.test.ts +896 -0
  358. package/test/integration/execution.test.ts +625 -0
  359. package/test/integration/helpers.test.ts +295 -0
  360. package/test/integration/hooks.test.ts +361 -0
  361. package/test/integration/interaction-chain-pipeline.test.ts +464 -0
  362. package/test/integration/isolation.test.ts +143 -0
  363. package/test/integration/logger.test.ts +461 -0
  364. package/test/integration/parallel.test.ts +250 -0
  365. package/test/integration/path-security.test.ts +173 -0
  366. package/test/integration/pipeline-acceptance.test.ts +302 -0
  367. package/test/integration/pipeline-events.test.ts +475 -0
  368. package/test/integration/pipeline.test.ts +658 -0
  369. package/test/integration/plan.test.ts +157 -0
  370. package/test/integration/plugin-routing.test.ts +921 -0
  371. package/test/integration/plugins/config-integration.test.ts +172 -0
  372. package/test/integration/plugins/config-resolution.test.ts +522 -0
  373. package/test/integration/plugins/loader.test.ts +641 -0
  374. package/test/integration/plugins/registry.test.ts +746 -0
  375. package/test/integration/plugins/validator.test.ts +563 -0
  376. package/test/integration/prd-pause.test.ts +205 -0
  377. package/test/integration/prd-resolvers.test.ts +185 -0
  378. package/test/integration/precheck-integration.test.ts +468 -0
  379. package/test/integration/precheck.test.ts +805 -0
  380. package/test/integration/progress.test.ts +34 -0
  381. package/test/integration/rectification-flow.test.ts +512 -0
  382. package/test/integration/reporter-lifecycle.test.ts +860 -0
  383. package/test/integration/review-config-commands.test.ts +319 -0
  384. package/test/integration/review-config-schema.test.ts +116 -0
  385. package/test/integration/review-plugin-integration.test.ts +722 -0
  386. package/test/integration/review.test.ts +149 -0
  387. package/test/integration/routing-stage-bug-021.test.ts +274 -0
  388. package/test/integration/routing-stage-greenfield.test.ts +286 -0
  389. package/test/integration/runner-config-plugins.test.ts +461 -0
  390. package/test/integration/runner-fixes.test.ts +399 -0
  391. package/test/integration/runner-plugin-integration.test.ts +543 -0
  392. package/test/integration/runner.test.ts +1679 -0
  393. package/test/integration/s5-greenfield-fallback.test.ts +297 -0
  394. package/test/integration/status-file-integration.test.ts +325 -0
  395. package/test/integration/status-file.test.ts +379 -0
  396. package/test/integration/status-writer.test.ts +345 -0
  397. package/test/integration/story-id-in-events.test.ts +273 -0
  398. package/test/integration/tdd-cleanup.test.ts +246 -0
  399. package/test/integration/tdd-orchestrator.test.ts +1762 -0
  400. package/test/integration/test-scanner.test.ts +403 -0
  401. package/test/integration/verification-asset-check.test.ts +142 -0
  402. package/test/integration/verify-stage.test.ts +275 -0
  403. package/test/integration/worktree/manager.test.ts +218 -0
  404. package/test/integration/worktree/merge.test.ts +341 -0
  405. package/test/manual/logging-formatter-demo.ts +158 -0
  406. package/test/ui/tui-agent-panel.test.tsx +99 -0
  407. package/test/ui/tui-controls.test.ts +334 -0
  408. package/test/ui/tui-cost-and-pty.test.ts +189 -0
  409. package/test/ui/tui-layout.test.ts +378 -0
  410. package/test/ui/tui-pty-integration.test.tsx +159 -0
  411. package/test/ui/tui-stories.test.ts +332 -0
  412. package/test/unit/acceptance.test.ts +186 -0
  413. package/test/unit/agent-stderr-capture.test.ts +146 -0
  414. package/test/unit/analyze-classifier.test.ts +215 -0
  415. package/test/unit/analyze.test.ts +224 -0
  416. package/test/unit/auto-detect.test.ts +249 -0
  417. package/test/unit/cli-status.test.ts +417 -0
  418. package/test/unit/commands/common.test.ts +320 -0
  419. package/test/unit/commands/logs.test.ts +416 -0
  420. package/test/unit/commands/unlock.test.ts +319 -0
  421. package/test/unit/constitution-generators.test.ts +160 -0
  422. package/test/unit/constitution.test.ts +209 -0
  423. package/test/unit/context.test.ts +1722 -0
  424. package/test/unit/cost.test.ts +231 -0
  425. package/test/unit/crash-recovery.test.ts +308 -0
  426. package/test/unit/escalation.test.ts +126 -0
  427. package/test/unit/execution-logging-stderr.test.ts +156 -0
  428. package/test/unit/execution-stage.test.ts +122 -0
  429. package/test/unit/fix-generator.test.ts +275 -0
  430. package/test/unit/formatters.test.ts +469 -0
  431. package/test/unit/greenfield.test.ts +179 -0
  432. package/test/unit/helpers.test.ts +317 -0
  433. package/test/unit/interaction/human-review-trigger.test.ts +164 -0
  434. package/test/unit/interaction-network-failures.test.ts +389 -0
  435. package/test/unit/interaction-plugins.test.ts +164 -0
  436. package/test/unit/isolation.test.ts +134 -0
  437. package/test/unit/logging/formatter.test.ts +455 -0
  438. package/test/unit/merge.test.ts +268 -0
  439. package/test/unit/metrics.test.ts +276 -0
  440. package/test/unit/optimizer/noop.optimizer.test.ts +125 -0
  441. package/test/unit/optimizer/rule-based.optimizer.test.ts +358 -0
  442. package/test/unit/prd-auto-default.test.ts +290 -0
  443. package/test/unit/prd-failure-category.test.ts +176 -0
  444. package/test/unit/prd-get-next-story.test.ts +186 -0
  445. package/test/unit/precheck-checks.test.ts +840 -0
  446. package/test/unit/precheck-story-size-gate.test.ts +287 -0
  447. package/test/unit/precheck-types.test.ts +142 -0
  448. package/test/unit/prompts.test.ts +475 -0
  449. package/test/unit/queue.test.ts +237 -0
  450. package/test/unit/rectification.test.ts +284 -0
  451. package/test/unit/registry.test.ts +287 -0
  452. package/test/unit/routing.test.ts +937 -0
  453. package/test/unit/run-lifecycle.test.ts +140 -0
  454. package/test/unit/storyid-events.test.ts +224 -0
  455. package/test/unit/tdd-verdict.test.ts +492 -0
  456. package/test/unit/test-output-parser.test.ts +377 -0
  457. package/test/unit/verdict.test.ts +324 -0
  458. package/test/unit/worktree-manager.test.ts +158 -0
  459. package/tsconfig.json +27 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Pipeline Stages
3
+ *
4
+ * Composable stages for the execution pipeline.
5
+ * Each stage performs a specific step in story execution.
6
+ */
7
+
8
+ import type { PipelineStage } from "../types";
9
+ import { acceptanceStage } from "./acceptance";
10
+ import { completionStage } from "./completion";
11
+ import { constitutionStage } from "./constitution";
12
+ import { contextStage } from "./context";
13
+ import { executionStage } from "./execution";
14
+ import { optimizerStage } from "./optimizer";
15
+ import { promptStage } from "./prompt";
16
+ import { queueCheckStage } from "./queue-check";
17
+ import { reviewStage } from "./review";
18
+ import { routingStage } from "./routing";
19
+ import { verifyStage } from "./verify";
20
+
21
+ /**
22
+ * Default pipeline stages in execution order.
23
+ *
24
+ * This is the standard pipeline for executing a story:
25
+ * 1. Check for queue commands (PAUSE/ABORT/SKIP)
26
+ * 2. Route (classify complexity → model tier)
27
+ * 3. Load constitution (project coding standards)
28
+ * 4. Build context (gather relevant code/docs)
29
+ * 5. Assemble prompt (story + context + constitution)
30
+ * 6. Optimize prompt (reduce token usage)
31
+ * 7. Execute agent session (TDD or test-after)
32
+ * 8. Verify output (tests pass, build succeeds)
33
+ * 9. Review (quality checks, linting, etc.)
34
+ * 10. Mark complete (save PRD, fire hooks, log progress)
35
+ * 11. Acceptance (run acceptance tests when all stories complete)
36
+ */
37
+ export const defaultPipeline: PipelineStage[] = [
38
+ queueCheckStage,
39
+ routingStage,
40
+ constitutionStage,
41
+ contextStage,
42
+ promptStage,
43
+ optimizerStage,
44
+ executionStage,
45
+ verifyStage,
46
+ reviewStage,
47
+ completionStage,
48
+ acceptanceStage,
49
+ ];
50
+
51
+ // Re-export individual stages for custom pipeline construction
52
+ export { queueCheckStage } from "./queue-check";
53
+ export { routingStage } from "./routing";
54
+ export { constitutionStage } from "./constitution";
55
+ export { contextStage } from "./context";
56
+ export { promptStage } from "./prompt";
57
+ export { optimizerStage } from "./optimizer";
58
+ export { executionStage } from "./execution";
59
+ export { verifyStage } from "./verify";
60
+ export { reviewStage } from "./review";
61
+ export { completionStage } from "./completion";
62
+ export { acceptanceStage } from "./acceptance";
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Optimizer Stage
3
+ *
4
+ * Optimizes the assembled prompt to reduce token usage while preserving
5
+ * semantic meaning. Runs between prompt assembly and agent execution.
6
+ *
7
+ * Resolution order for optimizer selection:
8
+ * 1. Plugin-provided optimizer (if plugins loaded)
9
+ * 2. Built-in strategy from config (rule-based, noop)
10
+ * 3. Fallback to NoopOptimizer
11
+ *
12
+ * @returns
13
+ * - `continue`: Optimization complete (or skipped if disabled)
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * // With rule-based optimizer enabled
18
+ * await optimizerStage.execute(ctx);
19
+ * // ctx.prompt: optimized, whitespace stripped, criteria compacted
20
+ * // Logs: "optimizer: rule-based: 15% savings"
21
+ *
22
+ * // With optimizer disabled
23
+ * await optimizerStage.execute(ctx);
24
+ * // ctx.prompt: unchanged (passthrough)
25
+ * // Logs: "optimizer: noop: 0% savings"
26
+ * ```
27
+ */
28
+
29
+ import { getLogger } from "../../logger/index.js";
30
+ import { resolveOptimizer } from "../../optimizer/index.js";
31
+ import type { PipelineContext, PipelineStage, StageResult } from "../types.js";
32
+
33
+ export const optimizerStage: PipelineStage = {
34
+ name: "optimizer",
35
+ enabled: (ctx) => {
36
+ // Always enabled - NoopOptimizer is used when optimization is disabled
37
+ return true;
38
+ },
39
+
40
+ async execute(ctx: PipelineContext): Promise<StageResult> {
41
+ const logger = getLogger();
42
+
43
+ // Ensure prompt exists
44
+ if (!ctx.prompt) {
45
+ logger.warn("optimizer", "No prompt to optimize, skipping");
46
+ return { action: "continue" };
47
+ }
48
+
49
+ // Resolve optimizer (checks plugins first, then config)
50
+ const optimizer = resolveOptimizer(ctx.config, ctx.plugins);
51
+
52
+ // Optimize the prompt
53
+ const result = await optimizer.optimize({
54
+ prompt: ctx.prompt,
55
+ stories: ctx.stories,
56
+ contextMarkdown: ctx.contextMarkdown,
57
+ config: ctx.config,
58
+ });
59
+
60
+ // Update context with optimized prompt
61
+ ctx.prompt = result.prompt;
62
+
63
+ // Log optimization results
64
+ const savingsPercent = Math.round(result.savings * 100);
65
+ logger.info("optimizer", `${optimizer.name}: ${savingsPercent}% savings`, {
66
+ originalTokens: result.originalTokens,
67
+ optimizedTokens: result.optimizedTokens,
68
+ tokensSaved: result.originalTokens - result.optimizedTokens,
69
+ appliedRules: result.appliedRules,
70
+ });
71
+
72
+ return { action: "continue" };
73
+ },
74
+ };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Prompt Stage
3
+ *
4
+ * Assembles the final prompt for the agent from:
5
+ * - Story/stories (batch or single)
6
+ * - Context markdown
7
+ * - Constitution content
8
+ *
9
+ * @returns
10
+ * - `continue`: Prompt built successfully
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * // Single story with constitution
15
+ * await promptStage.execute(ctx);
16
+ * // ctx.prompt: "# CONSTITUTION\n...\n\n# Task: Add login button\n..."
17
+ *
18
+ * // Batch of stories without constitution
19
+ * await promptStage.execute(ctx);
20
+ * // ctx.prompt: "# Batch Task: 3 Stories\n## Story 1: US-001...\n"
21
+ * ```
22
+ */
23
+
24
+ import { buildBatchPrompt, buildSingleSessionPrompt } from "../../execution/prompts";
25
+ import { getLogger } from "../../logger";
26
+ import type { PipelineContext, PipelineStage, StageResult } from "../types";
27
+
28
+ export const promptStage: PipelineStage = {
29
+ name: "prompt",
30
+ enabled: (ctx) =>
31
+ ctx.routing.testStrategy !== "three-session-tdd" && ctx.routing.testStrategy !== "three-session-tdd-lite",
32
+
33
+ async execute(ctx: PipelineContext): Promise<StageResult> {
34
+ const logger = getLogger();
35
+ const isBatch = ctx.stories.length > 1;
36
+
37
+ const prompt = isBatch
38
+ ? buildBatchPrompt(ctx.stories, ctx.contextMarkdown, ctx.constitution)
39
+ : buildSingleSessionPrompt(ctx.story, ctx.contextMarkdown, ctx.constitution);
40
+
41
+ ctx.prompt = prompt;
42
+
43
+ if (isBatch) {
44
+ logger.info("prompt", "Batch session prepared", {
45
+ storyCount: ctx.stories.length,
46
+ testStrategy: "test-after",
47
+ });
48
+ } else {
49
+ logger.info("prompt", "Single session prepared", {
50
+ storyId: ctx.story.id,
51
+ testStrategy: "test-after",
52
+ });
53
+ }
54
+
55
+ return { action: "continue" };
56
+ },
57
+ };
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Queue Check Stage
3
+ *
4
+ * Checks for queue commands (PAUSE/ABORT/SKIP) before executing a story.
5
+ * Processes commands atomically and updates PRD accordingly.
6
+ */
7
+
8
+ import { clearQueueFile, readQueueFile } from "../../execution/queue-handler";
9
+ import { getLogger } from "../../logger";
10
+ import { markStorySkipped, savePRD } from "../../prd";
11
+ import type { PipelineContext, PipelineStage, StageResult } from "../types";
12
+
13
+ /**
14
+ * Queue Check Stage
15
+ *
16
+ * Checks for queue commands (PAUSE/ABORT/SKIP) before executing a story.
17
+ * If a command is found, processes it and returns appropriate action.
18
+ *
19
+ * @returns
20
+ * - `continue`: No queue commands, proceed
21
+ * - `pause`: PAUSE/ABORT command found, stop execution
22
+ * - `skip`: SKIP command removed all stories from batch
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * // User writes: echo "PAUSE" > .queue.txt
27
+ * const result = await queueCheckStage.execute(ctx);
28
+ * // result: { action: "pause", reason: "User requested pause via .queue.txt" }
29
+ * ```
30
+ */
31
+ export const queueCheckStage: PipelineStage = {
32
+ name: "queue-check",
33
+ enabled: () => true,
34
+
35
+ async execute(ctx: PipelineContext): Promise<StageResult> {
36
+ const logger = getLogger();
37
+ const queueCommands = await readQueueFile(ctx.workdir);
38
+
39
+ if (queueCommands.length === 0) {
40
+ return { action: "continue" };
41
+ }
42
+
43
+ for (const cmd of queueCommands) {
44
+ if (cmd.type === "PAUSE") {
45
+ logger.warn("queue", "Paused by user", { command: "PAUSE" });
46
+ await clearQueueFile(ctx.workdir);
47
+ return { action: "pause", reason: "User requested pause via .queue.txt" };
48
+ }
49
+
50
+ if (cmd.type === "ABORT") {
51
+ logger.warn("queue", "Aborting: marking remaining stories as skipped");
52
+
53
+ // Mark all pending stories as skipped
54
+ for (const s of ctx.prd.userStories) {
55
+ if (s.status === "pending") {
56
+ markStorySkipped(ctx.prd, s.id);
57
+ }
58
+ }
59
+
60
+ // Save PRD path from featureDir
61
+ const prdPath = ctx.featureDir ? `${ctx.featureDir}/prd.json` : `${ctx.workdir}/nax/features/unknown/prd.json`;
62
+ await savePRD(ctx.prd, prdPath);
63
+ await clearQueueFile(ctx.workdir);
64
+
65
+ return { action: "pause", reason: "User requested abort" };
66
+ }
67
+
68
+ if (cmd.type === "SKIP") {
69
+ // Check if this SKIP applies to any story in the current batch
70
+ const isTargeted = ctx.stories.some((s) => s.id === cmd.storyId);
71
+
72
+ if (isTargeted) {
73
+ logger.warn("queue", "Skipping story by user request", {
74
+ storyId: cmd.storyId,
75
+ });
76
+
77
+ // Mark as skipped in PRD
78
+ markStorySkipped(ctx.prd, cmd.storyId);
79
+
80
+ // Save PRD
81
+ const prdPath = ctx.featureDir
82
+ ? `${ctx.featureDir}/prd.json`
83
+ : `${ctx.workdir}/nax/features/unknown/prd.json`;
84
+ await savePRD(ctx.prd, prdPath);
85
+
86
+ // Remove from batch
87
+ ctx.stories = ctx.stories.filter((s) => s.id !== cmd.storyId);
88
+
89
+ // If batch is now empty, skip this iteration
90
+ if (ctx.stories.length === 0) {
91
+ await clearQueueFile(ctx.workdir);
92
+ return { action: "skip", reason: "All stories in batch were skipped" };
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ // Clear processed commands
99
+ await clearQueueFile(ctx.workdir);
100
+
101
+ return { action: "continue" };
102
+ },
103
+ };
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Review Stage
3
+ *
4
+ * Runs post-implementation review phase if enabled.
5
+ * Checks code quality, tests, linting, etc. via review module.
6
+ * After built-in checks, runs plugin reviewers if any are registered.
7
+ *
8
+ * @returns
9
+ * - `continue`: Review passed
10
+ * - `fail`: Review failed (hard failure)
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * // Review enabled and passes
15
+ * await reviewStage.execute(ctx);
16
+ * // ctx.reviewResult: { success: true, totalDurationMs: 1500, ... }
17
+ *
18
+ * // Review enabled but fails
19
+ * await reviewStage.execute(ctx);
20
+ * // Returns: { action: "fail", reason: "Review failed: typecheck errors" }
21
+ * ```
22
+ */
23
+
24
+ import { spawn } from "bun";
25
+ import { getLogger } from "../../logger";
26
+ import { runReview } from "../../review";
27
+ import type { PipelineContext, PipelineStage, StageResult } from "../types";
28
+
29
+ /**
30
+ * Get list of changed files from git.
31
+ * Includes both staged and unstaged changes.
32
+ *
33
+ * @param workdir - Working directory
34
+ * @returns Array of changed file paths
35
+ */
36
+ async function getChangedFiles(workdir: string): Promise<string[]> {
37
+ try {
38
+ // Get both staged and unstaged changes
39
+ const [stagedProc, unstagedProc] = [
40
+ spawn({
41
+ cmd: ["git", "diff", "--name-only", "--cached"],
42
+ cwd: workdir,
43
+ stdout: "pipe",
44
+ stderr: "pipe",
45
+ }),
46
+ spawn({
47
+ cmd: ["git", "diff", "--name-only"],
48
+ cwd: workdir,
49
+ stdout: "pipe",
50
+ stderr: "pipe",
51
+ }),
52
+ ];
53
+
54
+ await Promise.all([stagedProc.exited, unstagedProc.exited]);
55
+
56
+ const stagedFiles = (await new Response(stagedProc.stdout).text())
57
+ .trim()
58
+ .split("\n")
59
+ .filter((line) => line.length > 0);
60
+
61
+ const unstagedFiles = (await new Response(unstagedProc.stdout).text())
62
+ .trim()
63
+ .split("\n")
64
+ .filter((line) => line.length > 0);
65
+
66
+ // Combine and deduplicate
67
+ return Array.from(new Set([...stagedFiles, ...unstagedFiles]));
68
+ } catch {
69
+ return [];
70
+ }
71
+ }
72
+
73
+ export const reviewStage: PipelineStage = {
74
+ name: "review",
75
+ enabled: (ctx) => ctx.config.review.enabled,
76
+
77
+ async execute(ctx: PipelineContext): Promise<StageResult> {
78
+ const logger = getLogger();
79
+
80
+ logger.info("review", "Running review phase");
81
+
82
+ // Run built-in checks (typecheck, lint, test)
83
+ const reviewResult = await runReview(ctx.config.review, ctx.workdir, ctx.config.execution);
84
+ ctx.reviewResult = reviewResult;
85
+
86
+ // HARD FAILURE: Review failure means code quality gate not met
87
+ if (!reviewResult.success) {
88
+ logger.error("review", "Review failed (built-in checks)", {
89
+ reason: reviewResult.failureReason,
90
+ storyId: ctx.story.id,
91
+ });
92
+ return { action: "fail", reason: `Review failed: ${reviewResult.failureReason}` };
93
+ }
94
+
95
+ // Run plugin reviewers if any are registered
96
+ if (ctx.plugins) {
97
+ const pluginReviewers = ctx.plugins.getReviewers();
98
+ if (pluginReviewers.length > 0) {
99
+ logger.info("review", `Running ${pluginReviewers.length} plugin reviewer(s)`);
100
+
101
+ const changedFiles = await getChangedFiles(ctx.workdir);
102
+ const pluginReviewerResults: Array<{
103
+ name: string;
104
+ passed: boolean;
105
+ output: string;
106
+ exitCode?: number;
107
+ error?: string;
108
+ }> = [];
109
+
110
+ for (const reviewer of pluginReviewers) {
111
+ logger.info("review", `Running plugin reviewer: ${reviewer.name}`);
112
+ try {
113
+ const result = await reviewer.check(ctx.workdir, changedFiles);
114
+
115
+ // Capture result for debugging
116
+ pluginReviewerResults.push({
117
+ name: reviewer.name,
118
+ passed: result.passed,
119
+ output: result.output,
120
+ exitCode: result.exitCode,
121
+ });
122
+
123
+ if (!result.passed) {
124
+ logger.error("review", `Plugin reviewer failed: ${reviewer.name}`, {
125
+ output: result.output,
126
+ storyId: ctx.story.id,
127
+ });
128
+
129
+ // Store results in review result before failing
130
+ if (ctx.reviewResult) {
131
+ ctx.reviewResult.pluginReviewers = pluginReviewerResults;
132
+ }
133
+
134
+ return {
135
+ action: "fail",
136
+ reason: `Review failed: plugin reviewer '${reviewer.name}' failed`,
137
+ };
138
+ }
139
+
140
+ logger.info("review", `Plugin reviewer passed: ${reviewer.name}`);
141
+ } catch (error) {
142
+ const errorMsg = error instanceof Error ? error.message : String(error);
143
+ logger.error("review", `Plugin reviewer error: ${reviewer.name}`, {
144
+ error: errorMsg,
145
+ storyId: ctx.story.id,
146
+ });
147
+
148
+ // Capture error for debugging
149
+ pluginReviewerResults.push({
150
+ name: reviewer.name,
151
+ passed: false,
152
+ output: "",
153
+ error: errorMsg,
154
+ });
155
+
156
+ // Store results in review result before failing
157
+ if (ctx.reviewResult) {
158
+ ctx.reviewResult.pluginReviewers = pluginReviewerResults;
159
+ }
160
+
161
+ return {
162
+ action: "fail",
163
+ reason: `Review failed: plugin reviewer '${reviewer.name}' threw error`,
164
+ };
165
+ }
166
+ }
167
+
168
+ // Store successful plugin reviewer results
169
+ if (ctx.reviewResult) {
170
+ ctx.reviewResult.pluginReviewers = pluginReviewerResults;
171
+ }
172
+ }
173
+ }
174
+
175
+ logger.info("review", "Review passed", {
176
+ durationMs: reviewResult.totalDurationMs,
177
+ storyId: ctx.story.id,
178
+ });
179
+ return { action: "continue" };
180
+ },
181
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Routing Stage
3
+ *
4
+ * Classifies story complexity and determines model tier + test strategy.
5
+ * Uses cached complexity/testStrategy from story if available, but ALWAYS
6
+ * derives modelTier from current config (never cached).
7
+ *
8
+ * @returns
9
+ * - `continue`: Routing determined, proceed to next stage
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // Story has cached routing with complexity
14
+ * await routingStage.execute(ctx);
15
+ * // ctx.routing: { complexity: "simple", modelTier: "fast", testStrategy: "test-after", reasoning: "..." }
16
+ * // modelTier is derived from current config.autoMode.complexityRouting
17
+ * ```
18
+ */
19
+
20
+ import { isGreenfieldStory } from "../../context/greenfield";
21
+ import { getLogger } from "../../logger";
22
+ import { complexityToModelTier, routeStory } from "../../routing";
23
+ import { clearCache, routeBatch } from "../../routing/strategies/llm";
24
+ import type { PipelineContext, PipelineStage, RoutingResult, StageResult } from "../types";
25
+
26
+ export const routingStage: PipelineStage = {
27
+ name: "routing",
28
+ enabled: () => true,
29
+
30
+ async execute(ctx: PipelineContext): Promise<StageResult> {
31
+ const logger = getLogger();
32
+
33
+ // If story has cached routing, use it but re-derive modelTier from current config
34
+ // Otherwise, perform fresh classification
35
+ let routing: { complexity: string; testStrategy: string; modelTier: string; reasoning?: string };
36
+ if (ctx.story.routing) {
37
+ // Use cached complexity/testStrategy, but re-derive modelTier from current config
38
+ routing = await routeStory(ctx.story, { config: ctx.config }, ctx.workdir, ctx.plugins);
39
+ // Override with cached complexity if available
40
+ routing.complexity = ctx.story.routing.complexity;
41
+ routing.testStrategy = ctx.story.routing.testStrategy;
42
+ // Re-derive modelTier from cached complexity and current config
43
+ routing.modelTier = complexityToModelTier(routing.complexity as import("../../config").Complexity, ctx.config);
44
+ } else {
45
+ // Fresh classification
46
+ routing = await routeStory(ctx.story, { config: ctx.config }, ctx.workdir, ctx.plugins);
47
+ }
48
+
49
+ // BUG-010: Greenfield detection — force test-after if no test files exist
50
+ const greenfieldDetectionEnabled = ctx.config.tdd.greenfieldDetection ?? true;
51
+ if (greenfieldDetectionEnabled && routing.testStrategy.startsWith("three-session-tdd")) {
52
+ const isGreenfield = await isGreenfieldStory(ctx.story, ctx.workdir);
53
+ if (isGreenfield) {
54
+ logger.info("routing", "Greenfield detected — forcing test-after strategy", {
55
+ storyId: ctx.story.id,
56
+ originalStrategy: routing.testStrategy,
57
+ });
58
+ routing.testStrategy = "test-after";
59
+ routing.reasoning = `${routing.reasoning} [GREENFIELD OVERRIDE: No test files exist, using test-after instead of TDD]`;
60
+ }
61
+ }
62
+
63
+ // Set ctx.routing after all overrides are applied
64
+ ctx.routing = routing as RoutingResult;
65
+
66
+ const isBatch = ctx.stories.length > 1;
67
+
68
+ logger.debug("routing", "Task classified", {
69
+ complexity: ctx.routing.complexity,
70
+ modelTier: ctx.routing.modelTier,
71
+ testStrategy: ctx.routing.testStrategy,
72
+ storyId: ctx.story.id,
73
+ });
74
+
75
+ if (!isBatch) {
76
+ logger.debug("routing", ctx.routing.reasoning);
77
+ }
78
+
79
+ return { action: "continue" };
80
+ },
81
+ };
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Verify Stage
3
+ *
4
+ * Verifies the agent's work meets basic requirements by running tests.
5
+ * This is a lightweight verification before the full review stage.
6
+ *
7
+ * @returns
8
+ * - `continue`: Tests passed
9
+ * - `escalate`: Tests failed (retry with escalation)
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // Tests pass
14
+ * await verifyStage.execute(ctx);
15
+ * // Logs: "✓ Tests passed"
16
+ *
17
+ * // Tests fail
18
+ * await verifyStage.execute(ctx);
19
+ * // Returns: { action: "escalate", reason: "Tests failed (exit code 1)" }
20
+ * ```
21
+ */
22
+
23
+ import { getLogger } from "../../logger";
24
+ import { regression } from "../../verification/gate";
25
+ import type { PipelineContext, PipelineStage, StageResult } from "../types";
26
+
27
+ export const verifyStage: PipelineStage = {
28
+ name: "verify",
29
+ enabled: () => true,
30
+
31
+ async execute(ctx: PipelineContext): Promise<StageResult> {
32
+ const logger = getLogger();
33
+
34
+ // Skip verification if tests are not required
35
+ if (!ctx.config.quality.requireTests) {
36
+ logger.debug("verify", "Skipping verification (quality.requireTests = false)", { storyId: ctx.story.id });
37
+ return { action: "continue" };
38
+ }
39
+
40
+ // Skip verification if no test command is configured
41
+ const testCommand = ctx.config.review?.commands?.test ?? ctx.config.quality.commands.test;
42
+ if (!testCommand) {
43
+ logger.debug("verify", "Skipping verification (no test command configured)", { storyId: ctx.story.id });
44
+ return { action: "continue" };
45
+ }
46
+
47
+ logger.info("verify", "Running verification", { storyId: ctx.story.id });
48
+
49
+ // Use unified regression gate (includes 2s wait for agent process cleanup)
50
+ const result = await regression({
51
+ workdir: ctx.workdir,
52
+ command: testCommand,
53
+ timeoutSeconds: ctx.config.execution.verificationTimeoutSeconds,
54
+ });
55
+
56
+ // HARD FAILURE: Tests must pass for story to be marked complete
57
+ if (!result.success) {
58
+ // BUG-019: Distinguish timeout from actual test failures
59
+ if (result.status === "TIMEOUT") {
60
+ const timeout = ctx.config.execution.verificationTimeoutSeconds;
61
+ logger.error(
62
+ "verify",
63
+ `Test suite exceeded timeout (${timeout}s). This is NOT a test failure — consider increasing execution.verificationTimeoutSeconds or scoping tests.`,
64
+ {
65
+ exitCode: result.status,
66
+ storyId: ctx.story.id,
67
+ timeoutSeconds: timeout,
68
+ },
69
+ );
70
+ } else {
71
+ logger.error("verify", "Tests failed", {
72
+ exitCode: result.status,
73
+ storyId: ctx.story.id,
74
+ });
75
+ }
76
+
77
+ // Log first few lines of output for context (skip for TIMEOUT — output is misleading)
78
+ if (result.output && result.status !== "TIMEOUT") {
79
+ const outputLines = result.output.split("\n").slice(0, 10);
80
+ if (outputLines.length > 0) {
81
+ logger.debug("verify", "Test output preview", {
82
+ storyId: ctx.story.id,
83
+ output: outputLines.join("\n"),
84
+ });
85
+ }
86
+ }
87
+
88
+ return {
89
+ action: "escalate",
90
+ reason:
91
+ result.status === "TIMEOUT"
92
+ ? `Test suite TIMEOUT after ${ctx.config.execution.verificationTimeoutSeconds}s (not a code failure)`
93
+ : `Tests failed (exit code ${result.status ?? "non-zero"})`,
94
+ };
95
+ }
96
+
97
+ logger.info("verify", "Tests passed", { storyId: ctx.story.id });
98
+ return { action: "continue" };
99
+ },
100
+ };