@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,702 @@
1
+ /**
2
+ * Integration tests for context builder with execution runner
3
+ */
4
+
5
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
6
+ import { DEFAULT_CONFIG } from "../../src/config";
7
+ import { buildContext, formatContextAsMarkdown } from "../../src/context/builder";
8
+ import type { ContextBudget, StoryContext } from "../../src/context/types";
9
+ import { run } from "../../src/execution/runner";
10
+ import type { RunOptions } from "../../src/execution/runner";
11
+ import type { PRD, UserStory } from "../../src/prd";
12
+
13
+ // Sample PRD for testing
14
+ const createTestPRD = (stories: Partial<UserStory>[]): PRD => ({
15
+ project: "test-project",
16
+ feature: "test-feature",
17
+ branchName: "test-branch",
18
+ createdAt: new Date().toISOString(),
19
+ updatedAt: new Date().toISOString(),
20
+ userStories: stories.map((s, i) => ({
21
+ id: s.id || `US-${String(i + 1).padStart(3, "0")}`,
22
+ title: s.title || "Test Story",
23
+ description: s.description || "Test description",
24
+ acceptanceCriteria: s.acceptanceCriteria || ["AC1"],
25
+ dependencies: s.dependencies || [],
26
+ tags: s.tags || [],
27
+ status: s.status || "pending",
28
+ passes: s.passes ?? false,
29
+ escalations: s.escalations || [],
30
+ attempts: s.attempts || 0,
31
+ routing: s.routing,
32
+ priorErrors: s.priorErrors,
33
+ })),
34
+ });
35
+
36
+ let tmpDirs: string[] = [];
37
+
38
+ const createTmpDir = async (): Promise<string> => {
39
+ const tmpDir = `/tmp/nax-context-test-${Date.now()}-${Math.random()}`;
40
+ await Bun.spawn(["mkdir", "-p", tmpDir], { stdout: "pipe" }).exited;
41
+ tmpDirs.push(tmpDir);
42
+ return tmpDir;
43
+ };
44
+
45
+ afterEach(async () => {
46
+ // Cleanup all temporary directories
47
+ for (const tmpDir of tmpDirs) {
48
+ await Bun.spawn(["rm", "-rf", tmpDir], { stdout: "pipe" }).exited;
49
+ }
50
+ tmpDirs = [];
51
+ });
52
+
53
+ describe("Context Builder Integration", () => {
54
+ describe("Runner Integration", () => {
55
+ test("should use context builder by default", async () => {
56
+ const prd = createTestPRD([
57
+ {
58
+ id: "US-001",
59
+ title: "Fix typo",
60
+ description: "Fix a typo in error message",
61
+ acceptanceCriteria: ["Typo is fixed"],
62
+ tags: [],
63
+ },
64
+ ]);
65
+
66
+ const tmpDir = await createTmpDir();
67
+ const prdPath = `${tmpDir}/prd.json`;
68
+ await Bun.write(prdPath, JSON.stringify(prd, null, 2));
69
+
70
+ const opts: RunOptions = {
71
+ prdPath,
72
+ workdir: tmpDir,
73
+ config: {
74
+ ...DEFAULT_CONFIG,
75
+ execution: { ...DEFAULT_CONFIG.execution, maxIterations: 1 },
76
+ },
77
+ hooks: { hooks: {} },
78
+ feature: "test-feature",
79
+ dryRun: true,
80
+ useContext: true, // Default behavior
81
+ skipPrecheck: true,
82
+ };
83
+
84
+ const result = await run(opts);
85
+
86
+ expect(result.iterations).toBeGreaterThan(0);
87
+ // In a full implementation, we'd verify context was included in prompt
88
+ });
89
+
90
+ test("should skip context builder when useContext is false", async () => {
91
+ const prd = createTestPRD([
92
+ {
93
+ id: "US-001",
94
+ title: "Fix typo",
95
+ description: "Fix a typo in error message",
96
+ acceptanceCriteria: ["Typo is fixed"],
97
+ tags: [],
98
+ },
99
+ ]);
100
+
101
+ const tmpDir = await createTmpDir();
102
+ const prdPath = `${tmpDir}/prd.json`;
103
+ await Bun.write(prdPath, JSON.stringify(prd, null, 2));
104
+
105
+ const opts: RunOptions = {
106
+ prdPath,
107
+ workdir: tmpDir,
108
+ config: {
109
+ ...DEFAULT_CONFIG,
110
+ execution: { ...DEFAULT_CONFIG.execution, maxIterations: 1 },
111
+ },
112
+ hooks: { hooks: {} },
113
+ feature: "test-feature",
114
+ dryRun: true,
115
+ useContext: false, // Disable context builder
116
+ skipPrecheck: true,
117
+ };
118
+
119
+ const result = await run(opts);
120
+
121
+ expect(result.iterations).toBeGreaterThan(0);
122
+ // In a full implementation, we'd verify context was NOT included
123
+ });
124
+
125
+ test("should handle context builder errors gracefully", async () => {
126
+ const prd = createTestPRD([
127
+ {
128
+ id: "US-001",
129
+ title: "Task with invalid reference",
130
+ description: "This task has an issue",
131
+ acceptanceCriteria: ["Works"],
132
+ tags: [],
133
+ },
134
+ ]);
135
+
136
+ const tmpDir = await createTmpDir();
137
+ const prdPath = `${tmpDir}/prd.json`;
138
+ await Bun.write(prdPath, JSON.stringify(prd, null, 2));
139
+
140
+ const opts: RunOptions = {
141
+ prdPath,
142
+ workdir: tmpDir,
143
+ config: {
144
+ ...DEFAULT_CONFIG,
145
+ execution: { ...DEFAULT_CONFIG.execution, maxIterations: 1 },
146
+ },
147
+ hooks: { hooks: {} },
148
+ feature: "test-feature",
149
+ dryRun: true,
150
+ useContext: true,
151
+ skipPrecheck: true,
152
+ };
153
+
154
+ // Should not throw even if context builder encounters errors
155
+ const result = await run(opts);
156
+
157
+ expect(result.iterations).toBeGreaterThan(0);
158
+ });
159
+ });
160
+
161
+ describe("Story Extraction", () => {
162
+ test("should extract current story from PRD", async () => {
163
+ const prd = createTestPRD([
164
+ {
165
+ id: "US-001",
166
+ title: "First Story",
167
+ description: "First description",
168
+ acceptanceCriteria: ["AC1"],
169
+ },
170
+ {
171
+ id: "US-002",
172
+ title: "Second Story",
173
+ description: "Second description",
174
+ acceptanceCriteria: ["AC2"],
175
+ },
176
+ ]);
177
+
178
+ const storyContext: StoryContext = {
179
+ prd,
180
+ currentStoryId: "US-001",
181
+ };
182
+
183
+ const budget: ContextBudget = {
184
+ maxTokens: 10000,
185
+ reservedForInstructions: 1000,
186
+ availableForContext: 9000,
187
+ };
188
+
189
+ const built = await buildContext(storyContext, budget);
190
+
191
+ expect(built.elements.some((e) => e.type === "story" && e.storyId === "US-001")).toBe(true);
192
+ expect(built.elements.some((e) => e.type === "story" && e.storyId === "US-002")).toBe(false);
193
+ });
194
+
195
+ test("should include dependency stories", async () => {
196
+ const prd = createTestPRD([
197
+ {
198
+ id: "US-001",
199
+ title: "Dependency Story",
200
+ description: "Dependency description",
201
+ acceptanceCriteria: ["AC1"],
202
+ status: "passed",
203
+ passes: true,
204
+ },
205
+ {
206
+ id: "US-002",
207
+ title: "Current Story",
208
+ description: "Current description",
209
+ acceptanceCriteria: ["AC2"],
210
+ dependencies: ["US-001"],
211
+ },
212
+ ]);
213
+
214
+ const storyContext: StoryContext = {
215
+ prd,
216
+ currentStoryId: "US-002",
217
+ };
218
+
219
+ const budget: ContextBudget = {
220
+ maxTokens: 10000,
221
+ reservedForInstructions: 1000,
222
+ availableForContext: 9000,
223
+ };
224
+
225
+ const built = await buildContext(storyContext, budget);
226
+ const markdown = formatContextAsMarkdown(built);
227
+
228
+ expect(built.elements.some((e) => e.type === "dependency" && e.storyId === "US-001")).toBe(true);
229
+ expect(markdown).toContain("## Dependency Stories");
230
+ expect(markdown).toContain("US-001");
231
+ });
232
+
233
+ test("should include progress summary", async () => {
234
+ const prd = createTestPRD([
235
+ { id: "US-001", status: "passed", passes: true },
236
+ { id: "US-002", status: "passed", passes: true },
237
+ { id: "US-003", status: "failed", passes: false },
238
+ { id: "US-004", status: "pending", passes: false },
239
+ ]);
240
+
241
+ const storyContext: StoryContext = {
242
+ prd,
243
+ currentStoryId: "US-004",
244
+ };
245
+
246
+ const budget: ContextBudget = {
247
+ maxTokens: 10000,
248
+ reservedForInstructions: 1000,
249
+ availableForContext: 9000,
250
+ };
251
+
252
+ const built = await buildContext(storyContext, budget);
253
+ const markdown = formatContextAsMarkdown(built);
254
+
255
+ expect(markdown).toContain("Progress:");
256
+ expect(markdown).toContain("3/4 stories complete");
257
+ expect(markdown).toContain("2 passed");
258
+ expect(markdown).toContain("1 failed");
259
+ });
260
+
261
+ test("should include prior errors", async () => {
262
+ const prd = createTestPRD([
263
+ {
264
+ id: "US-001",
265
+ title: "Failed Story",
266
+ description: "Story with errors",
267
+ acceptanceCriteria: ["AC1"],
268
+ priorErrors: ["TypeError: Cannot read property", "ReferenceError: x is not defined"],
269
+ },
270
+ ]);
271
+
272
+ const storyContext: StoryContext = {
273
+ prd,
274
+ currentStoryId: "US-001",
275
+ };
276
+
277
+ const budget: ContextBudget = {
278
+ maxTokens: 10000,
279
+ reservedForInstructions: 1000,
280
+ availableForContext: 9000,
281
+ };
282
+
283
+ const built = await buildContext(storyContext, budget);
284
+ const markdown = formatContextAsMarkdown(built);
285
+
286
+ expect(markdown).toContain("## Prior Errors");
287
+ expect(markdown).toContain("TypeError: Cannot read property");
288
+ expect(markdown).toContain("ReferenceError: x is not defined");
289
+ });
290
+
291
+ test("should handle missing dependency gracefully", async () => {
292
+ const prd = createTestPRD([
293
+ {
294
+ id: "US-001",
295
+ title: "Story with missing dependency",
296
+ description: "Current story",
297
+ acceptanceCriteria: ["AC1"],
298
+ dependencies: ["US-999"], // Non-existent
299
+ },
300
+ ]);
301
+
302
+ const storyContext: StoryContext = {
303
+ prd,
304
+ currentStoryId: "US-001",
305
+ };
306
+
307
+ const budget: ContextBudget = {
308
+ maxTokens: 10000,
309
+ reservedForInstructions: 1000,
310
+ availableForContext: 9000,
311
+ };
312
+
313
+ const built = await buildContext(storyContext, budget);
314
+
315
+ // Should not crash, just skip the missing dependency
316
+ expect(built.elements.some((e) => e.type === "dependency")).toBe(false);
317
+ });
318
+ });
319
+
320
+ describe("Context Prioritization", () => {
321
+ test("should prioritize progress over other elements", async () => {
322
+ const prd = createTestPRD([
323
+ {
324
+ id: "US-001",
325
+ title: "Story",
326
+ priorErrors: ["Error 1"],
327
+ dependencies: ["US-002"],
328
+ },
329
+ { id: "US-002", status: "passed", passes: true },
330
+ ]);
331
+
332
+ const storyContext: StoryContext = {
333
+ prd,
334
+ currentStoryId: "US-001",
335
+ };
336
+
337
+ const budget: ContextBudget = {
338
+ maxTokens: 10000,
339
+ reservedForInstructions: 1000,
340
+ availableForContext: 9000,
341
+ };
342
+
343
+ const built = await buildContext(storyContext, budget);
344
+
345
+ // Progress should be first (highest priority)
346
+ expect(built.elements[0].type).toBe("progress");
347
+ });
348
+
349
+ test("should prioritize errors over stories", async () => {
350
+ const prd = createTestPRD([
351
+ {
352
+ id: "US-001",
353
+ title: "Story",
354
+ priorErrors: ["Critical error"],
355
+ dependencies: ["US-002"],
356
+ },
357
+ { id: "US-002", status: "passed", passes: true },
358
+ ]);
359
+
360
+ const storyContext: StoryContext = {
361
+ prd,
362
+ currentStoryId: "US-001",
363
+ };
364
+
365
+ const budget: ContextBudget = {
366
+ maxTokens: 10000,
367
+ reservedForInstructions: 1000,
368
+ availableForContext: 9000,
369
+ };
370
+
371
+ const built = await buildContext(storyContext, budget);
372
+
373
+ // Errors should come before story and dependencies
374
+ const errorIndex = built.elements.findIndex((e) => e.type === "error");
375
+ const storyIndex = built.elements.findIndex((e) => e.type === "story");
376
+ const depIndex = built.elements.findIndex((e) => e.type === "dependency");
377
+
378
+ expect(errorIndex).toBeLessThan(storyIndex);
379
+ expect(errorIndex).toBeLessThan(depIndex);
380
+ });
381
+
382
+ test("should truncate low-priority items when budget exceeded", async () => {
383
+ const prd = createTestPRD([
384
+ {
385
+ id: "US-001",
386
+ title: "Story with many dependencies",
387
+ description: "x".repeat(2000),
388
+ acceptanceCriteria: ["AC1"],
389
+ dependencies: ["US-002", "US-003", "US-004", "US-005"],
390
+ },
391
+ { id: "US-002", description: "x".repeat(2000), acceptanceCriteria: ["AC2"] },
392
+ { id: "US-003", description: "x".repeat(2000), acceptanceCriteria: ["AC3"] },
393
+ { id: "US-004", description: "x".repeat(2000), acceptanceCriteria: ["AC4"] },
394
+ { id: "US-005", description: "x".repeat(2000), acceptanceCriteria: ["AC5"] },
395
+ ]);
396
+
397
+ const storyContext: StoryContext = {
398
+ prd,
399
+ currentStoryId: "US-001",
400
+ };
401
+
402
+ const budget: ContextBudget = {
403
+ maxTokens: 2000,
404
+ reservedForInstructions: 1000,
405
+ availableForContext: 1000, // Small budget
406
+ };
407
+
408
+ const built = await buildContext(storyContext, budget);
409
+
410
+ expect(built.truncated).toBe(true);
411
+ expect(built.totalTokens).toBeLessThanOrEqual(1000);
412
+ // Progress should always be included (highest priority)
413
+ expect(built.elements.some((e) => e.type === "progress")).toBe(true);
414
+ // Current story should be included (high priority)
415
+ expect(built.elements.some((e) => e.type === "story")).toBe(true);
416
+ // Some dependencies may be truncated (lower priority)
417
+ const depCount = built.elements.filter((e) => e.type === "dependency").length;
418
+ expect(depCount).toBeLessThan(4); // Not all dependencies included
419
+ });
420
+ });
421
+
422
+ describe("Markdown Formatting", () => {
423
+ test("should format context with all sections", async () => {
424
+ const prd = createTestPRD([
425
+ {
426
+ id: "US-001",
427
+ title: "Dependency",
428
+ description: "Dep description",
429
+ acceptanceCriteria: ["AC1"],
430
+ status: "passed",
431
+ passes: true,
432
+ },
433
+ {
434
+ id: "US-002",
435
+ title: "Current",
436
+ description: "Current description",
437
+ acceptanceCriteria: ["AC2"],
438
+ dependencies: ["US-001"],
439
+ priorErrors: ["Test error"],
440
+ },
441
+ ]);
442
+
443
+ const storyContext: StoryContext = {
444
+ prd,
445
+ currentStoryId: "US-002",
446
+ };
447
+
448
+ const budget: ContextBudget = {
449
+ maxTokens: 10000,
450
+ reservedForInstructions: 1000,
451
+ availableForContext: 9000,
452
+ };
453
+
454
+ const built = await buildContext(storyContext, budget);
455
+ const markdown = formatContextAsMarkdown(built);
456
+
457
+ // Verify all sections are present
458
+ expect(markdown).toContain("# Story Context");
459
+ expect(markdown).toContain("## Progress");
460
+ expect(markdown).toContain("## Prior Errors");
461
+ expect(markdown).toContain("## Current Story");
462
+ expect(markdown).toContain("## Dependency Stories");
463
+
464
+ // Verify content
465
+ expect(markdown).toContain("US-001");
466
+ expect(markdown).toContain("US-002");
467
+ expect(markdown).toContain("Test error");
468
+ expect(markdown).toContain("Dep description");
469
+ expect(markdown).toContain("Current description");
470
+ });
471
+
472
+ test("should include summary with token count", async () => {
473
+ const prd = createTestPRD([{ id: "US-001", title: "Story" }]);
474
+
475
+ const storyContext: StoryContext = {
476
+ prd,
477
+ currentStoryId: "US-001",
478
+ };
479
+
480
+ const budget: ContextBudget = {
481
+ maxTokens: 10000,
482
+ reservedForInstructions: 1000,
483
+ availableForContext: 9000,
484
+ };
485
+
486
+ const built = await buildContext(storyContext, budget);
487
+ const markdown = formatContextAsMarkdown(built);
488
+
489
+ expect(markdown).toContain("Context:");
490
+ expect(markdown).toContain("tokens");
491
+ expect(markdown).toContain(built.totalTokens.toString());
492
+ });
493
+
494
+ test("should show truncation indicator", async () => {
495
+ const prd = createTestPRD([
496
+ {
497
+ id: "US-001",
498
+ description: "x".repeat(3000),
499
+ dependencies: ["US-002", "US-003"],
500
+ },
501
+ { id: "US-002", description: "x".repeat(3000) },
502
+ { id: "US-003", description: "x".repeat(3000) },
503
+ ]);
504
+
505
+ const storyContext: StoryContext = {
506
+ prd,
507
+ currentStoryId: "US-001",
508
+ };
509
+
510
+ const budget: ContextBudget = {
511
+ maxTokens: 500,
512
+ reservedForInstructions: 250,
513
+ availableForContext: 250,
514
+ };
515
+
516
+ const built = await buildContext(storyContext, budget);
517
+ const markdown = formatContextAsMarkdown(built);
518
+
519
+ expect(markdown).toContain("[TRUNCATED]");
520
+ });
521
+ });
522
+
523
+ describe("contextFiles and expectedFiles", () => {
524
+ test("should use contextFiles when present", async () => {
525
+ const prd = createTestPRD([
526
+ {
527
+ id: "US-001",
528
+ title: "Story with contextFiles",
529
+ description: "Test contextFiles usage",
530
+ acceptanceCriteria: ["AC1"],
531
+ contextFiles: ["src/foo.ts", "src/bar.ts"],
532
+ },
533
+ ]);
534
+
535
+ const storyContext: StoryContext = {
536
+ prd,
537
+ currentStoryId: "US-001",
538
+ };
539
+
540
+ const budget: ContextBudget = {
541
+ maxTokens: 10000,
542
+ reservedForInstructions: 1000,
543
+ availableForContext: 9000,
544
+ };
545
+
546
+ const built = await buildContext(storyContext, budget);
547
+ const markdown = formatContextAsMarkdown(built);
548
+
549
+ // Context builder should attempt to load contextFiles
550
+ expect(built.elements.some((e) => e.type === "story")).toBe(true);
551
+ expect(markdown).toContain("US-001");
552
+ });
553
+
554
+ test("should fall back to relevantFiles for context when contextFiles not set", async () => {
555
+ const prd = createTestPRD([
556
+ {
557
+ id: "US-001",
558
+ title: "Legacy story with relevantFiles",
559
+ description: "Test relevantFiles fallback",
560
+ acceptanceCriteria: ["AC1"],
561
+ relevantFiles: ["src/legacy.ts"],
562
+ },
563
+ ]);
564
+
565
+ const storyContext: StoryContext = {
566
+ prd,
567
+ currentStoryId: "US-001",
568
+ };
569
+
570
+ const budget: ContextBudget = {
571
+ maxTokens: 10000,
572
+ reservedForInstructions: 1000,
573
+ availableForContext: 9000,
574
+ };
575
+
576
+ const built = await buildContext(storyContext, budget);
577
+ const markdown = formatContextAsMarkdown(built);
578
+
579
+ // Should still build context successfully using relevantFiles fallback
580
+ expect(built.elements.some((e) => e.type === "story")).toBe(true);
581
+ expect(markdown).toContain("US-001");
582
+ });
583
+
584
+ test("should handle story with no files specified", async () => {
585
+ const prd = createTestPRD([
586
+ {
587
+ id: "US-001",
588
+ title: "Story with no files",
589
+ description: "Test no files case",
590
+ acceptanceCriteria: ["AC1"],
591
+ },
592
+ ]);
593
+
594
+ const storyContext: StoryContext = {
595
+ prd,
596
+ currentStoryId: "US-001",
597
+ };
598
+
599
+ const budget: ContextBudget = {
600
+ maxTokens: 10000,
601
+ reservedForInstructions: 1000,
602
+ availableForContext: 9000,
603
+ };
604
+
605
+ const built = await buildContext(storyContext, budget);
606
+ const markdown = formatContextAsMarkdown(built);
607
+
608
+ // Should build context successfully without file loading
609
+ expect(built.elements.some((e) => e.type === "story")).toBe(true);
610
+ expect(markdown).toContain("US-001");
611
+ });
612
+ });
613
+
614
+ describe("Edge Cases", () => {
615
+ test("should handle single story PRD", async () => {
616
+ const prd = createTestPRD([
617
+ {
618
+ id: "US-001",
619
+ title: "Only Story",
620
+ description: "The only story",
621
+ acceptanceCriteria: ["AC1"],
622
+ },
623
+ ]);
624
+
625
+ const storyContext: StoryContext = {
626
+ prd,
627
+ currentStoryId: "US-001",
628
+ };
629
+
630
+ const budget: ContextBudget = {
631
+ maxTokens: 10000,
632
+ reservedForInstructions: 1000,
633
+ availableForContext: 9000,
634
+ };
635
+
636
+ const built = await buildContext(storyContext, budget);
637
+ const markdown = formatContextAsMarkdown(built);
638
+
639
+ expect(built.elements.length).toBeGreaterThan(0);
640
+ expect(markdown).toContain("US-001");
641
+ expect(markdown).toContain("Progress: 0/1 stories complete");
642
+ });
643
+
644
+ test("should handle story with no dependencies", async () => {
645
+ const prd = createTestPRD([
646
+ {
647
+ id: "US-001",
648
+ title: "Independent Story",
649
+ description: "No dependencies",
650
+ acceptanceCriteria: ["AC1"],
651
+ dependencies: [],
652
+ },
653
+ ]);
654
+
655
+ const storyContext: StoryContext = {
656
+ prd,
657
+ currentStoryId: "US-001",
658
+ };
659
+
660
+ const budget: ContextBudget = {
661
+ maxTokens: 10000,
662
+ reservedForInstructions: 1000,
663
+ availableForContext: 9000,
664
+ };
665
+
666
+ const built = await buildContext(storyContext, budget);
667
+
668
+ expect(built.elements.some((e) => e.type === "dependency")).toBe(false);
669
+ });
670
+
671
+ test("should handle extremely small budget gracefully", async () => {
672
+ const prd = createTestPRD([
673
+ {
674
+ id: "US-001",
675
+ description: "x".repeat(5000),
676
+ priorErrors: ["Error".repeat(1000)],
677
+ dependencies: ["US-002", "US-003"],
678
+ },
679
+ { id: "US-002", description: "x".repeat(5000) },
680
+ { id: "US-003", description: "x".repeat(5000) },
681
+ ]);
682
+
683
+ const storyContext: StoryContext = {
684
+ prd,
685
+ currentStoryId: "US-001",
686
+ };
687
+
688
+ const budget: ContextBudget = {
689
+ maxTokens: 100,
690
+ reservedForInstructions: 50,
691
+ availableForContext: 50, // Extremely small
692
+ };
693
+
694
+ const built = await buildContext(storyContext, budget);
695
+
696
+ expect(built.totalTokens).toBeLessThanOrEqual(50);
697
+ expect(built.truncated).toBe(true);
698
+ // At minimum, progress should fit
699
+ expect(built.elements.length).toBeGreaterThan(0);
700
+ });
701
+ });
702
+ });