@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,149 @@
1
+ /**
2
+ * Review Phase Tests
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { mkdtempSync } from "node:fs";
7
+ import { tmpdir } from "node:os";
8
+ import { join } from "node:path";
9
+ import { runReview } from "../../src/review";
10
+ import type { ReviewConfig } from "../../src/review";
11
+
12
+ describe("Review Phase", () => {
13
+ test("runReview - all checks pass", async () => {
14
+ const tempDir = mkdtempSync(join(tmpdir(), "nax-review-test-"));
15
+
16
+ const config: ReviewConfig = {
17
+ enabled: true,
18
+ checks: ["test"],
19
+ commands: {
20
+ test: "echo 'Tests passed'",
21
+ },
22
+ };
23
+
24
+ const result = await runReview(config, tempDir);
25
+
26
+ expect(result.success).toBe(true);
27
+ expect(result.checks).toHaveLength(1);
28
+ expect(result.checks[0].check).toBe("test");
29
+ expect(result.checks[0].success).toBe(true);
30
+ expect(result.checks[0].exitCode).toBe(0);
31
+ expect(result.failureReason).toBeUndefined();
32
+ });
33
+
34
+ test("runReview - check fails", async () => {
35
+ const tempDir = mkdtempSync(join(tmpdir(), "nax-review-test-"));
36
+
37
+ const config: ReviewConfig = {
38
+ enabled: true,
39
+ checks: ["typecheck"],
40
+ commands: {
41
+ typecheck: "sh -c 'exit 1'",
42
+ },
43
+ };
44
+
45
+ const result = await runReview(config, tempDir);
46
+
47
+ expect(result.success).toBe(false);
48
+ expect(result.checks).toHaveLength(1);
49
+ expect(result.checks[0].check).toBe("typecheck");
50
+ expect(result.checks[0].success).toBe(false);
51
+ expect(result.checks[0].exitCode).not.toBe(0);
52
+ expect(result.failureReason).toContain("typecheck failed");
53
+ });
54
+
55
+ test("runReview - multiple checks, stop on first failure", async () => {
56
+ const tempDir = mkdtempSync(join(tmpdir(), "nax-review-test-"));
57
+
58
+ const config: ReviewConfig = {
59
+ enabled: true,
60
+ checks: ["typecheck", "lint", "test"],
61
+ commands: {
62
+ typecheck: "echo 'typecheck ok'",
63
+ lint: "sh -c 'exit 1'",
64
+ test: "echo 'test ok'",
65
+ },
66
+ };
67
+
68
+ const result = await runReview(config, tempDir);
69
+
70
+ expect(result.success).toBe(false);
71
+ // Should only run typecheck and lint, not test (fail-fast)
72
+ expect(result.checks).toHaveLength(2);
73
+ expect(result.checks[0].check).toBe("typecheck");
74
+ expect(result.checks[0].success).toBe(true);
75
+ expect(result.checks[1].check).toBe("lint");
76
+ expect(result.checks[1].success).toBe(false);
77
+ expect(result.failureReason).toContain("lint failed");
78
+ });
79
+
80
+ test("runReview - uses review config commands when specified", async () => {
81
+ const tempDir = mkdtempSync(join(tmpdir(), "nax-review-test-"));
82
+
83
+ const config: ReviewConfig = {
84
+ enabled: true,
85
+ checks: ["test"],
86
+ commands: {
87
+ test: "echo 'custom test command'",
88
+ },
89
+ };
90
+
91
+ const result = await runReview(config, tempDir);
92
+
93
+ // Custom command from config.review.commands
94
+ expect(result.checks[0].command).toBe("echo 'custom test command'");
95
+ });
96
+
97
+ test("runReview - empty checks array", async () => {
98
+ const tempDir = mkdtempSync(join(tmpdir(), "nax-review-test-"));
99
+
100
+ const config: ReviewConfig = {
101
+ enabled: true,
102
+ checks: [],
103
+ commands: {},
104
+ };
105
+
106
+ const result = await runReview(config, tempDir);
107
+
108
+ expect(result.success).toBe(true);
109
+ expect(result.checks).toHaveLength(0);
110
+ expect(result.failureReason).toBeUndefined();
111
+ });
112
+
113
+ test("runReview - captures command output", async () => {
114
+ const tempDir = mkdtempSync(join(tmpdir(), "nax-review-test-"));
115
+
116
+ const config: ReviewConfig = {
117
+ enabled: true,
118
+ checks: ["test"],
119
+ commands: {
120
+ test: "echo 'Test output line 1' && echo 'Test output line 2'",
121
+ },
122
+ };
123
+
124
+ const result = await runReview(config, tempDir);
125
+
126
+ expect(result.success).toBe(true);
127
+ expect(result.checks[0].output).toContain("Test output line 1");
128
+ expect(result.checks[0].output).toContain("Test output line 2");
129
+ });
130
+
131
+ test("runReview - records duration", async () => {
132
+ const tempDir = mkdtempSync(join(tmpdir(), "nax-review-test-"));
133
+
134
+ const config: ReviewConfig = {
135
+ enabled: true,
136
+ checks: ["test"],
137
+ commands: {
138
+ test: "echo 'done'",
139
+ },
140
+ };
141
+
142
+ const result = await runReview(config, tempDir);
143
+
144
+ expect(result.success).toBe(true);
145
+ expect(result.checks[0].durationMs).toBeGreaterThan(0);
146
+ expect(result.totalDurationMs).toBeGreaterThan(0);
147
+ expect(result.totalDurationMs).toBeGreaterThanOrEqual(result.checks[0].durationMs);
148
+ });
149
+ });
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Routing Stage Bug-021 Fix: Ensure "Task classified" log shows final routing state
3
+ *
4
+ * Tests that the "Task classified" log reflects the final routing state after all
5
+ * overrides (config cache overrides, greenfield detection, etc.) are applied.
6
+ *
7
+ * BUG-021: The log was showing raw LLM output even after overrides were applied.
8
+ * Fix: Ensure ctx.routing is set AFTER all overrides, and the log uses ctx.routing.
9
+ */
10
+
11
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
12
+ import { mkdtemp, rm } from "node:fs/promises";
13
+ import { tmpdir } from "node:os";
14
+ import { join } from "node:path";
15
+ import type { NaxConfig } from "../../src/config/schema";
16
+ import { initLogger, resetLogger } from "../../src/logger";
17
+ import type { Logger } from "../../src/logger";
18
+ import { routingStage } from "../../src/pipeline/stages/routing";
19
+ import type { PipelineContext } from "../../src/pipeline/types";
20
+ import { PluginRegistry } from "../../src/plugins/registry";
21
+ import type { PRD, UserStory } from "../../src/prd/types";
22
+
23
+ /**
24
+ * Helper: Create minimal test context
25
+ */
26
+ function createTestContext(
27
+ workdir: string,
28
+ overrides?: Partial<PipelineContext>,
29
+ ): PipelineContext {
30
+ const story: UserStory = {
31
+ id: "BUG-021-test",
32
+ title: "Add user authentication",
33
+ description: "Implement JWT-based authentication",
34
+ acceptanceCriteria: ["Secure token storage", "Token refresh", "Password hashing", "Session management"],
35
+ tags: ["security", "auth"],
36
+ dependencies: [],
37
+ status: "pending",
38
+ passes: false,
39
+ escalations: [],
40
+ attempts: 0,
41
+ };
42
+
43
+ const prd: PRD = {
44
+ project: "test-project",
45
+ feature: "test-feature",
46
+ branchName: "test-branch",
47
+ createdAt: new Date().toISOString(),
48
+ updatedAt: new Date().toISOString(),
49
+ userStories: [story],
50
+ };
51
+
52
+ const config: NaxConfig = {
53
+ version: 1,
54
+ models: {
55
+ fast: "claude-haiku-4-5",
56
+ balanced: "claude-sonnet-4-5",
57
+ powerful: "claude-opus-4-6",
58
+ },
59
+ autoMode: {
60
+ enabled: true,
61
+ defaultAgent: "nax-agent-claude",
62
+ fallbackOrder: ["nax-agent-claude"],
63
+ complexityRouting: {
64
+ simple: "fast",
65
+ medium: "balanced",
66
+ complex: "powerful",
67
+ expert: "powerful",
68
+ },
69
+ escalation: {
70
+ enabled: true,
71
+ tierOrder: [
72
+ { tier: "fast", attempts: 2 },
73
+ { tier: "balanced", attempts: 2 },
74
+ { tier: "powerful", attempts: 1 },
75
+ ],
76
+ escalateEntireBatch: true,
77
+ },
78
+ },
79
+ routing: {
80
+ strategy: "keyword",
81
+ },
82
+ execution: {
83
+ maxIterations: 100,
84
+ iterationDelayMs: 1000,
85
+ costLimit: 50,
86
+ sessionTimeoutSeconds: 600,
87
+ verificationTimeoutSeconds: 300,
88
+ maxStoriesPerFeature: 50,
89
+ rectification: {
90
+ enabled: true,
91
+ maxRetries: 2,
92
+ fullSuiteTimeoutSeconds: 120,
93
+ maxFailureSummaryChars: 2000,
94
+ abortOnIncreasingFailures: true,
95
+ },
96
+ contextProviderTokenBudget: 2000,
97
+ },
98
+ quality: {
99
+ requireTypecheck: false,
100
+ requireLint: false,
101
+ requireTests: true,
102
+ commands: {},
103
+ forceExit: false,
104
+ detectOpenHandles: true,
105
+ detectOpenHandlesRetries: 1,
106
+ gracePeriodMs: 5000,
107
+ drainTimeoutMs: 2000,
108
+ shell: "/bin/sh",
109
+ stripEnvVars: [],
110
+ environmentalEscalationDivisor: 2,
111
+ },
112
+ tdd: {
113
+ maxRetries: 3,
114
+ autoVerifyIsolation: true,
115
+ autoApproveVerifier: true,
116
+ strategy: "auto",
117
+ greenfieldDetection: true,
118
+ rollbackOnFailure: true,
119
+ },
120
+ constitution: {
121
+ enabled: false,
122
+ path: "constitution.md",
123
+ maxTokens: 2000,
124
+ },
125
+ analyze: {
126
+ llmEnhanced: false,
127
+ model: "balanced",
128
+ fallbackToKeywords: true,
129
+ maxCodebaseSummaryTokens: 4000,
130
+ },
131
+ review: {
132
+ enabled: true,
133
+ checks: ["test"],
134
+ commands: {},
135
+ },
136
+ plan: {
137
+ model: "balanced",
138
+ outputPath: "features",
139
+ },
140
+ acceptance: {
141
+ enabled: true,
142
+ maxRetries: 2,
143
+ generateTests: true,
144
+ testPath: "acceptance.test.ts",
145
+ },
146
+ context: {
147
+ testCoverage: {
148
+ enabled: true,
149
+ detail: "names-and-counts",
150
+ maxTokens: 500,
151
+ testPattern: "**/*.test.{ts,js,tsx,jsx}",
152
+ scopeToStory: true,
153
+ },
154
+ },
155
+ };
156
+
157
+ return {
158
+ workdir,
159
+ story,
160
+ stories: [story],
161
+ prd,
162
+ config,
163
+ plugins: new PluginRegistry([]),
164
+ ...overrides,
165
+ };
166
+ }
167
+
168
+ describe("Routing Stage - BUG-021: Task classified log shows final routing state", () => {
169
+ let workdir: string;
170
+ let capturedLogs: Array<{
171
+ level: string;
172
+ stage: string;
173
+ message: string;
174
+ data?: Record<string, unknown>;
175
+ }>;
176
+
177
+ beforeEach(async () => {
178
+ workdir = await mkdtemp(join(tmpdir(), "nax-bug-021-test-"));
179
+ capturedLogs = [];
180
+
181
+ // Initialize logger with debug level to capture all logs
182
+ initLogger({ level: "debug" });
183
+ });
184
+
185
+ afterEach(async () => {
186
+ await rm(workdir, { recursive: true, force: true });
187
+ resetLogger();
188
+ });
189
+
190
+ test("logs final routing state (not raw LLM output) when greenfield override is applied", async () => {
191
+ // Create source files but no test files (triggers greenfield detection)
192
+ await Bun.write(join(workdir, "src/index.ts"), "export const foo = 42;");
193
+
194
+ const ctx = createTestContext(workdir);
195
+ const result = await routingStage.execute(ctx);
196
+
197
+ expect(result.action).toBe("continue");
198
+
199
+ // Verify ctx.routing has the final state (greenfield override applied)
200
+ expect(ctx.routing).toBeDefined();
201
+ expect(ctx.routing?.testStrategy).toBe("test-after");
202
+ expect(ctx.routing?.complexity).toBe("complex");
203
+ expect(ctx.routing?.modelTier).toBe("powerful");
204
+
205
+ // Verify reasoning mentions the greenfield override
206
+ expect(ctx.routing?.reasoning).toContain("GREENFIELD OVERRIDE");
207
+ });
208
+
209
+ test("logs final routing state when using cached routing with greenfield override", async () => {
210
+ // Create source files but no test files
211
+ await Bun.write(join(workdir, "src/index.ts"), "export const foo = 42;");
212
+
213
+ const ctx = createTestContext(workdir);
214
+ // Simulate cached routing that would normally use TDD
215
+ ctx.story.routing = {
216
+ complexity: "medium",
217
+ testStrategy: "three-session-tdd",
218
+ reasoning: "Cached from previous run",
219
+ };
220
+
221
+ const result = await routingStage.execute(ctx);
222
+
223
+ expect(result.action).toBe("continue");
224
+
225
+ // Verify final state has greenfield override applied to cached TDD strategy
226
+ expect(ctx.routing).toBeDefined();
227
+ expect(ctx.routing?.testStrategy).toBe("test-after");
228
+ expect(ctx.routing?.complexity).toBe("medium");
229
+ expect(ctx.routing?.modelTier).toBe("balanced");
230
+ expect(ctx.routing?.reasoning).toContain("GREENFIELD OVERRIDE");
231
+ });
232
+
233
+ test("logs final routing state when no overrides are needed", async () => {
234
+ // Create test files so greenfield detection doesn't trigger
235
+ await Bun.write(join(workdir, "src/index.test.ts"), "test('foo', () => {})");
236
+
237
+ const ctx = createTestContext(workdir);
238
+ const result = await routingStage.execute(ctx);
239
+
240
+ expect(result.action).toBe("continue");
241
+
242
+ // Verify ctx.routing shows final state (no greenfield override)
243
+ expect(ctx.routing).toBeDefined();
244
+ expect(ctx.routing?.testStrategy).toMatch(/three-session-tdd/);
245
+ expect(ctx.routing?.complexity).toBe("complex");
246
+ expect(ctx.routing?.modelTier).toBe("powerful");
247
+ expect(ctx.routing?.reasoning).not.toContain("GREENFIELD OVERRIDE");
248
+ });
249
+
250
+ test("ctx.routing is set after all overrides are applied", async () => {
251
+ // Create source files but no test files (triggers greenfield detection)
252
+ await Bun.write(join(workdir, "src/auth.ts"), "export function authenticate() {}");
253
+
254
+ const ctx = createTestContext(workdir);
255
+ // Set initial cached routing
256
+ ctx.story.routing = {
257
+ complexity: "simple",
258
+ testStrategy: "three-session-tdd-lite",
259
+ reasoning: "Cached simple routing",
260
+ };
261
+
262
+ const result = await routingStage.execute(ctx);
263
+
264
+ expect(result.action).toBe("continue");
265
+
266
+ // After routing stage completes, ctx.routing should reflect:
267
+ // 1. Cached complexity: "simple"
268
+ // 2. Fresh modelTier: derived from config
269
+ // 3. Greenfield override: test-after instead of TDD
270
+ expect(ctx.routing?.complexity).toBe("simple");
271
+ expect(ctx.routing?.testStrategy).toBe("test-after");
272
+ expect(ctx.routing?.modelTier).toBe("fast"); // simple -> fast
273
+ });
274
+ });
@@ -0,0 +1,286 @@
1
+ /**
2
+ * Routing Stage Greenfield Detection Tests
3
+ *
4
+ * Tests BUG-010 fix: greenfield detection forces test-after strategy
5
+ */
6
+
7
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
8
+ import { mkdtemp, rm } from "node:fs/promises";
9
+ import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
11
+ import type { NaxConfig } from "../../src/config/schema";
12
+ import { initLogger, resetLogger } from "../../src/logger";
13
+ import { routingStage } from "../../src/pipeline/stages/routing";
14
+ import type { PipelineContext } from "../../src/pipeline/types";
15
+ import { PluginRegistry } from "../../src/plugins/registry";
16
+ import type { PRD, UserStory } from "../../src/prd/types";
17
+
18
+ // ─────────────────────────────────────────────────────────────────────────────
19
+ // Test Helpers
20
+ // ─────────────────────────────────────────────────────────────────────────────
21
+
22
+ async function createTestFile(workdir: string, filepath: string, content = ""): Promise<void> {
23
+ const fullPath = join(workdir, filepath);
24
+ await Bun.write(fullPath, content);
25
+ }
26
+
27
+ /** Helper: Create minimal test context */
28
+ function createTestContext(
29
+ workdir: string,
30
+ greenfieldDetectionEnabled = true,
31
+ overrides?: Partial<PipelineContext>,
32
+ ): PipelineContext {
33
+ const story: UserStory = {
34
+ id: "US-001",
35
+ title: "Add user authentication",
36
+ description: "Implement JWT-based authentication",
37
+ acceptanceCriteria: ["Secure token storage", "Token refresh", "Password hashing", "Session management"],
38
+ tags: ["security", "auth"],
39
+ dependencies: [],
40
+ status: "pending",
41
+ passes: false,
42
+ escalations: [],
43
+ attempts: 0,
44
+ };
45
+
46
+ const prd: PRD = {
47
+ project: "test-project",
48
+ feature: "test-feature",
49
+ branchName: "test-branch",
50
+ createdAt: new Date().toISOString(),
51
+ updatedAt: new Date().toISOString(),
52
+ userStories: [story],
53
+ };
54
+
55
+ const config: NaxConfig = {
56
+ version: 1,
57
+ models: {
58
+ fast: "claude-haiku-4-5",
59
+ balanced: "claude-sonnet-4-5",
60
+ powerful: "claude-opus-4-6",
61
+ },
62
+ autoMode: {
63
+ enabled: true,
64
+ defaultAgent: "nax-agent-claude",
65
+ fallbackOrder: ["nax-agent-claude"],
66
+ complexityRouting: {
67
+ simple: "fast",
68
+ medium: "balanced",
69
+ complex: "powerful",
70
+ expert: "powerful",
71
+ },
72
+ escalation: {
73
+ enabled: true,
74
+ tierOrder: [
75
+ { tier: "fast", attempts: 2 },
76
+ { tier: "balanced", attempts: 2 },
77
+ { tier: "powerful", attempts: 1 },
78
+ ],
79
+ escalateEntireBatch: true,
80
+ },
81
+ },
82
+ routing: {
83
+ strategy: "keyword",
84
+ },
85
+ execution: {
86
+ maxIterations: 100,
87
+ iterationDelayMs: 1000,
88
+ costLimit: 50,
89
+ sessionTimeoutSeconds: 600,
90
+ verificationTimeoutSeconds: 300,
91
+ maxStoriesPerFeature: 50,
92
+ rectification: {
93
+ enabled: true,
94
+ maxRetries: 2,
95
+ fullSuiteTimeoutSeconds: 120,
96
+ maxFailureSummaryChars: 2000,
97
+ abortOnIncreasingFailures: true,
98
+ },
99
+ contextProviderTokenBudget: 2000,
100
+ },
101
+ quality: {
102
+ requireTypecheck: false,
103
+ requireLint: false,
104
+ requireTests: true,
105
+ commands: {},
106
+ forceExit: false,
107
+ detectOpenHandles: true,
108
+ detectOpenHandlesRetries: 1,
109
+ gracePeriodMs: 5000,
110
+ drainTimeoutMs: 2000,
111
+ shell: "/bin/sh",
112
+ stripEnvVars: [],
113
+ environmentalEscalationDivisor: 2,
114
+ },
115
+ tdd: {
116
+ maxRetries: 3,
117
+ autoVerifyIsolation: true,
118
+ autoApproveVerifier: true,
119
+ strategy: "auto",
120
+ greenfieldDetection: greenfieldDetectionEnabled,
121
+ rollbackOnFailure: true,
122
+ },
123
+ constitution: {
124
+ enabled: false,
125
+ path: "constitution.md",
126
+ maxTokens: 2000,
127
+ },
128
+ analyze: {
129
+ llmEnhanced: false,
130
+ model: "balanced",
131
+ fallbackToKeywords: true,
132
+ maxCodebaseSummaryTokens: 4000,
133
+ },
134
+ review: {
135
+ enabled: true,
136
+ checks: ["test"],
137
+ commands: {},
138
+ },
139
+ plan: {
140
+ model: "balanced",
141
+ outputPath: "features",
142
+ },
143
+ acceptance: {
144
+ enabled: true,
145
+ maxRetries: 2,
146
+ generateTests: true,
147
+ testPath: "acceptance.test.ts",
148
+ },
149
+ context: {
150
+ testCoverage: {
151
+ enabled: true,
152
+ detail: "names-and-counts",
153
+ maxTokens: 500,
154
+ testPattern: "**/*.test.{ts,js,tsx,jsx}",
155
+ scopeToStory: true,
156
+ },
157
+ },
158
+ };
159
+
160
+ return {
161
+ workdir,
162
+ story,
163
+ stories: [story],
164
+ prd,
165
+ config,
166
+ plugins: new PluginRegistry([]),
167
+ ...overrides,
168
+ };
169
+ }
170
+
171
+ // ─────────────────────────────────────────────────────────────────────────────
172
+ // Tests
173
+ // ─────────────────────────────────────────────────────────────────────────────
174
+
175
+ describe("Routing Stage - Greenfield Detection (BUG-010)", () => {
176
+ let workdir: string;
177
+
178
+ beforeEach(async () => {
179
+ workdir = await mkdtemp(join(tmpdir(), "nax-routing-greenfield-test-"));
180
+ await initLogger({ level: "silent" });
181
+ });
182
+
183
+ afterEach(async () => {
184
+ await rm(workdir, { recursive: true, force: true });
185
+ resetLogger();
186
+ });
187
+
188
+ test("forces test-after when no test files exist (greenfield)", async () => {
189
+ // Create source files but no test files
190
+ await createTestFile(workdir, "src/index.ts", "export const foo = 42;");
191
+
192
+ const ctx = createTestContext(workdir, true);
193
+ const result = await routingStage.execute(ctx);
194
+
195
+ expect(result.action).toBe("continue");
196
+ expect(ctx.routing).toBeDefined();
197
+ expect(ctx.routing?.testStrategy).toBe("test-after");
198
+ expect(ctx.routing?.reasoning).toContain("GREENFIELD OVERRIDE");
199
+ });
200
+
201
+ test("preserves TDD when test files exist", async () => {
202
+ // Create test files
203
+ await createTestFile(workdir, "src/index.test.ts", "test('foo', () => {})");
204
+
205
+ const ctx = createTestContext(workdir, true);
206
+ const result = await routingStage.execute(ctx);
207
+
208
+ expect(result.action).toBe("continue");
209
+ expect(ctx.routing).toBeDefined();
210
+ // Should use TDD for complex stories with existing tests
211
+ expect(ctx.routing?.testStrategy).toMatch(/three-session-tdd/);
212
+ });
213
+
214
+ test("respects greenfieldDetection config disabled", async () => {
215
+ // No test files, but greenfield detection disabled
216
+ await createTestFile(workdir, "src/index.ts", "export const foo = 42;");
217
+
218
+ const ctx = createTestContext(workdir, false); // greenfieldDetection = false
219
+ const result = await routingStage.execute(ctx);
220
+
221
+ expect(result.action).toBe("continue");
222
+ expect(ctx.routing).toBeDefined();
223
+ // Should use TDD even though greenfield, because detection is disabled
224
+ expect(ctx.routing?.testStrategy).toMatch(/three-session-tdd/);
225
+ });
226
+
227
+ test("only overrides TDD strategies, not test-after", async () => {
228
+ // Create a simple story that would normally get test-after
229
+ const ctx = createTestContext(workdir, true);
230
+ ctx.story.title = "Fix typo in README";
231
+ ctx.story.description = "Update README.md";
232
+ ctx.story.acceptanceCriteria = ["Typo fixed"];
233
+
234
+ const result = await routingStage.execute(ctx);
235
+
236
+ expect(result.action).toBe("continue");
237
+ expect(ctx.routing).toBeDefined();
238
+ // test-after strategy should remain unchanged
239
+ expect(ctx.routing?.testStrategy).toBe("test-after");
240
+ });
241
+
242
+ test("handles both TDD and TDD-lite strategies", async () => {
243
+ // Test that greenfield detection works for both TDD variants
244
+ await createTestFile(workdir, "src/index.ts", "export const foo = 42;");
245
+
246
+ const ctx = createTestContext(workdir, true);
247
+ ctx.story.routing = {
248
+ complexity: "medium",
249
+ testStrategy: "three-session-tdd-lite",
250
+ reasoning: "Pre-cached routing",
251
+ };
252
+
253
+ const result = await routingStage.execute(ctx);
254
+
255
+ expect(result.action).toBe("continue");
256
+ expect(ctx.routing).toBeDefined();
257
+ expect(ctx.routing?.testStrategy).toBe("test-after");
258
+ expect(ctx.routing?.reasoning).toContain("GREENFIELD OVERRIDE");
259
+ });
260
+
261
+ test("ignores test files in node_modules", async () => {
262
+ // Create test file in node_modules (should be ignored)
263
+ await createTestFile(workdir, "node_modules/lib/foo.test.ts", "test('foo', () => {})");
264
+
265
+ const ctx = createTestContext(workdir, true);
266
+ const result = await routingStage.execute(ctx);
267
+
268
+ expect(result.action).toBe("continue");
269
+ expect(ctx.routing).toBeDefined();
270
+ // Should treat as greenfield since node_modules is ignored
271
+ expect(ctx.routing?.testStrategy).toBe("test-after");
272
+ });
273
+
274
+ test("detects various test file patterns", async () => {
275
+ // Test .spec.ts pattern
276
+ await createTestFile(workdir, "src/foo.spec.ts", "describe('foo', () => {})");
277
+
278
+ const ctx = createTestContext(workdir, true);
279
+ const result = await routingStage.execute(ctx);
280
+
281
+ expect(result.action).toBe("continue");
282
+ expect(ctx.routing).toBeDefined();
283
+ // Should preserve TDD because .spec.ts files exist
284
+ expect(ctx.routing?.testStrategy).toMatch(/three-session-tdd/);
285
+ });
286
+ });