@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,464 @@
1
+ /**
2
+ * Integration Tests: Interaction Chain → Pipeline (BUG-025)
3
+ *
4
+ * Verifies the three acceptance criteria:
5
+ * AC1: interactionChain is accessible in PipelineContext
6
+ * AC2: Story reaching max retries triggers a 'human-review' interaction request
7
+ * AC3: CLI interaction plugin participates in non-headless human-review
8
+ *
9
+ * These tests FAIL until BUG-025 is implemented.
10
+ */
11
+
12
+ import { describe, expect, mock, test } from "bun:test";
13
+ import type { NaxConfig } from "../../src/config";
14
+ import { InteractionChain } from "../../src/interaction/chain";
15
+ import { CLIInteractionPlugin } from "../../src/interaction/plugins/cli";
16
+ import type { InteractionPlugin, InteractionRequest, InteractionResponse, TriggerName } from "../../src/interaction/types";
17
+ import { TRIGGER_METADATA } from "../../src/interaction/types";
18
+ import type { PipelineContext } from "../../src/pipeline/types";
19
+ import type { SequentialExecutionContext } from "../../src/execution/sequential-executor";
20
+ import type { PRD, UserStory } from "../../src/prd/types";
21
+
22
+ // ─────────────────────────────────────────────────────────────────────────────
23
+ // Fixtures
24
+ // ─────────────────────────────────────────────────────────────────────────────
25
+
26
+ const baseConfig: Partial<NaxConfig> = {
27
+ execution: {
28
+ maxIterations: 10,
29
+ costLimit: 100,
30
+ iterationDelayMs: 0,
31
+ sessionTimeoutSeconds: 60,
32
+ maxStoriesPerFeature: 50,
33
+ rectification: { enabled: true, maxRetries: 2, fullSuiteTimeoutSeconds: 30, maxFailureSummaryChars: 500 },
34
+ verificationTimeoutSeconds: 60,
35
+ },
36
+ interaction: {
37
+ triggers: {
38
+ "human-review": { enabled: true },
39
+ },
40
+ defaults: {
41
+ timeout: 5000,
42
+ fallback: "skip" as const,
43
+ },
44
+ },
45
+ } as Partial<NaxConfig>;
46
+
47
+ const baseStory: UserStory = {
48
+ id: "US-001",
49
+ title: "Test story",
50
+ description: "A story that fails repeatedly",
51
+ acceptanceCriteria: [],
52
+ status: "pending",
53
+ attempts: 0,
54
+ };
55
+
56
+ const basePrd: PRD = {
57
+ feature: "test-feature",
58
+ version: "1",
59
+ userStories: [baseStory],
60
+ };
61
+
62
+ /** Build a mock InteractionPlugin that records sent requests */
63
+ function buildCapturingPlugin(): { plugin: InteractionPlugin; sentRequests: InteractionRequest[] } {
64
+ const sentRequests: InteractionRequest[] = [];
65
+ const plugin: InteractionPlugin = {
66
+ name: "capture",
67
+ send: mock(async (req: InteractionRequest) => {
68
+ sentRequests.push(req);
69
+ }),
70
+ receive: mock(async (requestId: string): Promise<InteractionResponse> => ({
71
+ requestId,
72
+ action: "skip",
73
+ respondedBy: "user",
74
+ respondedAt: Date.now(),
75
+ })),
76
+ };
77
+ return { plugin, sentRequests };
78
+ }
79
+
80
+ function buildInteractionChain(): InteractionChain {
81
+ return new InteractionChain({ defaultTimeout: 5000, defaultFallback: "skip" });
82
+ }
83
+
84
+ // ─────────────────────────────────────────────────────────────────────────────
85
+ // AC1: interactionChain is accessible in PipelineContext
86
+ // ─────────────────────────────────────────────────────────────────────────────
87
+
88
+ describe("AC1: interactionChain accessible in PipelineContext", () => {
89
+ test("PipelineContext type includes 'interaction' field", () => {
90
+ // FAILS until BUG-025 adds 'interaction?: InteractionChain' to PipelineContext interface
91
+ // We create a minimal context and verify the interaction field is accepted by the type system at runtime
92
+ const chain = buildInteractionChain();
93
+
94
+ const ctx: PipelineContext = {
95
+ config: baseConfig as NaxConfig,
96
+ prd: basePrd,
97
+ story: baseStory,
98
+ stories: [baseStory],
99
+ routing: { complexity: "simple", modelTier: "fast", testStrategy: "test-after", reasoning: "test" },
100
+ workdir: "/tmp",
101
+ hooks: { hooks: {} },
102
+ // @ts-expect-error — will fail until PipelineContext adds 'interaction' field
103
+ interaction: chain,
104
+ };
105
+
106
+ // The interaction field should be accessible
107
+ expect((ctx as Record<string, unknown>).interaction).toBe(chain);
108
+ });
109
+
110
+ test("PipelineContext 'interaction' field is optional (not required)", () => {
111
+ // FAILS until BUG-025 adds the field (or if it becomes required incorrectly)
112
+ // A context without 'interaction' should still be valid
113
+ const ctx: PipelineContext = {
114
+ config: baseConfig as NaxConfig,
115
+ prd: basePrd,
116
+ story: baseStory,
117
+ stories: [baseStory],
118
+ routing: { complexity: "simple", modelTier: "fast", testStrategy: "test-after", reasoning: "test" },
119
+ workdir: "/tmp",
120
+ hooks: { hooks: {} },
121
+ };
122
+
123
+ // Without the field, it should be undefined (not cause errors)
124
+ expect((ctx as Record<string, unknown>).interaction).toBeUndefined();
125
+ });
126
+
127
+ test("interactionChain stored in PipelineContext survives pipeline execution (via runPipeline)", async () => {
128
+ // BUG-025: PipelineContext now accepts 'interaction?: InteractionChain' field.
129
+ // Verifies the field is set and preserved correctly.
130
+ const chain = buildInteractionChain();
131
+ const { plugin } = buildCapturingPlugin();
132
+ chain.register(plugin, 10);
133
+
134
+ const ctx: PipelineContext = {
135
+ config: baseConfig as NaxConfig,
136
+ prd: basePrd,
137
+ story: baseStory,
138
+ stories: [baseStory],
139
+ routing: { complexity: "simple", modelTier: "fast", testStrategy: "test-after", reasoning: "test" },
140
+ workdir: "/tmp",
141
+ hooks: { hooks: {} },
142
+ interaction: chain, // BUG-025: 'interaction' field now accepted by PipelineContext
143
+ };
144
+
145
+ // The interaction chain is preserved in the context
146
+ expect((ctx as Record<string, unknown>).interaction).toBeInstanceOf(InteractionChain);
147
+ expect((ctx as Record<string, unknown>).interaction).toBe(chain);
148
+ });
149
+ });
150
+
151
+ // ─────────────────────────────────────────────────────────────────────────────
152
+ // SequentialExecutionContext — accepts interactionChain field
153
+ // ─────────────────────────────────────────────────────────────────────────────
154
+
155
+ describe("SequentialExecutionContext accepts interactionChain", () => {
156
+ test("SequentialExecutionContext type has interactionChain field", () => {
157
+ // FAILS until BUG-025 adds 'interactionChain?: InteractionChain | null' to SequentialExecutionContext
158
+ const chain = buildInteractionChain();
159
+
160
+ const ctx: SequentialExecutionContext = {
161
+ prdPath: "/tmp/prd.json",
162
+ workdir: "/tmp",
163
+ config: baseConfig as NaxConfig,
164
+ hooks: { hooks: {} } as any,
165
+ feature: "test-feature",
166
+ dryRun: true,
167
+ useBatch: false,
168
+ pluginRegistry: { plugins: [], getReporters: () => [], teardownAll: async () => {} } as any,
169
+ statusWriter: { setPrd: () => {}, setCurrentStory: () => {}, setRunStatus: () => {}, update: async () => {} } as any,
170
+ logFilePath: undefined,
171
+ runId: "run-test-001",
172
+ startTime: Date.now(),
173
+ batchPlan: [],
174
+ // @ts-expect-error — will fail until SequentialExecutionContext adds 'interactionChain' field
175
+ interactionChain: chain,
176
+ };
177
+
178
+ expect((ctx as Record<string, unknown>).interactionChain).toBe(chain);
179
+ });
180
+
181
+ test("runner.ts passes interactionChain from setupRun result to executeSequential", () => {
182
+ // BUG-025: SequentialExecutionContext now has 'interactionChain?: InteractionChain | null'.
183
+ // Verifies the data flow: runner.ts -> executeSequential ctx -> pipeline handler.
184
+ const chain = buildInteractionChain();
185
+
186
+ const ctx: SequentialExecutionContext = {
187
+ prdPath: "/tmp/prd.json",
188
+ workdir: "/tmp",
189
+ config: baseConfig as NaxConfig,
190
+ hooks: { hooks: {} } as any,
191
+ feature: "test-feature",
192
+ dryRun: true,
193
+ useBatch: false,
194
+ pluginRegistry: { plugins: [], getReporters: () => [], teardownAll: async () => {} } as any,
195
+ statusWriter: { setPrd: () => {}, setCurrentStory: () => {}, setRunStatus: () => {}, update: async () => {} } as any,
196
+ logFilePath: undefined,
197
+ runId: "run-test-002",
198
+ startTime: Date.now(),
199
+ batchPlan: [],
200
+ interactionChain: chain, // BUG-025: field now exists in SequentialExecutionContext
201
+ };
202
+
203
+ // interactionChain is accessible and preserved in the context
204
+ expect((ctx as Record<string, unknown>).interactionChain).toBe(chain);
205
+ expect((ctx as Record<string, unknown>).interactionChain).toBeInstanceOf(InteractionChain);
206
+ });
207
+ });
208
+
209
+ // ─────────────────────────────────────────────────────────────────────────────
210
+ // AC2: Story reaching max retries triggers 'human-review'
211
+ // ─────────────────────────────────────────────────────────────────────────────
212
+
213
+ describe("AC2: max retries triggers human-review interaction", () => {
214
+ test("human-review trigger is called when story.attempts >= maxRetries", async () => {
215
+ // FAILS until BUG-025 wires executeTrigger('human-review', ...) into the failure/max-retries path
216
+ const chain = buildInteractionChain();
217
+ const { plugin, sentRequests } = buildCapturingPlugin();
218
+ chain.register(plugin, 10);
219
+
220
+ // Story has already hit max retries (attempts >= maxRetries = 2)
221
+ const exhaustedStory: UserStory = {
222
+ ...baseStory,
223
+ id: "US-002",
224
+ attempts: 2,
225
+ status: "pending",
226
+ };
227
+
228
+ const prd: PRD = { ...basePrd, userStories: [exhaustedStory] };
229
+
230
+ // Import and invoke the failure handler or sequential executor path
231
+ // that should fire 'human-review' when a story has exceeded max retries
232
+ const { handlePipelineFailure } = await import("../../src/execution/pipeline-result-handler");
233
+ await handlePipelineFailure(
234
+ {
235
+ config: baseConfig as NaxConfig,
236
+ prd,
237
+ prdPath: "/tmp/prd.json",
238
+ workdir: "/tmp",
239
+ featureDir: undefined,
240
+ hooks: { hooks: {} } as any,
241
+ feature: "test-feature",
242
+ totalCost: 0,
243
+ startTime: Date.now(),
244
+ runId: "run-test-001",
245
+ pluginRegistry: { plugins: [], getReporters: () => [], teardownAll: async () => {} } as any,
246
+ story: exhaustedStory,
247
+ storiesToExecute: [exhaustedStory],
248
+ routing: { complexity: "simple", modelTier: "fast", testStrategy: "test-after", reasoning: "test" },
249
+ isBatchExecution: false,
250
+ allStoryMetrics: [],
251
+ timeoutRetryCountMap: new Map(),
252
+ storyGitRef: null,
253
+ // @ts-expect-error — interactionChain not in PipelineHandlerContext yet
254
+ interactionChain: chain,
255
+ },
256
+ {
257
+ success: false,
258
+ finalAction: "fail",
259
+ reason: "Max retries exceeded",
260
+ context: {
261
+ config: baseConfig as NaxConfig,
262
+ prd,
263
+ story: exhaustedStory,
264
+ stories: [exhaustedStory],
265
+ routing: { complexity: "simple", modelTier: "fast", testStrategy: "test-after", reasoning: "test" },
266
+ workdir: "/tmp",
267
+ hooks: { hooks: {} } as any,
268
+ },
269
+ } as any,
270
+ );
271
+
272
+ // FAILS: human-review trigger is not currently called in handlePipelineFailure
273
+ const humanReviewRequests = sentRequests.filter(
274
+ (r) => (r.metadata?.trigger as string) === "human-review",
275
+ );
276
+ expect(humanReviewRequests.length).toBeGreaterThan(0);
277
+ });
278
+
279
+ test("human-review request has storyId set to the failing story", async () => {
280
+ // BUG-025: handlePipelineFailure fires 'human-review' with the correct storyId
281
+ const chain = buildInteractionChain();
282
+ const { plugin, sentRequests } = buildCapturingPlugin();
283
+ chain.register(plugin, 10);
284
+
285
+ const failingStory: UserStory = {
286
+ ...baseStory,
287
+ id: "US-FAILING",
288
+ attempts: 3, // exceeds maxRetries=2
289
+ status: "pending",
290
+ };
291
+
292
+ const prd: PRD = { ...basePrd, userStories: [failingStory] };
293
+
294
+ // Verify the trigger is enabled
295
+ const { isTriggerEnabled } = await import("../../src/interaction/triggers");
296
+ const enabled = isTriggerEnabled("human-review" as TriggerName, baseConfig as NaxConfig);
297
+ expect(enabled).toBe(true);
298
+
299
+ // Call handlePipelineFailure to trigger the human-review request
300
+ const { handlePipelineFailure } = await import("../../src/execution/pipeline-result-handler");
301
+ await handlePipelineFailure(
302
+ {
303
+ config: baseConfig as NaxConfig,
304
+ prd,
305
+ prdPath: "/tmp/prd.json",
306
+ workdir: "/tmp",
307
+ featureDir: undefined,
308
+ hooks: { hooks: {} } as any,
309
+ feature: "test-feature",
310
+ totalCost: 0,
311
+ startTime: Date.now(),
312
+ runId: "run-test-003",
313
+ pluginRegistry: { plugins: [], getReporters: () => [], teardownAll: async () => {} } as any,
314
+ story: failingStory,
315
+ storiesToExecute: [failingStory],
316
+ routing: { complexity: "simple", modelTier: "fast", testStrategy: "test-after", reasoning: "test" },
317
+ isBatchExecution: false,
318
+ allStoryMetrics: [],
319
+ timeoutRetryCountMap: new Map(),
320
+ storyGitRef: null,
321
+ interactionChain: chain,
322
+ },
323
+ {
324
+ success: false,
325
+ finalAction: "fail",
326
+ reason: "Max retries exceeded",
327
+ context: {
328
+ config: baseConfig as NaxConfig,
329
+ prd,
330
+ story: failingStory,
331
+ stories: [failingStory],
332
+ routing: { complexity: "simple", modelTier: "fast", testStrategy: "test-after", reasoning: "test" },
333
+ workdir: "/tmp",
334
+ hooks: { hooks: {} } as any,
335
+ },
336
+ } as any,
337
+ );
338
+
339
+ // human-review request should have the correct storyId
340
+ const humanReviewRequests = sentRequests.filter(
341
+ (r) => (r.metadata?.trigger as string) === "human-review",
342
+ );
343
+ expect(humanReviewRequests.length).toBeGreaterThan(0);
344
+ expect(humanReviewRequests[0].storyId).toBe("US-FAILING");
345
+ });
346
+
347
+ test("human-review response 'skip' causes story to be skipped", async () => {
348
+ // FAILS until BUG-025 implements the full human-review trigger + response handling
349
+ const chain = buildInteractionChain();
350
+ const plugin: InteractionPlugin = {
351
+ name: "skip-responder",
352
+ send: mock(async () => {}),
353
+ receive: mock(async (requestId: string): Promise<InteractionResponse> => ({
354
+ requestId,
355
+ action: "skip",
356
+ respondedBy: "user",
357
+ respondedAt: Date.now(),
358
+ })),
359
+ };
360
+ chain.register(plugin, 10);
361
+
362
+ // When human-review returns 'skip', the story outcome should be 'skipped'
363
+ const { executeSequential } = await import("../../src/execution/sequential-executor");
364
+
365
+ const exhaustedStory: UserStory = {
366
+ ...baseStory,
367
+ id: "US-EXHAUST",
368
+ attempts: 3, // exceeds maxRetries=2
369
+ status: "pending",
370
+ };
371
+
372
+ // We cannot run full executeSequential (spawns agents, prechecks, etc.)
373
+ // Instead, verify the interactionChain is passed correctly and used
374
+ // This test serves as a sentinel: after BUG-025, executeSequential must accept interactionChain
375
+ const seqCtx = {
376
+ prdPath: "/tmp/prd.json",
377
+ workdir: "/tmp",
378
+ config: baseConfig as NaxConfig,
379
+ hooks: { hooks: {} },
380
+ feature: "test-feature",
381
+ dryRun: false,
382
+ useBatch: false,
383
+ pluginRegistry: { plugins: [], getReporters: () => [] },
384
+ statusWriter: { setPrd: () => {}, setCurrentStory: () => {}, update: async () => {} },
385
+ runId: "run-001",
386
+ startTime: Date.now(),
387
+ batchPlan: [],
388
+ interactionChain: chain, // <- not accepted yet
389
+ };
390
+
391
+ // The interactionChain field is not recognized by SequentialExecutionContext — FAILS
392
+ expect((seqCtx as Record<string, unknown>).interactionChain).toBeInstanceOf(InteractionChain);
393
+ // AND it should be passed through to pipeline execution
394
+ // (behavioral verification would require full integration, guarded by AC1 tests above)
395
+ });
396
+ });
397
+
398
+ // ─────────────────────────────────────────────────────────────────────────────
399
+ // AC3: CLI interaction plugin participates in non-headless human-review
400
+ // ─────────────────────────────────────────────────────────────────────────────
401
+
402
+ describe("AC3: CLI interaction plugin for non-headless human-review", () => {
403
+ test("CLIInteractionPlugin can be instantiated and registered in the chain", async () => {
404
+ // FAILS if CLIInteractionPlugin doesn't exist or cannot be imported
405
+ const plugin = new CLIInteractionPlugin();
406
+ expect(plugin.name).toBe("cli");
407
+
408
+ const chain = buildInteractionChain();
409
+ chain.register(plugin, 10);
410
+
411
+ // Primary plugin should be CLI
412
+ expect(chain.getPrimary()).toBe(plugin);
413
+ });
414
+
415
+ test("initInteractionChain registers CLI plugin for non-headless mode", async () => {
416
+ // FAILS if initInteractionChain doesn't register CLI plugin when headless=false
417
+ const { initInteractionChain } = await import("../../src/interaction");
418
+
419
+ const config = {
420
+ ...baseConfig,
421
+ interaction: {
422
+ ...baseConfig.interaction,
423
+ enabled: true,
424
+ plugin: "cli",
425
+ },
426
+ } as unknown as NaxConfig;
427
+
428
+ const chain = await initInteractionChain(config, false /* headless = false */);
429
+
430
+ // After BUG-025, the chain should have a CLI plugin registered for non-headless mode
431
+ // Currently may return null or a chain without a CLI plugin
432
+ expect(chain).not.toBeNull();
433
+ expect(chain?.getPrimary()).not.toBeNull();
434
+ expect(chain?.getPrimary()?.name).toBe("cli");
435
+ });
436
+
437
+ test("human-review request sent through CLI plugin contains all required fields", async () => {
438
+ // FAILS until BUG-025 implements human-review trigger in TRIGGER_METADATA
439
+ const { createTriggerRequest } = await import("../../src/interaction/triggers");
440
+
441
+ const request = createTriggerRequest(
442
+ "human-review" as TriggerName,
443
+ {
444
+ featureName: "my-feature",
445
+ storyId: "US-003",
446
+ iteration: 5,
447
+ reason: "Story has failed 5 times",
448
+ },
449
+ baseConfig as NaxConfig,
450
+ );
451
+
452
+ // Verify request is well-formed for CLI presentation
453
+ expect(request.type).toBe("confirm");
454
+ expect(request.stage).toBe("custom");
455
+ expect(request.featureName).toBe("my-feature");
456
+ expect(request.storyId).toBe("US-003");
457
+ expect(request.summary).toBeDefined();
458
+ expect(request.summary.length).toBeGreaterThan(0);
459
+ expect(request.fallback).toBe("skip");
460
+ expect(request.metadata?.trigger).toBe("human-review");
461
+ expect(request.metadata?.safety).toBe("yellow");
462
+ expect(request.createdAt).toBeGreaterThan(0);
463
+ });
464
+ });
@@ -0,0 +1,143 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { execSync } from "node:child_process";
3
+ import { mkdtempSync, rmSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { isSourceFile, isTestFile, verifyTestWriterIsolation } from "../../src/tdd";
7
+
8
+ describe("isTestFile", () => {
9
+ test("matches test/ directory", () => {
10
+ expect(isTestFile("test/auth.e2e-spec.ts")).toBe(true);
11
+ });
12
+
13
+ test("matches .spec.ts files", () => {
14
+ expect(isTestFile("src/auth/auth.spec.ts")).toBe(true);
15
+ });
16
+
17
+ test("matches .test.ts files", () => {
18
+ expect(isTestFile("src/utils.test.ts")).toBe(true);
19
+ });
20
+
21
+ test("does not match source files", () => {
22
+ expect(isTestFile("src/auth/auth.service.ts")).toBe(false);
23
+ });
24
+ });
25
+
26
+ describe("isSourceFile", () => {
27
+ test("matches src/ directory", () => {
28
+ expect(isSourceFile("src/auth/auth.service.ts")).toBe(true);
29
+ });
30
+
31
+ test("matches lib/ directory", () => {
32
+ expect(isSourceFile("lib/utils.ts")).toBe(true);
33
+ });
34
+
35
+ test("does not match test files in test/", () => {
36
+ expect(isSourceFile("test/auth.spec.ts")).toBe(false);
37
+ });
38
+ });
39
+
40
+ describe("verifyTestWriterIsolation", () => {
41
+ let testDir: string;
42
+
43
+ beforeEach(() => {
44
+ // Create a temporary git repository for testing
45
+ testDir = mkdtempSync(join(tmpdir(), "nax-isolation-test-"));
46
+ execSync("git init", { cwd: testDir, stdio: "ignore" });
47
+ execSync("git config user.email 'test@test.com'", { cwd: testDir, stdio: "ignore" });
48
+ execSync("git config user.name 'Test'", { cwd: testDir, stdio: "ignore" });
49
+ // Create initial commit
50
+ execSync("touch README.md", { cwd: testDir, stdio: "ignore" });
51
+ execSync("git add .", { cwd: testDir, stdio: "ignore" });
52
+ execSync("git commit -m 'Initial commit'", { cwd: testDir, stdio: "ignore" });
53
+ });
54
+
55
+ afterEach(() => {
56
+ // Clean up
57
+ rmSync(testDir, { recursive: true, force: true });
58
+ });
59
+
60
+ test("passes when only test files are modified", async () => {
61
+ // Create test file
62
+ execSync("mkdir -p test", { cwd: testDir, stdio: "ignore" });
63
+ execSync("echo 'test code' > test/example.test.ts", { cwd: testDir, stdio: "ignore" });
64
+ execSync("git add .", { cwd: testDir, stdio: "ignore" });
65
+
66
+ const result = await verifyTestWriterIsolation(testDir, "HEAD");
67
+ expect(result.passed).toBe(true);
68
+ expect(result.violations).toHaveLength(0);
69
+ expect(result.softViolations ?? []).toHaveLength(0);
70
+ });
71
+
72
+ test("fails when source files are modified (hard violation)", async () => {
73
+ // Create source file
74
+ execSync("mkdir -p src/auth", { cwd: testDir, stdio: "ignore" });
75
+ execSync("echo 'source code' > src/auth/service.ts", { cwd: testDir, stdio: "ignore" });
76
+ execSync("git add .", { cwd: testDir, stdio: "ignore" });
77
+
78
+ const result = await verifyTestWriterIsolation(testDir, "HEAD");
79
+ expect(result.passed).toBe(false);
80
+ expect(result.violations).toContain("src/auth/service.ts");
81
+ });
82
+
83
+ test("passes with soft violation when barrel export is modified (default allowed paths)", async () => {
84
+ // Create src/index.ts (barrel export)
85
+ execSync("mkdir -p src", { cwd: testDir, stdio: "ignore" });
86
+ execSync("echo 'export * from \"./module\"' > src/index.ts", { cwd: testDir, stdio: "ignore" });
87
+ execSync("git add .", { cwd: testDir, stdio: "ignore" });
88
+
89
+ const result = await verifyTestWriterIsolation(testDir, "HEAD");
90
+ expect(result.passed).toBe(true);
91
+ expect(result.violations).toHaveLength(0);
92
+ expect(result.softViolations).toContain("src/index.ts");
93
+ });
94
+
95
+ test("passes with soft violation when nested barrel export is modified", async () => {
96
+ // Create src/module/index.ts (nested barrel export)
97
+ execSync("mkdir -p src/module", { cwd: testDir, stdio: "ignore" });
98
+ execSync("echo 'export * from \"./service\"' > src/module/index.ts", { cwd: testDir, stdio: "ignore" });
99
+ execSync("git add .", { cwd: testDir, stdio: "ignore" });
100
+
101
+ const result = await verifyTestWriterIsolation(testDir, "HEAD");
102
+ expect(result.passed).toBe(true);
103
+ expect(result.violations).toHaveLength(0);
104
+ expect(result.softViolations).toContain("src/module/index.ts");
105
+ });
106
+
107
+ test("respects custom allowed paths", async () => {
108
+ // Create custom allowed file
109
+ execSync("mkdir -p src/config", { cwd: testDir, stdio: "ignore" });
110
+ execSync("echo 'config' > src/config/config.ts", { cwd: testDir, stdio: "ignore" });
111
+ execSync("git add .", { cwd: testDir, stdio: "ignore" });
112
+
113
+ const result = await verifyTestWriterIsolation(testDir, "HEAD", ["src/config/config.ts"]);
114
+ expect(result.passed).toBe(true);
115
+ expect(result.violations).toHaveLength(0);
116
+ expect(result.softViolations).toContain("src/config/config.ts");
117
+ });
118
+
119
+ test("combines hard and soft violations correctly", async () => {
120
+ // Create both barrel export (soft) and source file (hard)
121
+ execSync("mkdir -p src/auth", { cwd: testDir, stdio: "ignore" });
122
+ execSync("echo 'export * from \"./module\"' > src/index.ts", { cwd: testDir, stdio: "ignore" });
123
+ execSync("echo 'source code' > src/auth/service.ts", { cwd: testDir, stdio: "ignore" });
124
+ execSync("git add .", { cwd: testDir, stdio: "ignore" });
125
+
126
+ const result = await verifyTestWriterIsolation(testDir, "HEAD");
127
+ expect(result.passed).toBe(false);
128
+ expect(result.violations).toContain("src/auth/service.ts");
129
+ expect(result.softViolations).toContain("src/index.ts");
130
+ });
131
+
132
+ test("empty allowed paths array means no soft violations", async () => {
133
+ // Create src/index.ts
134
+ execSync("mkdir -p src", { cwd: testDir, stdio: "ignore" });
135
+ execSync("echo 'export * from \"./module\"' > src/index.ts", { cwd: testDir, stdio: "ignore" });
136
+ execSync("git add .", { cwd: testDir, stdio: "ignore" });
137
+
138
+ const result = await verifyTestWriterIsolation(testDir, "HEAD", []);
139
+ expect(result.passed).toBe(false);
140
+ expect(result.violations).toContain("src/index.ts");
141
+ expect(result.softViolations ?? []).toHaveLength(0);
142
+ });
143
+ });