@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,197 @@
1
+ /**
2
+ * Acceptance Stage
3
+ *
4
+ * Runs acceptance tests when all stories are complete.
5
+ * Validates the feature against acceptance criteria from spec.md.
6
+ *
7
+ * Only executes when:
8
+ * - All stories in the PRD are complete (status: passed/failed/skipped, not pending/in-progress)
9
+ * - Acceptance validation is enabled in config
10
+ *
11
+ * @returns
12
+ * - `continue`: All acceptance tests pass
13
+ * - `fail`: One or more acceptance tests failed
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * // All stories complete, acceptance tests pass
18
+ * await acceptanceStage.execute(ctx);
19
+ * // Returns: { action: "continue" }
20
+ *
21
+ * // All stories complete, acceptance tests fail
22
+ * await acceptanceStage.execute(ctx);
23
+ * // Returns: { action: "fail", reason: "Acceptance tests failed: AC-2, AC-5" }
24
+ * ```
25
+ */
26
+
27
+ import path from "node:path";
28
+ import { getLogger } from "../../logger";
29
+ import { countStories } from "../../prd";
30
+ import type { PipelineContext, PipelineStage, StageResult } from "../types";
31
+
32
+ /**
33
+ * Parse bun test output to extract failed test names.
34
+ *
35
+ * Looks for lines containing "AC-N:" to identify which acceptance criteria failed.
36
+ *
37
+ * @param output - stdout/stderr from bun test
38
+ * @returns Array of failed AC IDs (e.g., ["AC-2", "AC-5"])
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const output = `
43
+ * ✓ AC-1: TTL expiry
44
+ * ✗ AC-2: handles empty input
45
+ * ✓ AC-3: validates format
46
+ * `;
47
+ * const failed = parseTestFailures(output);
48
+ * // Returns: ["AC-2"]
49
+ * ```
50
+ */
51
+ function parseTestFailures(output: string): string[] {
52
+ const failedACs: string[] = [];
53
+ const lines = output.split("\n");
54
+
55
+ for (const line of lines) {
56
+ // Look for Bun's (fail) marker followed by AC-N pattern
57
+ // Pattern: (fail) ... > AC-N: description
58
+ if (line.includes("(fail)")) {
59
+ const acMatch = line.match(/(AC-\d+):/i);
60
+ if (acMatch) {
61
+ const acId = acMatch[1].toUpperCase();
62
+ if (!failedACs.includes(acId)) {
63
+ failedACs.push(acId);
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ return failedACs;
70
+ }
71
+
72
+ /**
73
+ * Check if all stories in the PRD are complete.
74
+ *
75
+ * Stories are complete if their status is passed, failed, or skipped.
76
+ * Pending or in-progress stories are not complete.
77
+ *
78
+ * @param ctx - Pipeline context
79
+ * @returns true if all stories complete, false otherwise
80
+ */
81
+ function areAllStoriesComplete(ctx: PipelineContext): boolean {
82
+ const counts = countStories(ctx.prd);
83
+ const totalComplete = counts.passed + counts.failed + counts.skipped;
84
+ return totalComplete === counts.total;
85
+ }
86
+
87
+ export const acceptanceStage: PipelineStage = {
88
+ name: "acceptance",
89
+
90
+ enabled(ctx: PipelineContext): boolean {
91
+ // Only run when:
92
+ // 1. Acceptance validation is enabled
93
+ // 2. All stories are complete
94
+ if (!ctx.config.acceptance.enabled) {
95
+ return false;
96
+ }
97
+
98
+ if (!areAllStoriesComplete(ctx)) {
99
+ return false;
100
+ }
101
+
102
+ return true;
103
+ },
104
+
105
+ async execute(ctx: PipelineContext): Promise<StageResult> {
106
+ const logger = getLogger();
107
+
108
+ logger.info("acceptance", "Running acceptance tests");
109
+
110
+ // Build path to acceptance test file
111
+ if (!ctx.featureDir) {
112
+ logger.warn("acceptance", "No feature directory — skipping acceptance tests");
113
+ return { action: "continue" };
114
+ }
115
+
116
+ const testPath = path.join(ctx.featureDir, ctx.config.acceptance.testPath);
117
+
118
+ // Check if test file exists
119
+ const testFile = Bun.file(testPath);
120
+ const exists = await testFile.exists();
121
+
122
+ if (!exists) {
123
+ logger.warn("acceptance", "Acceptance test file not found — skipping", {
124
+ testPath,
125
+ });
126
+ return { action: "continue" };
127
+ }
128
+
129
+ // Run bun test on the acceptance test file
130
+ const proc = Bun.spawn(["bun", "test", testPath], {
131
+ cwd: ctx.workdir,
132
+ stdout: "pipe",
133
+ stderr: "pipe",
134
+ });
135
+
136
+ const exitCode = await proc.exited;
137
+ const stdout = await new Response(proc.stdout).text();
138
+ const stderr = await new Response(proc.stderr).text();
139
+
140
+ // Combine stdout and stderr for parsing
141
+ const output = `${stdout}\n${stderr}`;
142
+
143
+ // Parse test results
144
+ const failedACs = parseTestFailures(output);
145
+
146
+ // Check for overridden ACs (skip those)
147
+ const overrides = ctx.prd.acceptanceOverrides || {};
148
+ const actualFailures = failedACs.filter((acId) => !overrides[acId]);
149
+
150
+ // If all failed ACs are overridden, treat as success
151
+ if (actualFailures.length === 0 && exitCode === 0) {
152
+ logger.info("acceptance", "All acceptance tests passed");
153
+ return { action: "continue" };
154
+ }
155
+
156
+ if (actualFailures.length === 0 && exitCode !== 0) {
157
+ // Tests failed but we couldn't parse which ACs
158
+ // This might be a setup/teardown error
159
+ logger.warn("acceptance", "Tests failed but no specific AC failures detected", {
160
+ output,
161
+ });
162
+ return { action: "continue" }; // Don't block on unparseable failures
163
+ }
164
+
165
+ // If we have actual failures, report them
166
+ if (actualFailures.length > 0) {
167
+ // Log overridden failures (if any)
168
+ const overriddenFailures = failedACs.filter((acId) => overrides[acId]);
169
+ if (overriddenFailures.length > 0) {
170
+ logger.warn("acceptance", "Skipped failures (overridden)", {
171
+ overriddenFailures,
172
+ overrides: overriddenFailures.map((acId) => ({ acId, reason: overrides[acId] })),
173
+ });
174
+ }
175
+
176
+ logger.error("acceptance", "Acceptance tests failed", {
177
+ failedACs: actualFailures,
178
+ output,
179
+ });
180
+
181
+ // Store failed ACs and test output in context for fix generation
182
+ ctx.acceptanceFailures = {
183
+ failedACs: actualFailures,
184
+ testOutput: output,
185
+ };
186
+
187
+ return {
188
+ action: "fail",
189
+ reason: `Acceptance tests failed: ${actualFailures.join(", ")}`,
190
+ };
191
+ }
192
+
193
+ // All tests passed
194
+ logger.info("acceptance", "All acceptance tests passed");
195
+ return { action: "continue" };
196
+ },
197
+ };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Completion Stage
3
+ *
4
+ * Marks stories as passed, logs progress, fires completion hooks.
5
+ * This is the final stage in the pipeline for successful executions.
6
+ *
7
+ * @returns
8
+ * - `continue`: Stories marked complete, hooks fired
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * // Single story completion
13
+ * await completionStage.execute(ctx);
14
+ * // Logs: "✓ Story US-001 passed"
15
+ * // Fires: on-story-complete hook
16
+ *
17
+ * // Batch completion
18
+ * await completionStage.execute(ctx);
19
+ * // Logs: "✓ Story US-001 passed", "✓ Story US-002 passed", ...
20
+ * // Progress: "📊 Progress: 5/20 stories | ✅ 5 passed | ❌ 0 failed"
21
+ * ```
22
+ */
23
+
24
+ import { hookCtx } from "../../execution/helpers";
25
+ import { appendProgress } from "../../execution/progress";
26
+ import { fireHook } from "../../hooks";
27
+ import { getLogger } from "../../logger";
28
+ import { collectBatchMetrics, collectStoryMetrics } from "../../metrics";
29
+ import { countStories, markStoryPassed, savePRD } from "../../prd";
30
+ import type { PipelineContext, PipelineStage, StageResult } from "../types";
31
+
32
+ export const completionStage: PipelineStage = {
33
+ name: "completion",
34
+ enabled: () => true,
35
+
36
+ async execute(ctx: PipelineContext): Promise<StageResult> {
37
+ const logger = getLogger();
38
+ const isBatch = ctx.stories.length > 1;
39
+ const sessionCost = ctx.agentResult?.estimatedCost || 0;
40
+
41
+ // Calculate PRD path
42
+ const prdPath = ctx.featureDir ? `${ctx.featureDir}/prd.json` : `${ctx.workdir}/nax/features/unknown/prd.json`;
43
+
44
+ // Collect story metrics
45
+ const storyStartTime = ctx.storyStartTime || new Date().toISOString();
46
+ if (isBatch) {
47
+ ctx.storyMetrics = collectBatchMetrics(ctx, storyStartTime);
48
+ } else {
49
+ ctx.storyMetrics = [collectStoryMetrics(ctx, storyStartTime)];
50
+ }
51
+
52
+ // Mark all stories in batch as passed
53
+ for (const completedStory of ctx.stories) {
54
+ markStoryPassed(ctx.prd, completedStory.id);
55
+
56
+ logger.info("completion", "Story passed", {
57
+ storyId: completedStory.id,
58
+ cost: sessionCost / ctx.stories.length,
59
+ });
60
+
61
+ // Log progress
62
+ if (ctx.featureDir) {
63
+ const costPerStory = sessionCost / ctx.stories.length;
64
+ await appendProgress(
65
+ ctx.featureDir,
66
+ completedStory.id,
67
+ "passed",
68
+ `${completedStory.title} — Cost: $${costPerStory.toFixed(4)}${isBatch ? " (batched)" : ""}`,
69
+ );
70
+ }
71
+
72
+ // Fire story-complete hook
73
+ await fireHook(
74
+ ctx.hooks,
75
+ "on-story-complete",
76
+ hookCtx(ctx.prd.feature, {
77
+ storyId: completedStory.id,
78
+ status: "passed",
79
+ cost: sessionCost / ctx.stories.length,
80
+ }),
81
+ ctx.workdir,
82
+ );
83
+ }
84
+
85
+ // Save PRD
86
+ await savePRD(ctx.prd, prdPath);
87
+
88
+ // Display progress
89
+ const updatedCounts = countStories(ctx.prd);
90
+ logger.info("completion", "Progress update", {
91
+ completed: updatedCounts.passed + updatedCounts.failed,
92
+ total: updatedCounts.total,
93
+ passed: updatedCounts.passed,
94
+ failed: updatedCounts.failed,
95
+ });
96
+
97
+ return { action: "continue" };
98
+ },
99
+ };
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Constitution Stage
3
+ *
4
+ * Loads the project constitution (if enabled) and stores it in context.
5
+ * Constitution defines coding standards, architectural rules, and forbidden patterns.
6
+ *
7
+ * @returns
8
+ * - `continue`: Always continues (soft failure if constitution missing)
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * // Constitution enabled and found
13
+ * await constitutionStage.execute(ctx);
14
+ * // ctx.constitution: { content: "...", tokens: 500, truncated: false }
15
+ *
16
+ * // Constitution enabled but not found
17
+ * await constitutionStage.execute(ctx);
18
+ * // ctx.constitution: undefined (stage logs warning and continues)
19
+ * ```
20
+ */
21
+
22
+ import { dirname } from "node:path";
23
+ import { loadConstitution } from "../../constitution";
24
+ import { getLogger } from "../../logger";
25
+ import type { PipelineContext, PipelineStage, StageResult } from "../types";
26
+
27
+ export const constitutionStage: PipelineStage = {
28
+ name: "constitution",
29
+ enabled: (ctx) => ctx.config.constitution.enabled,
30
+
31
+ async execute(ctx: PipelineContext): Promise<StageResult> {
32
+ const logger = getLogger();
33
+
34
+ // Constitution file is in nax/constitution.md
35
+ // featureDir is nax/features/<name>/, so we need to go up two levels
36
+ const ngentDir = ctx.featureDir ? dirname(dirname(ctx.featureDir)) : `${ctx.workdir}/nax`;
37
+
38
+ const result = await loadConstitution(ngentDir, ctx.config.constitution);
39
+
40
+ if (result) {
41
+ ctx.constitution = result;
42
+
43
+ logger.debug("constitution", "Constitution loaded", {
44
+ tokens: result.tokens,
45
+ truncated: result.truncated,
46
+ });
47
+
48
+ if (result.truncated) {
49
+ logger.warn("constitution", "Constitution truncated", {
50
+ originalTokens: result.originalTokens,
51
+ tokens: result.tokens,
52
+ maxTokens: ctx.config.constitution.maxTokens,
53
+ });
54
+ }
55
+ } else {
56
+ // SOFT FAILURE: Constitution missing or failed to load — continue without it
57
+ // This is acceptable because constitution is optional project governance
58
+ logger.debug("constitution", "Constitution not found or failed to load");
59
+ }
60
+
61
+ return { action: "continue" };
62
+ },
63
+ };
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Context Stage
3
+ *
4
+ * Builds contextual information for the agent from the PRD and related stories.
5
+ * After building core context, calls plugin context providers to inject external data.
6
+ * Formats as markdown for inclusion in the prompt.
7
+ *
8
+ * @returns
9
+ * - `continue`: Always continues (soft failure if context empty)
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * // PRD has related stories with context
14
+ * await contextStage.execute(ctx);
15
+ * // ctx.contextMarkdown: "## Related Stories\n- US-001: ..."
16
+ *
17
+ * // No related context found
18
+ * await contextStage.execute(ctx);
19
+ * // ctx.contextMarkdown: "" (empty but continues)
20
+ * ```
21
+ */
22
+
23
+ import type { ContextElement } from "../../context/types";
24
+ import { buildStoryContextFull } from "../../execution/helpers";
25
+ import { getLogger } from "../../logger";
26
+ import type { PipelineContext, PipelineStage, StageResult } from "../types";
27
+
28
+ export const contextStage: PipelineStage = {
29
+ name: "context",
30
+ enabled: () => true,
31
+
32
+ async execute(ctx: PipelineContext): Promise<StageResult> {
33
+ const logger = getLogger();
34
+
35
+ // Build context from PRD with element-level tracking
36
+ const result = await buildStoryContextFull(ctx.prd, ctx.story, ctx.config);
37
+
38
+ // SOFT FAILURE: Empty context is acceptable — agent can work without PRD context
39
+ // This happens when no relevant stories/context is found, which is normal
40
+ if (result) {
41
+ ctx.contextMarkdown = result.markdown;
42
+ ctx.builtContext = result.builtContext;
43
+ } else {
44
+ // Initialize contextMarkdown to empty string if no PRD context was built
45
+ ctx.contextMarkdown = ctx.contextMarkdown || "";
46
+ }
47
+
48
+ // Add plugin context if any providers are registered
49
+ if (ctx.plugins) {
50
+ const providers = ctx.plugins.getContextProviders();
51
+ if (providers.length > 0) {
52
+ logger.info("context", `Running ${providers.length} plugin context provider(s)`);
53
+
54
+ const pluginElements: ContextElement[] = [];
55
+ let pluginTokensUsed = 0;
56
+ const tokenBudget = ctx.config.execution.contextProviderTokenBudget;
57
+
58
+ for (const provider of providers) {
59
+ // Check if we have budget remaining
60
+ if (pluginTokensUsed >= tokenBudget) {
61
+ logger.info("context", "Plugin context budget exhausted, skipping remaining providers");
62
+ break;
63
+ }
64
+
65
+ try {
66
+ logger.info("context", `Fetching context from plugin: ${provider.name}`);
67
+ const providerResult = await provider.getContext(ctx.story);
68
+
69
+ // Check if adding this provider's content would exceed budget
70
+ if (pluginTokensUsed + providerResult.estimatedTokens > tokenBudget) {
71
+ logger.info("context", `Skipping plugin ${provider.name}: would exceed budget`);
72
+ break;
73
+ }
74
+
75
+ // Add plugin context as a new element
76
+ pluginElements.push({
77
+ type: "file", // Reuse file type for external context
78
+ content: `## ${providerResult.label}\n\n${providerResult.content}`,
79
+ priority: 50, // Medium priority (between dependencies and errors)
80
+ tokens: providerResult.estimatedTokens,
81
+ });
82
+
83
+ pluginTokensUsed += providerResult.estimatedTokens;
84
+ logger.info(
85
+ "context",
86
+ `Added context from plugin ${provider.name} (${providerResult.estimatedTokens} tokens)`,
87
+ );
88
+ } catch (error) {
89
+ logger.error("context", `Plugin context provider error: ${provider.name}`, {
90
+ error: error instanceof Error ? error.message : String(error),
91
+ });
92
+ // Continue with other providers on error (soft failure)
93
+ }
94
+ }
95
+
96
+ // Append plugin context to existing markdown
97
+ if (pluginElements.length > 0) {
98
+ const pluginMarkdown = pluginElements.map((el) => el.content).join("\n\n");
99
+ ctx.contextMarkdown = ctx.contextMarkdown ? `${ctx.contextMarkdown}\n\n${pluginMarkdown}` : pluginMarkdown;
100
+
101
+ // Update built context with plugin elements
102
+ if (ctx.builtContext) {
103
+ ctx.builtContext.elements.push(...pluginElements);
104
+ ctx.builtContext.totalTokens += pluginTokensUsed;
105
+ }
106
+
107
+ logger.info(
108
+ "context",
109
+ `Added ${pluginElements.length} plugin context element(s) (${pluginTokensUsed} tokens total)`,
110
+ );
111
+ }
112
+ }
113
+ }
114
+
115
+ return { action: "continue" };
116
+ },
117
+ };
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Execution Stage
3
+ *
4
+ * Spawns the agent session(s) to execute the story/stories.
5
+ * Handles both single-session (test-after) and three-session TDD.
6
+ *
7
+ * @returns
8
+ * - `continue`: Agent session succeeded
9
+ * - `fail`: Agent not found or prompt missing
10
+ * - `escalate`: Agent session failed (will retry with higher tier)
11
+ * - `pause`: Three-session TDD fallback (backward compatible, no failureCategory)
12
+ *
13
+ * TDD failure routing by failureCategory:
14
+ * - `isolation-violation` (strict mode) → escalate + ctx.retryAsLite=true
15
+ * - `isolation-violation` (lite mode) → escalate
16
+ * - `session-failure` → escalate
17
+ * - `tests-failing` → escalate
18
+ * - `verifier-rejected` → escalate
19
+ * - no category / unknown → pause (backward compatible)
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * // Single session (test-after)
24
+ * await executionStage.execute(ctx);
25
+ * // ctx.agentResult: { success: true, estimatedCost: 0.05, ... }
26
+ *
27
+ * // Three-session TDD
28
+ * await executionStage.execute(ctx);
29
+ * // ctx.agentResult: { success: true, estimatedCost: 0.15, ... }
30
+ * ```
31
+ */
32
+
33
+ import { getAgent, validateAgentForTier } from "../../agents";
34
+ import { resolveModel } from "../../config";
35
+ import { getLogger } from "../../logger";
36
+ import type { FailureCategory } from "../../tdd";
37
+ import { runThreeSessionTdd } from "../../tdd";
38
+ import type { PipelineContext, PipelineStage, StageResult } from "../types";
39
+
40
+ /**
41
+ * Determine the pipeline action for a failed TDD result, based on its failureCategory.
42
+ *
43
+ * This is a pure routing function — it mutates only `ctx.retryAsLite` when needed.
44
+ * Exported for unit testing.
45
+ *
46
+ * @param failureCategory - Category set by the TDD orchestrator (or undefined)
47
+ * @param isLiteMode - Whether the story was running in tdd-lite mode
48
+ * @param ctx - Pipeline context (mutated: ctx.retryAsLite may be set)
49
+ * @param reviewReason - Human-readable reason string from the TDD result
50
+ */
51
+ export function routeTddFailure(
52
+ failureCategory: FailureCategory | undefined,
53
+ isLiteMode: boolean,
54
+ ctx: Pick<PipelineContext, "retryAsLite">,
55
+ reviewReason?: string,
56
+ ): StageResult {
57
+ if (failureCategory === "isolation-violation") {
58
+ // Strict mode: request a lite-mode retry on next attempt
59
+ if (!isLiteMode) {
60
+ ctx.retryAsLite = true;
61
+ }
62
+ return { action: "escalate" };
63
+ }
64
+
65
+ if (
66
+ failureCategory === "session-failure" ||
67
+ failureCategory === "tests-failing" ||
68
+ failureCategory === "verifier-rejected"
69
+ ) {
70
+ return { action: "escalate" };
71
+ }
72
+
73
+ // Default: no category or unknown — backward-compatible pause for human review
74
+ return {
75
+ action: "pause",
76
+ reason: reviewReason || "Three-session TDD requires review",
77
+ };
78
+ }
79
+
80
+ export const executionStage: PipelineStage = {
81
+ name: "execution",
82
+ enabled: () => true,
83
+
84
+ async execute(ctx: PipelineContext): Promise<StageResult> {
85
+ const logger = getLogger();
86
+
87
+ // HARD FAILURE: No agent available — cannot proceed without an agent
88
+ const agent = getAgent(ctx.config.autoMode.defaultAgent);
89
+ if (!agent) {
90
+ return {
91
+ action: "fail",
92
+ reason: `Agent "${ctx.config.autoMode.defaultAgent}" not found`,
93
+ };
94
+ }
95
+
96
+ // Three-session TDD path (respect tdd.enabled config)
97
+ const isTddStrategy =
98
+ ctx.routing.testStrategy === "three-session-tdd" || ctx.routing.testStrategy === "three-session-tdd-lite";
99
+ const isLiteMode = ctx.routing.testStrategy === "three-session-tdd-lite";
100
+
101
+ // TYPE-2 fix: TddConfig has no enabled field, removed dead code
102
+ if (isTddStrategy) {
103
+ logger.info("execution", `Starting three-session TDD${isLiteMode ? " (lite)" : ""}`, {
104
+ storyId: ctx.story.id,
105
+ lite: isLiteMode,
106
+ });
107
+
108
+ const tddResult = await runThreeSessionTdd({
109
+ agent,
110
+ story: ctx.story,
111
+ config: ctx.config,
112
+ workdir: ctx.workdir,
113
+ modelTier: ctx.routing.modelTier,
114
+ contextMarkdown: ctx.contextMarkdown,
115
+ dryRun: false,
116
+ lite: isLiteMode,
117
+ });
118
+
119
+ ctx.agentResult = {
120
+ success: tddResult.success,
121
+ estimatedCost: tddResult.totalCost,
122
+ rateLimited: false,
123
+ output: "",
124
+ exitCode: tddResult.success ? 0 : 1,
125
+ durationMs: 0, // TDD result doesn't track total duration
126
+ };
127
+
128
+ if (!tddResult.success) {
129
+ // Store failure category in context for runner to use at max-attempts decision
130
+ ctx.tddFailureCategory = tddResult.failureCategory;
131
+
132
+ // Log needsHumanReview context when present
133
+ if (tddResult.needsHumanReview) {
134
+ logger.warn("execution", "Human review needed", {
135
+ storyId: ctx.story.id,
136
+ reason: tddResult.reviewReason,
137
+ lite: tddResult.lite,
138
+ failureCategory: tddResult.failureCategory,
139
+ });
140
+ }
141
+
142
+ return routeTddFailure(tddResult.failureCategory, isLiteMode, ctx, tddResult.reviewReason);
143
+ }
144
+
145
+ return { action: "continue" };
146
+ }
147
+
148
+ // Single/batch session (test-after) path
149
+ // HARD FAILURE: Missing prompt indicates pipeline misconfiguration
150
+ if (!ctx.prompt) {
151
+ return { action: "fail", reason: "Prompt not built (prompt stage skipped?)" };
152
+ }
153
+
154
+ // Validate agent supports the requested tier
155
+ if (!validateAgentForTier(agent, ctx.routing.modelTier)) {
156
+ logger.warn("execution", "Agent tier mismatch", {
157
+ storyId: ctx.story.id,
158
+ agentName: agent.name,
159
+ requestedTier: ctx.routing.modelTier,
160
+ supportedTiers: agent.capabilities.supportedTiers,
161
+ });
162
+ }
163
+
164
+ const result = await agent.run({
165
+ prompt: ctx.prompt,
166
+ workdir: ctx.workdir,
167
+ modelTier: ctx.routing.modelTier,
168
+ modelDef: resolveModel(ctx.config.models[ctx.routing.modelTier]),
169
+ timeoutSeconds: ctx.config.execution.sessionTimeoutSeconds,
170
+ dangerouslySkipPermissions: ctx.config.execution.dangerouslySkipPermissions,
171
+ });
172
+
173
+ ctx.agentResult = result;
174
+
175
+ if (!result.success) {
176
+ logger.error("execution", "Agent session failed", {
177
+ exitCode: result.exitCode,
178
+ stderr: result.stderr || "",
179
+ rateLimited: result.rateLimited,
180
+ storyId: ctx.story.id,
181
+ });
182
+ if (result.rateLimited) {
183
+ logger.warn("execution", "Rate limited — will retry", { storyId: ctx.story.id });
184
+ }
185
+ return { action: "escalate" };
186
+ }
187
+
188
+ logger.info("execution", "Agent session complete", {
189
+ storyId: ctx.story.id,
190
+ cost: result.estimatedCost,
191
+ });
192
+ return { action: "continue" };
193
+ },
194
+ };