@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.
- package/.gitlab-ci.yml +96 -0
- package/BRIEF.md +140 -0
- package/CHANGELOG.md +60 -0
- package/CLAUDE.md +159 -0
- package/README.md +373 -0
- package/US-007-IMPLEMENTATION.md +139 -0
- package/bin/nax.ts +930 -0
- package/biome.json +14 -0
- package/bun.lock +168 -0
- package/bunfig.toml +11 -0
- package/docs/20260216-fix-plan-context-review.md +56 -0
- package/docs/20260216-relentless-vs-ngent-comparison.md +208 -0
- package/docs/20260216-v02-plan.md +136 -0
- package/docs/20260216-v02-review.md +685 -0
- package/docs/20260217-dogfood-findings.md +56 -0
- package/docs/20260217-p2-plus-plan.md +117 -0
- package/docs/20260217-partial-fixes-plan.md +62 -0
- package/docs/20260217-plan-analyze-spec.md +117 -0
- package/docs/20260217-post-impl-review.md +1137 -0
- package/docs/20260217-quick-wins-plan.md +66 -0
- package/docs/20260217-split-runner-plan.md +75 -0
- package/docs/20260217-v03-impl-plan.md +80 -0
- package/docs/20260217-v03-post-impl-review.md +589 -0
- package/docs/20260217-v04-impl-plan.md +86 -0
- package/docs/20260217-v05-post-impl-review.md +850 -0
- package/docs/20260217-v06-post-impl-review.md +817 -0
- package/docs/20260218-adr003-port-plan.md +151 -0
- package/docs/20260218-review-adr003-verification.md +175 -0
- package/docs/20260219-fix-plan-bug16-19.md +79 -0
- package/docs/20260219-fix-plan-bug20-22.md +114 -0
- package/docs/20260219-plan-llm-routing.md +116 -0
- package/docs/20260219-review-bug20-22-fixes.md +135 -0
- package/docs/20260219-routing-baseline-keyword.md +63 -0
- package/docs/20260220-plan-structured-logging-p1.md +80 -0
- package/docs/20260220-plan-structured-logging-p2.md +37 -0
- package/docs/20260220-review-llm-routing.md +180 -0
- package/docs/20260220-review-post-fix-llm-routing.md +70 -0
- package/docs/20260221-fix-plan-relevantfiles-split.md +101 -0
- package/docs/20260221-fix-plan-routing-mode.md +125 -0
- package/docs/20260221-review-v0.9-implementation.md +379 -0
- package/docs/20260222-fix-plan-v091-routing-isolation.md +197 -0
- package/docs/20260223-fix-plan-prompt-audit.md +62 -0
- package/docs/20260224-nax-roadmap-phases.md +189 -0
- package/docs/20260225-phase2-llm-service-layer.md +401 -0
- package/docs/20260225-review-v0.10.1.md +187 -0
- package/docs/20260303-v010-implementation-plan.md +165 -0
- package/docs/CLAUDE.md.bak +191 -0
- package/docs/ROADMAP.md +165 -0
- package/docs/SPEC-rectification.md +0 -0
- package/docs/SPEC.md +324 -0
- package/docs/US-001-plugin-loading-verification.md +152 -0
- package/docs/architecture-analysis.md +1076 -0
- package/docs/bugs/BUG-21-escalation-null-attempts.md +48 -0
- package/docs/bugs-from-dogfood-run-c.md +243 -0
- package/docs/code-review-20260228.md +612 -0
- package/docs/code-review-v0.15.0.md +629 -0
- package/docs/hook-lifecycle-test-plan.md +149 -0
- package/docs/releases/v0.11.0-and-earlier.md +20 -0
- package/docs/releases/v0.12.0.md +15 -0
- package/docs/releases/v0.13.0.md +14 -0
- package/docs/releases/v0.14.0.md +20 -0
- package/docs/releases/v0.14.1.md +36 -0
- package/docs/releases/v0.14.2.md +51 -0
- package/docs/releases/v0.14.3.md +174 -0
- package/docs/releases/v0.14.4.md +94 -0
- package/docs/releases/v0.15.0.md +502 -0
- package/docs/releases/v0.15.1.md +170 -0
- package/docs/releases/v0.15.3.md +193 -0
- package/docs/specs/status-file-v0.10.1.md +812 -0
- package/docs/v0.10-global-config.md +206 -0
- package/docs/v0.10-plugin-system.md +415 -0
- package/docs/v0.10-prompt-optimizer.md +234 -0
- package/docs/v0.3-spec.md +244 -0
- package/docs/v0.4-spec.md +140 -0
- package/docs/v0.5-spec.md +237 -0
- package/docs/v0.6-spec.md +371 -0
- package/docs/v0.7-spec.md +177 -0
- package/docs/v0.8-llm-routing.md +206 -0
- package/docs/v0.8-structured-logging.md +132 -0
- package/docs/v0.9.3-prompt-audit.md +112 -0
- package/examples/plugins/console-reporter/index.test.ts +207 -0
- package/examples/plugins/console-reporter/index.ts +110 -0
- package/nax/config.json +147 -0
- package/nax/features/bugfix-v0171/prd.json +52 -0
- package/nax/features/config-management/prd.json +108 -0
- package/nax/features/config-management/progress.txt +5 -0
- package/nax/features/diagnose/acceptance.test.ts +412 -0
- package/nax/features/diagnose/prd.json +41 -0
- package/nax/features/orchestration-fixes/prd.json +89 -0
- package/nax/features/orchestration-fixes/progress.txt +1 -0
- package/nax/features/plugin-integration/US-007-VERIFICATION.md +259 -0
- package/nax/features/plugin-integration/prd.json +208 -0
- package/nax/features/plugin-integration/progress.txt +5 -0
- package/nax/features/precheck/prd.json +205 -0
- package/nax/features/precheck/progress.txt +15 -0
- package/nax/features/structured-logging/prd.json +199 -0
- package/nax/features/unlock/prd.json +36 -0
- package/package.json +47 -0
- package/src/acceptance/fix-generator.ts +348 -0
- package/src/acceptance/generator.ts +282 -0
- package/src/acceptance/index.ts +30 -0
- package/src/acceptance/types.ts +79 -0
- package/src/agents/claude-decompose.ts +169 -0
- package/src/agents/claude-plan.ts +139 -0
- package/src/agents/claude.ts +324 -0
- package/src/agents/cost.ts +268 -0
- package/src/agents/index.ts +13 -0
- package/src/agents/registry.ts +48 -0
- package/src/agents/types-extended.ts +133 -0
- package/src/agents/types.ts +113 -0
- package/src/agents/validation.ts +69 -0
- package/src/analyze/classifier.ts +305 -0
- package/src/analyze/index.ts +16 -0
- package/src/analyze/scanner.ts +175 -0
- package/src/analyze/types.ts +51 -0
- package/src/cli/accept.ts +108 -0
- package/src/cli/analyze-parser.ts +284 -0
- package/src/cli/analyze.ts +207 -0
- package/src/cli/config.ts +561 -0
- package/src/cli/constitution.ts +109 -0
- package/src/cli/diagnose-analysis.ts +159 -0
- package/src/cli/diagnose-formatter.ts +87 -0
- package/src/cli/diagnose.ts +203 -0
- package/src/cli/generate.ts +127 -0
- package/src/cli/index.ts +37 -0
- package/src/cli/init.ts +188 -0
- package/src/cli/interact.ts +295 -0
- package/src/cli/plan.ts +198 -0
- package/src/cli/plugins.ts +111 -0
- package/src/cli/prompts.ts +295 -0
- package/src/cli/runs.ts +174 -0
- package/src/cli/status-cost.ts +151 -0
- package/src/cli/status-features.ts +338 -0
- package/src/cli/status.ts +13 -0
- package/src/commands/common.ts +171 -0
- package/src/commands/diagnose.ts +17 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/logs.ts +384 -0
- package/src/commands/precheck.ts +86 -0
- package/src/commands/unlock.ts +96 -0
- package/src/config/defaults.ts +160 -0
- package/src/config/index.ts +22 -0
- package/src/config/loader.ts +121 -0
- package/src/config/merger.ts +147 -0
- package/src/config/path-security.ts +121 -0
- package/src/config/paths.ts +27 -0
- package/src/config/schema.ts +56 -0
- package/src/config/schemas.ts +286 -0
- package/src/config/types.ts +423 -0
- package/src/config/validate.ts +103 -0
- package/src/constitution/generator.ts +191 -0
- package/src/constitution/generators/aider.ts +41 -0
- package/src/constitution/generators/claude.ts +35 -0
- package/src/constitution/generators/cursor.ts +36 -0
- package/src/constitution/generators/opencode.ts +38 -0
- package/src/constitution/generators/types.ts +33 -0
- package/src/constitution/generators/windsurf.ts +36 -0
- package/src/constitution/index.ts +10 -0
- package/src/constitution/loader.ts +133 -0
- package/src/constitution/types.ts +31 -0
- package/src/context/auto-detect.ts +227 -0
- package/src/context/builder.ts +246 -0
- package/src/context/elements.ts +83 -0
- package/src/context/formatter.ts +107 -0
- package/src/context/generator.ts +129 -0
- package/src/context/generators/aider.ts +34 -0
- package/src/context/generators/claude.ts +28 -0
- package/src/context/generators/cursor.ts +28 -0
- package/src/context/generators/opencode.ts +30 -0
- package/src/context/generators/windsurf.ts +28 -0
- package/src/context/greenfield.ts +114 -0
- package/src/context/index.ts +33 -0
- package/src/context/injector.ts +279 -0
- package/src/context/test-scanner.ts +370 -0
- package/src/context/types.ts +98 -0
- package/src/errors.ts +67 -0
- package/src/execution/batching.ts +157 -0
- package/src/execution/crash-recovery.ts +373 -0
- package/src/execution/escalation/escalation.ts +44 -0
- package/src/execution/escalation/index.ts +13 -0
- package/src/execution/escalation/tier-escalation.ts +295 -0
- package/src/execution/escalation/tier-outcome.ts +158 -0
- package/src/execution/helpers.ts +38 -0
- package/src/execution/index.ts +45 -0
- package/src/execution/lifecycle/acceptance-loop.ts +272 -0
- package/src/execution/lifecycle/headless-formatter.ts +85 -0
- package/src/execution/lifecycle/index.ts +12 -0
- package/src/execution/lifecycle/parallel-lifecycle.ts +101 -0
- package/src/execution/lifecycle/precheck-runner.ts +140 -0
- package/src/execution/lifecycle/run-cleanup.ts +81 -0
- package/src/execution/lifecycle/run-completion.ts +129 -0
- package/src/execution/lifecycle/run-initialization.ts +141 -0
- package/src/execution/lifecycle/run-lifecycle.ts +312 -0
- package/src/execution/lifecycle/run-setup.ts +204 -0
- package/src/execution/lifecycle/story-hooks.ts +38 -0
- package/src/execution/lifecycle/story-size-prompts.ts +123 -0
- package/src/execution/lock.ts +115 -0
- package/src/execution/parallel-executor.ts +216 -0
- package/src/execution/parallel.ts +400 -0
- package/src/execution/pid-registry.ts +280 -0
- package/src/execution/pipeline-result-handler.ts +388 -0
- package/src/execution/post-verify-rectification.ts +188 -0
- package/src/execution/post-verify.ts +274 -0
- package/src/execution/progress.ts +25 -0
- package/src/execution/prompts.ts +127 -0
- package/src/execution/queue-handler.ts +109 -0
- package/src/execution/rectification.ts +13 -0
- package/src/execution/runner.ts +377 -0
- package/src/execution/sequential-executor.ts +388 -0
- package/src/execution/status-file.ts +264 -0
- package/src/execution/status-writer.ts +139 -0
- package/src/execution/story-context.ts +229 -0
- package/src/execution/test-output-parser.ts +14 -0
- package/src/execution/verification.ts +72 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/runner.ts +286 -0
- package/src/hooks/types.ts +67 -0
- package/src/interaction/chain.ts +154 -0
- package/src/interaction/index.ts +60 -0
- package/src/interaction/init.ts +83 -0
- package/src/interaction/plugins/auto.ts +217 -0
- package/src/interaction/plugins/cli.ts +300 -0
- package/src/interaction/plugins/telegram.ts +384 -0
- package/src/interaction/plugins/webhook.ts +258 -0
- package/src/interaction/state.ts +171 -0
- package/src/interaction/triggers.ts +229 -0
- package/src/interaction/types.ts +163 -0
- package/src/logger/formatters.ts +84 -0
- package/src/logger/index.ts +16 -0
- package/src/logger/logger.ts +298 -0
- package/src/logger/types.ts +48 -0
- package/src/logging/formatter.ts +355 -0
- package/src/logging/index.ts +22 -0
- package/src/logging/types.ts +93 -0
- package/src/metrics/aggregator.ts +190 -0
- package/src/metrics/index.ts +14 -0
- package/src/metrics/tracker.ts +200 -0
- package/src/metrics/types.ts +109 -0
- package/src/optimizer/index.ts +62 -0
- package/src/optimizer/noop.optimizer.ts +24 -0
- package/src/optimizer/rule-based.optimizer.ts +248 -0
- package/src/optimizer/types.ts +53 -0
- package/src/pipeline/events.ts +130 -0
- package/src/pipeline/index.ts +19 -0
- package/src/pipeline/runner.ts +161 -0
- package/src/pipeline/stages/acceptance.ts +197 -0
- package/src/pipeline/stages/completion.ts +99 -0
- package/src/pipeline/stages/constitution.ts +63 -0
- package/src/pipeline/stages/context.ts +117 -0
- package/src/pipeline/stages/execution.ts +194 -0
- package/src/pipeline/stages/index.ts +62 -0
- package/src/pipeline/stages/optimizer.ts +74 -0
- package/src/pipeline/stages/prompt.ts +57 -0
- package/src/pipeline/stages/queue-check.ts +103 -0
- package/src/pipeline/stages/review.ts +181 -0
- package/src/pipeline/stages/routing.ts +81 -0
- package/src/pipeline/stages/verify.ts +100 -0
- package/src/pipeline/types.ts +167 -0
- package/src/plugins/index.ts +31 -0
- package/src/plugins/loader.ts +287 -0
- package/src/plugins/registry.ts +168 -0
- package/src/plugins/types.ts +327 -0
- package/src/plugins/validator.ts +352 -0
- package/src/prd/index.ts +172 -0
- package/src/prd/types.ts +202 -0
- package/src/precheck/checks-blockers.ts +391 -0
- package/src/precheck/checks-warnings.ts +142 -0
- package/src/precheck/checks.ts +30 -0
- package/src/precheck/index.ts +247 -0
- package/src/precheck/story-size-gate.ts +144 -0
- package/src/precheck/types.ts +31 -0
- package/src/queue/index.ts +2 -0
- package/src/queue/manager.ts +254 -0
- package/src/queue/types.ts +54 -0
- package/src/review/index.ts +8 -0
- package/src/review/runner.ts +172 -0
- package/src/review/types.ts +66 -0
- package/src/routing/builder.ts +81 -0
- package/src/routing/chain.ts +74 -0
- package/src/routing/index.ts +16 -0
- package/src/routing/loader.ts +58 -0
- package/src/routing/router.ts +303 -0
- package/src/routing/strategies/adaptive.ts +215 -0
- package/src/routing/strategies/index.ts +8 -0
- package/src/routing/strategies/keyword.ts +163 -0
- package/src/routing/strategies/llm-prompts.ts +209 -0
- package/src/routing/strategies/llm.ts +235 -0
- package/src/routing/strategies/manual.ts +50 -0
- package/src/routing/strategy.ts +99 -0
- package/src/tdd/cleanup.ts +111 -0
- package/src/tdd/index.ts +23 -0
- package/src/tdd/isolation.ts +123 -0
- package/src/tdd/orchestrator.ts +383 -0
- package/src/tdd/prompts.ts +270 -0
- package/src/tdd/rectification-gate.ts +183 -0
- package/src/tdd/session-runner.ts +179 -0
- package/src/tdd/types.ts +81 -0
- package/src/tdd/verdict.ts +271 -0
- package/src/tui/App.tsx +265 -0
- package/src/tui/components/AgentPanel.tsx +75 -0
- package/src/tui/components/CostOverlay.tsx +118 -0
- package/src/tui/components/HelpOverlay.tsx +107 -0
- package/src/tui/components/StatusBar.tsx +63 -0
- package/src/tui/components/StoriesPanel.tsx +177 -0
- package/src/tui/hooks/useKeyboard.ts +142 -0
- package/src/tui/hooks/useLayout.ts +137 -0
- package/src/tui/hooks/usePipelineEvents.ts +183 -0
- package/src/tui/hooks/usePty.ts +194 -0
- package/src/tui/index.tsx +38 -0
- package/src/tui/types.ts +76 -0
- package/src/utils/git.ts +83 -0
- package/src/utils/queue-writer.ts +54 -0
- package/src/verification/executor.ts +235 -0
- package/src/verification/gate.ts +207 -0
- package/src/verification/index.ts +12 -0
- package/src/verification/parser.ts +230 -0
- package/src/verification/rectification.ts +108 -0
- package/src/verification/types.ts +113 -0
- package/src/worktree/dispatcher.ts +65 -0
- package/src/worktree/index.ts +2 -0
- package/src/worktree/manager.ts +187 -0
- package/src/worktree/merge.ts +301 -0
- package/src/worktree/types.ts +4 -0
- package/test/TEST_COVERAGE_US001.md +217 -0
- package/test/TEST_COVERAGE_US003.md +84 -0
- package/test/TEST_COVERAGE_US005.md +86 -0
- package/test/US-002-orchestrator.test.ts +246 -0
- package/test/acceptance/cm-003-default-view.test.ts +194 -0
- package/test/execution/pid-registry.test.ts +240 -0
- package/test/execution/post-verify.test.ts +224 -0
- package/test/helpers/timeout.ts +42 -0
- package/test/integration/US-002-TEST-SUMMARY.md +107 -0
- package/test/integration/US-003-TEST-SUMMARY.md +149 -0
- package/test/integration/US-004-TEST-SUMMARY.md +106 -0
- package/test/integration/US-005-TEST-SUMMARY.md +138 -0
- package/test/integration/US-007-TEST-SUMMARY.md +100 -0
- package/test/integration/agent-validation.test.ts +439 -0
- package/test/integration/analyze-integration.test.ts +261 -0
- package/test/integration/analyze-scanner.test.ts +131 -0
- package/test/integration/cli-config-default-edge-cases.test.ts +222 -0
- package/test/integration/cli-config-default-view.test.ts +229 -0
- package/test/integration/cli-config-diff.test.ts +460 -0
- package/test/integration/cli-config.test.ts +736 -0
- package/test/integration/cli-diagnose.test.ts +592 -0
- package/test/integration/cli-logs.test.ts +314 -0
- package/test/integration/cli-plugins.test.ts +678 -0
- package/test/integration/cli-precheck.test.ts +371 -0
- package/test/integration/cli-run-headless.test.ts +173 -0
- package/test/integration/cli.test.ts +75 -0
- package/test/integration/config/merger.test.ts +465 -0
- package/test/integration/config/paths.test.ts +51 -0
- package/test/integration/config-loader.test.ts +265 -0
- package/test/integration/config.test.ts +444 -0
- package/test/integration/context-integration.test.ts +702 -0
- package/test/integration/context-provider-injection.test.ts +506 -0
- package/test/integration/context-verification-integration.test.ts +295 -0
- package/test/integration/e2e.test.ts +896 -0
- package/test/integration/execution.test.ts +625 -0
- package/test/integration/helpers.test.ts +295 -0
- package/test/integration/hooks.test.ts +361 -0
- package/test/integration/interaction-chain-pipeline.test.ts +464 -0
- package/test/integration/isolation.test.ts +143 -0
- package/test/integration/logger.test.ts +461 -0
- package/test/integration/parallel.test.ts +250 -0
- package/test/integration/path-security.test.ts +173 -0
- package/test/integration/pipeline-acceptance.test.ts +302 -0
- package/test/integration/pipeline-events.test.ts +475 -0
- package/test/integration/pipeline.test.ts +658 -0
- package/test/integration/plan.test.ts +157 -0
- package/test/integration/plugin-routing.test.ts +921 -0
- package/test/integration/plugins/config-integration.test.ts +172 -0
- package/test/integration/plugins/config-resolution.test.ts +522 -0
- package/test/integration/plugins/loader.test.ts +641 -0
- package/test/integration/plugins/registry.test.ts +746 -0
- package/test/integration/plugins/validator.test.ts +563 -0
- package/test/integration/prd-pause.test.ts +205 -0
- package/test/integration/prd-resolvers.test.ts +185 -0
- package/test/integration/precheck-integration.test.ts +468 -0
- package/test/integration/precheck.test.ts +805 -0
- package/test/integration/progress.test.ts +34 -0
- package/test/integration/rectification-flow.test.ts +512 -0
- package/test/integration/reporter-lifecycle.test.ts +860 -0
- package/test/integration/review-config-commands.test.ts +319 -0
- package/test/integration/review-config-schema.test.ts +116 -0
- package/test/integration/review-plugin-integration.test.ts +722 -0
- package/test/integration/review.test.ts +149 -0
- package/test/integration/routing-stage-bug-021.test.ts +274 -0
- package/test/integration/routing-stage-greenfield.test.ts +286 -0
- package/test/integration/runner-config-plugins.test.ts +461 -0
- package/test/integration/runner-fixes.test.ts +399 -0
- package/test/integration/runner-plugin-integration.test.ts +543 -0
- package/test/integration/runner.test.ts +1679 -0
- package/test/integration/s5-greenfield-fallback.test.ts +297 -0
- package/test/integration/status-file-integration.test.ts +325 -0
- package/test/integration/status-file.test.ts +379 -0
- package/test/integration/status-writer.test.ts +345 -0
- package/test/integration/story-id-in-events.test.ts +273 -0
- package/test/integration/tdd-cleanup.test.ts +246 -0
- package/test/integration/tdd-orchestrator.test.ts +1762 -0
- package/test/integration/test-scanner.test.ts +403 -0
- package/test/integration/verification-asset-check.test.ts +142 -0
- package/test/integration/verify-stage.test.ts +275 -0
- package/test/integration/worktree/manager.test.ts +218 -0
- package/test/integration/worktree/merge.test.ts +341 -0
- package/test/manual/logging-formatter-demo.ts +158 -0
- package/test/ui/tui-agent-panel.test.tsx +99 -0
- package/test/ui/tui-controls.test.ts +334 -0
- package/test/ui/tui-cost-and-pty.test.ts +189 -0
- package/test/ui/tui-layout.test.ts +378 -0
- package/test/ui/tui-pty-integration.test.tsx +159 -0
- package/test/ui/tui-stories.test.ts +332 -0
- package/test/unit/acceptance.test.ts +186 -0
- package/test/unit/agent-stderr-capture.test.ts +146 -0
- package/test/unit/analyze-classifier.test.ts +215 -0
- package/test/unit/analyze.test.ts +224 -0
- package/test/unit/auto-detect.test.ts +249 -0
- package/test/unit/cli-status.test.ts +417 -0
- package/test/unit/commands/common.test.ts +320 -0
- package/test/unit/commands/logs.test.ts +416 -0
- package/test/unit/commands/unlock.test.ts +319 -0
- package/test/unit/constitution-generators.test.ts +160 -0
- package/test/unit/constitution.test.ts +209 -0
- package/test/unit/context.test.ts +1722 -0
- package/test/unit/cost.test.ts +231 -0
- package/test/unit/crash-recovery.test.ts +308 -0
- package/test/unit/escalation.test.ts +126 -0
- package/test/unit/execution-logging-stderr.test.ts +156 -0
- package/test/unit/execution-stage.test.ts +122 -0
- package/test/unit/fix-generator.test.ts +275 -0
- package/test/unit/formatters.test.ts +469 -0
- package/test/unit/greenfield.test.ts +179 -0
- package/test/unit/helpers.test.ts +317 -0
- package/test/unit/interaction/human-review-trigger.test.ts +164 -0
- package/test/unit/interaction-network-failures.test.ts +389 -0
- package/test/unit/interaction-plugins.test.ts +164 -0
- package/test/unit/isolation.test.ts +134 -0
- package/test/unit/logging/formatter.test.ts +455 -0
- package/test/unit/merge.test.ts +268 -0
- package/test/unit/metrics.test.ts +276 -0
- package/test/unit/optimizer/noop.optimizer.test.ts +125 -0
- package/test/unit/optimizer/rule-based.optimizer.test.ts +358 -0
- package/test/unit/prd-auto-default.test.ts +290 -0
- package/test/unit/prd-failure-category.test.ts +176 -0
- package/test/unit/prd-get-next-story.test.ts +186 -0
- package/test/unit/precheck-checks.test.ts +840 -0
- package/test/unit/precheck-story-size-gate.test.ts +287 -0
- package/test/unit/precheck-types.test.ts +142 -0
- package/test/unit/prompts.test.ts +475 -0
- package/test/unit/queue.test.ts +237 -0
- package/test/unit/rectification.test.ts +284 -0
- package/test/unit/registry.test.ts +287 -0
- package/test/unit/routing.test.ts +937 -0
- package/test/unit/run-lifecycle.test.ts +140 -0
- package/test/unit/storyid-events.test.ts +224 -0
- package/test/unit/tdd-verdict.test.ts +492 -0
- package/test/unit/test-output-parser.test.ts +377 -0
- package/test/unit/verdict.test.ts +324 -0
- package/test/unit/worktree-manager.test.ts +158 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for BUG-2 (queue race condition) and PERF-1 (batching optimization)
|
|
3
|
+
* Tests for PERF-2 (PRD dirty-flag reload optimization) and MEM-1 (file size limit)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { groupStoriesIntoBatches } from "../../src/execution/runner";
|
|
9
|
+
import type { PRD, UserStory } from "../../src/prd";
|
|
10
|
+
import { PRD_MAX_FILE_SIZE, loadPRD } from "../../src/prd";
|
|
11
|
+
|
|
12
|
+
// Helper to create test stories
|
|
13
|
+
function createStory(
|
|
14
|
+
id: string,
|
|
15
|
+
complexity: "simple" | "medium" | "complex" | "expert" = "simple",
|
|
16
|
+
testStrategy: "test-after" | "three-session-tdd" = "test-after",
|
|
17
|
+
): UserStory {
|
|
18
|
+
return {
|
|
19
|
+
id,
|
|
20
|
+
title: `Story ${id}`,
|
|
21
|
+
description: "Test story",
|
|
22
|
+
acceptanceCriteria: ["AC1"],
|
|
23
|
+
dependencies: [],
|
|
24
|
+
tags: [],
|
|
25
|
+
status: "pending",
|
|
26
|
+
passes: false,
|
|
27
|
+
escalations: [],
|
|
28
|
+
attempts: 0,
|
|
29
|
+
routing: {
|
|
30
|
+
complexity,
|
|
31
|
+
modelTier: "fast",
|
|
32
|
+
testStrategy,
|
|
33
|
+
reasoning: "Test routing",
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("BUG-2: Queue race condition", () => {
|
|
39
|
+
let tmpDir: string;
|
|
40
|
+
|
|
41
|
+
beforeEach(async () => {
|
|
42
|
+
tmpDir = `/tmp/nax-race-test-${Date.now()}`;
|
|
43
|
+
await Bun.spawn(["mkdir", "-p", tmpDir], { stdout: "pipe" }).exited;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("atomic read-and-rename prevents race condition", async () => {
|
|
47
|
+
const queuePath = path.join(tmpDir, ".queue.txt");
|
|
48
|
+
const processingPath = path.join(tmpDir, ".queue.txt.processing");
|
|
49
|
+
|
|
50
|
+
// Write initial commands
|
|
51
|
+
await Bun.write(queuePath, "PAUSE\n");
|
|
52
|
+
|
|
53
|
+
// Simulate reader starting (rename to processing)
|
|
54
|
+
await Bun.spawn(["mv", queuePath, processingPath], { stdout: "pipe" }).exited;
|
|
55
|
+
|
|
56
|
+
// Simulate concurrent writer adding commands (should create new .queue.txt)
|
|
57
|
+
await Bun.write(queuePath, "SKIP US-001\n");
|
|
58
|
+
|
|
59
|
+
// Verify processing file exists
|
|
60
|
+
const processingFile = Bun.file(processingPath);
|
|
61
|
+
expect(await processingFile.exists()).toBe(true);
|
|
62
|
+
const processingContent = await processingFile.text();
|
|
63
|
+
expect(processingContent).toBe("PAUSE\n");
|
|
64
|
+
|
|
65
|
+
// Verify new queue file exists with new content
|
|
66
|
+
const newQueueFile = Bun.file(queuePath);
|
|
67
|
+
expect(await newQueueFile.exists()).toBe(true);
|
|
68
|
+
const newContent = await newQueueFile.text();
|
|
69
|
+
expect(newContent).toBe("SKIP US-001\n");
|
|
70
|
+
|
|
71
|
+
// Cleanup
|
|
72
|
+
await Bun.spawn(["rm", "-rf", tmpDir], { stdout: "pipe" }).exited;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("processing file is deleted after reading", async () => {
|
|
76
|
+
const queuePath = path.join(tmpDir, ".queue.txt");
|
|
77
|
+
const processingPath = path.join(tmpDir, ".queue.txt.processing");
|
|
78
|
+
|
|
79
|
+
// Write commands
|
|
80
|
+
await Bun.write(queuePath, "PAUSE\n");
|
|
81
|
+
|
|
82
|
+
// Rename to processing
|
|
83
|
+
await Bun.spawn(["mv", queuePath, processingPath], { stdout: "pipe" }).exited;
|
|
84
|
+
|
|
85
|
+
// Verify processing file exists
|
|
86
|
+
expect(await Bun.file(processingPath).exists()).toBe(true);
|
|
87
|
+
|
|
88
|
+
// Simulate cleanup (delete processing file)
|
|
89
|
+
await Bun.spawn(["rm", processingPath], { stdout: "pipe" }).exited;
|
|
90
|
+
|
|
91
|
+
// Verify processing file is deleted
|
|
92
|
+
expect(await Bun.file(processingPath).exists()).toBe(false);
|
|
93
|
+
|
|
94
|
+
// Cleanup
|
|
95
|
+
await Bun.spawn(["rm", "-rf", tmpDir], { stdout: "pipe" }).exited;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("concurrent writes during processing don't lose commands", async () => {
|
|
99
|
+
const queuePath = path.join(tmpDir, ".queue.txt");
|
|
100
|
+
const processingPath = path.join(tmpDir, ".queue.txt.processing");
|
|
101
|
+
|
|
102
|
+
// Write initial batch
|
|
103
|
+
await Bun.write(queuePath, "PAUSE\nSKIP US-001\n");
|
|
104
|
+
|
|
105
|
+
// Reader: rename to processing
|
|
106
|
+
await Bun.spawn(["mv", queuePath, processingPath], { stdout: "pipe" }).exited;
|
|
107
|
+
|
|
108
|
+
// Writer: add new commands (creates new .queue.txt)
|
|
109
|
+
await Bun.write(queuePath, "SKIP US-002\nSKIP US-003\n");
|
|
110
|
+
|
|
111
|
+
// Reader: read processing file
|
|
112
|
+
const processingContent = await Bun.file(processingPath).text();
|
|
113
|
+
const processedLines = processingContent.trim().split("\n");
|
|
114
|
+
|
|
115
|
+
// Reader: delete processing file
|
|
116
|
+
await Bun.spawn(["rm", processingPath], { stdout: "pipe" }).exited;
|
|
117
|
+
|
|
118
|
+
// Verify original commands were processed
|
|
119
|
+
expect(processedLines).toEqual(["PAUSE", "SKIP US-001"]);
|
|
120
|
+
|
|
121
|
+
// Verify new commands are in queue
|
|
122
|
+
const newContent = await Bun.file(queuePath).text();
|
|
123
|
+
const newLines = newContent.trim().split("\n");
|
|
124
|
+
expect(newLines).toEqual(["SKIP US-002", "SKIP US-003"]);
|
|
125
|
+
|
|
126
|
+
// Cleanup
|
|
127
|
+
await Bun.spawn(["rm", "-rf", tmpDir], { stdout: "pipe" }).exited;
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe("BUG-2: File locking", () => {
|
|
132
|
+
let tmpDir: string;
|
|
133
|
+
|
|
134
|
+
beforeEach(async () => {
|
|
135
|
+
tmpDir = `/tmp/nax-lock-test-${Date.now()}`;
|
|
136
|
+
await Bun.spawn(["mkdir", "-p", tmpDir], { stdout: "pipe" }).exited;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("lock file prevents concurrent execution", async () => {
|
|
140
|
+
const lockPath = path.join(tmpDir, "nax.lock");
|
|
141
|
+
|
|
142
|
+
// Create lock
|
|
143
|
+
const lockData = {
|
|
144
|
+
pid: process.pid,
|
|
145
|
+
timestamp: Date.now(),
|
|
146
|
+
};
|
|
147
|
+
await Bun.write(lockPath, JSON.stringify(lockData));
|
|
148
|
+
|
|
149
|
+
// Verify lock exists
|
|
150
|
+
const lockFile = Bun.file(lockPath);
|
|
151
|
+
expect(await lockFile.exists()).toBe(true);
|
|
152
|
+
|
|
153
|
+
// Verify lock content
|
|
154
|
+
const content = await lockFile.text();
|
|
155
|
+
const parsed = JSON.parse(content);
|
|
156
|
+
expect(parsed.pid).toBe(process.pid);
|
|
157
|
+
expect(parsed.timestamp).toBeGreaterThan(0);
|
|
158
|
+
|
|
159
|
+
// Cleanup
|
|
160
|
+
await Bun.spawn(["rm", "-rf", tmpDir], { stdout: "pipe" }).exited;
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("stale lock is removed after 1 hour", async () => {
|
|
164
|
+
const lockPath = path.join(tmpDir, "nax.lock");
|
|
165
|
+
|
|
166
|
+
// Create stale lock (2 hours old)
|
|
167
|
+
const staleLockData = {
|
|
168
|
+
pid: 99999,
|
|
169
|
+
timestamp: Date.now() - 2 * 60 * 60 * 1000, // 2 hours ago
|
|
170
|
+
};
|
|
171
|
+
await Bun.write(lockPath, JSON.stringify(staleLockData));
|
|
172
|
+
|
|
173
|
+
// Verify lock is stale
|
|
174
|
+
const lockContent = await Bun.file(lockPath).text();
|
|
175
|
+
const lockData = JSON.parse(lockContent);
|
|
176
|
+
const lockAge = Date.now() - lockData.timestamp;
|
|
177
|
+
const ONE_HOUR = 60 * 60 * 1000;
|
|
178
|
+
expect(lockAge).toBeGreaterThan(ONE_HOUR);
|
|
179
|
+
|
|
180
|
+
// Cleanup
|
|
181
|
+
await Bun.spawn(["rm", "-rf", tmpDir], { stdout: "pipe" }).exited;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("lock is released after execution", async () => {
|
|
185
|
+
const lockPath = path.join(tmpDir, "nax.lock");
|
|
186
|
+
|
|
187
|
+
// Create lock
|
|
188
|
+
const lockData = {
|
|
189
|
+
pid: process.pid,
|
|
190
|
+
timestamp: Date.now(),
|
|
191
|
+
};
|
|
192
|
+
await Bun.write(lockPath, JSON.stringify(lockData));
|
|
193
|
+
|
|
194
|
+
// Verify lock exists
|
|
195
|
+
expect(await Bun.file(lockPath).exists()).toBe(true);
|
|
196
|
+
|
|
197
|
+
// Simulate release (delete lock)
|
|
198
|
+
await Bun.spawn(["rm", lockPath], { stdout: "pipe" }).exited;
|
|
199
|
+
|
|
200
|
+
// Verify lock is removed
|
|
201
|
+
expect(await Bun.file(lockPath).exists()).toBe(false);
|
|
202
|
+
|
|
203
|
+
// Cleanup
|
|
204
|
+
await Bun.spawn(["rm", "-rf", tmpDir], { stdout: "pipe" }).exited;
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe("PERF-1: Batch optimization", () => {
|
|
209
|
+
test("groups consecutive simple stories correctly", () => {
|
|
210
|
+
const stories: UserStory[] = [
|
|
211
|
+
createStory("US-001", "simple", "test-after"),
|
|
212
|
+
createStory("US-002", "simple", "test-after"),
|
|
213
|
+
createStory("US-003", "simple", "test-after"),
|
|
214
|
+
createStory("US-004", "medium", "test-after"),
|
|
215
|
+
createStory("US-005", "simple", "test-after"),
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
const batches = groupStoriesIntoBatches(stories, 4);
|
|
219
|
+
|
|
220
|
+
expect(batches).toHaveLength(3);
|
|
221
|
+
expect(batches[0].stories).toHaveLength(3); // US-001, US-002, US-003
|
|
222
|
+
expect(batches[0].isBatch).toBe(true);
|
|
223
|
+
expect(batches[1].stories).toHaveLength(1); // US-004 (medium)
|
|
224
|
+
expect(batches[1].isBatch).toBe(false);
|
|
225
|
+
expect(batches[2].stories).toHaveLength(1); // US-005 (simple, but alone)
|
|
226
|
+
expect(batches[2].isBatch).toBe(false);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("respects max batch size", () => {
|
|
230
|
+
const stories: UserStory[] = [
|
|
231
|
+
createStory("US-001", "simple", "test-after"),
|
|
232
|
+
createStory("US-002", "simple", "test-after"),
|
|
233
|
+
createStory("US-003", "simple", "test-after"),
|
|
234
|
+
createStory("US-004", "simple", "test-after"),
|
|
235
|
+
createStory("US-005", "simple", "test-after"),
|
|
236
|
+
createStory("US-006", "simple", "test-after"),
|
|
237
|
+
];
|
|
238
|
+
|
|
239
|
+
const batches = groupStoriesIntoBatches(stories, 4);
|
|
240
|
+
|
|
241
|
+
expect(batches).toHaveLength(2);
|
|
242
|
+
expect(batches[0].stories).toHaveLength(4); // US-001 to US-004
|
|
243
|
+
expect(batches[0].isBatch).toBe(true);
|
|
244
|
+
expect(batches[1].stories).toHaveLength(2); // US-005, US-006
|
|
245
|
+
expect(batches[1].isBatch).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("handles mixed complexity correctly", () => {
|
|
249
|
+
const stories: UserStory[] = [
|
|
250
|
+
createStory("US-001", "simple", "test-after"),
|
|
251
|
+
createStory("US-002", "medium", "test-after"),
|
|
252
|
+
createStory("US-003", "simple", "test-after"),
|
|
253
|
+
createStory("US-004", "complex", "three-session-tdd"),
|
|
254
|
+
createStory("US-005", "simple", "test-after"),
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
const batches = groupStoriesIntoBatches(stories, 4);
|
|
258
|
+
|
|
259
|
+
expect(batches).toHaveLength(5);
|
|
260
|
+
// Each non-simple story creates its own batch
|
|
261
|
+
expect(batches[0].stories).toHaveLength(1); // US-001
|
|
262
|
+
expect(batches[1].stories).toHaveLength(1); // US-002
|
|
263
|
+
expect(batches[2].stories).toHaveLength(1); // US-003
|
|
264
|
+
expect(batches[3].stories).toHaveLength(1); // US-004
|
|
265
|
+
expect(batches[4].stories).toHaveLength(1); // US-005
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("large story list performance", () => {
|
|
269
|
+
// Create 1000 stories
|
|
270
|
+
const stories: UserStory[] = [];
|
|
271
|
+
for (let i = 0; i < 1000; i++) {
|
|
272
|
+
const complexity = i % 3 === 0 ? "simple" : "medium";
|
|
273
|
+
stories.push(createStory(`US-${String(i + 1).padStart(4, "0")}`, complexity, "test-after"));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const startTime = Date.now();
|
|
277
|
+
const batches = groupStoriesIntoBatches(stories, 4);
|
|
278
|
+
const duration = Date.now() - startTime;
|
|
279
|
+
|
|
280
|
+
// Should complete in under 100ms for 1000 stories
|
|
281
|
+
expect(duration).toBeLessThan(100);
|
|
282
|
+
|
|
283
|
+
// Verify batches were created
|
|
284
|
+
expect(batches.length).toBeGreaterThan(0);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("uses pre-computed routing", () => {
|
|
288
|
+
// Stories with routing already set (from analyze phase)
|
|
289
|
+
const stories: UserStory[] = [
|
|
290
|
+
createStory("US-001", "simple", "test-after"),
|
|
291
|
+
createStory("US-002", "simple", "test-after"),
|
|
292
|
+
];
|
|
293
|
+
|
|
294
|
+
const batches = groupStoriesIntoBatches(stories, 4);
|
|
295
|
+
|
|
296
|
+
// Should use routing from story.routing, not re-compute
|
|
297
|
+
expect(batches).toHaveLength(1);
|
|
298
|
+
expect(batches[0].stories).toHaveLength(2);
|
|
299
|
+
expect(batches[0].isBatch).toBe(true);
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
describe("PERF-2 & MEM-1: PRD file size limit and dirty-flag optimization", () => {
|
|
304
|
+
let tmpDir: string;
|
|
305
|
+
|
|
306
|
+
beforeEach(async () => {
|
|
307
|
+
tmpDir = `/tmp/nax-prd-test-${Date.now()}`;
|
|
308
|
+
await Bun.spawn(["mkdir", "-p", tmpDir], { stdout: "pipe" }).exited;
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test("rejects PRD files exceeding size limit", async () => {
|
|
312
|
+
const prdPath = path.join(tmpDir, "prd.json");
|
|
313
|
+
|
|
314
|
+
// Create a minimal PRD structure
|
|
315
|
+
const largePRD: PRD = {
|
|
316
|
+
project: "test-project",
|
|
317
|
+
feature: "test-feature",
|
|
318
|
+
branchName: "test-branch",
|
|
319
|
+
createdAt: new Date().toISOString(),
|
|
320
|
+
updatedAt: new Date().toISOString(),
|
|
321
|
+
userStories: [],
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// Add enough stories to exceed 5MB limit
|
|
325
|
+
// Each story is roughly 500 bytes, so we need ~10,000 stories
|
|
326
|
+
const storyTemplate = {
|
|
327
|
+
title: "Test Story with long description and multiple acceptance criteria",
|
|
328
|
+
description: "A".repeat(200), // 200 character description
|
|
329
|
+
acceptanceCriteria: Array.from({ length: 5 }, (_, i) => `Acceptance criterion ${i}: ${"x".repeat(50)}`),
|
|
330
|
+
dependencies: [],
|
|
331
|
+
tags: ["test", "performance", "large-prd"],
|
|
332
|
+
status: "pending" as const,
|
|
333
|
+
passes: false,
|
|
334
|
+
escalations: [],
|
|
335
|
+
attempts: 0,
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
for (let i = 0; i < 11000; i++) {
|
|
339
|
+
largePRD.userStories.push({
|
|
340
|
+
...storyTemplate,
|
|
341
|
+
id: `US-${String(i + 1).padStart(5, "0")}`,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Write large PRD
|
|
346
|
+
await Bun.write(prdPath, JSON.stringify(largePRD, null, 2));
|
|
347
|
+
|
|
348
|
+
// Verify file size exceeds limit
|
|
349
|
+
const stats = await Bun.file(prdPath).stat();
|
|
350
|
+
expect(stats.size).toBeGreaterThan(PRD_MAX_FILE_SIZE);
|
|
351
|
+
|
|
352
|
+
// Try to load — should throw error
|
|
353
|
+
try {
|
|
354
|
+
await loadPRD(prdPath);
|
|
355
|
+
expect(true).toBe(false); // Should not reach here
|
|
356
|
+
} catch (error) {
|
|
357
|
+
expect(error).toBeInstanceOf(Error);
|
|
358
|
+
expect((error as Error).message).toContain("too large");
|
|
359
|
+
expect((error as Error).message).toContain("exceeds");
|
|
360
|
+
expect((error as Error).message).toContain("MB");
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Cleanup
|
|
364
|
+
await Bun.spawn(["rm", "-rf", tmpDir], { stdout: "pipe" }).exited;
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test("accepts PRD files within size limit", async () => {
|
|
368
|
+
const prdPath = path.join(tmpDir, "prd.json");
|
|
369
|
+
|
|
370
|
+
// Create a normal-sized PRD
|
|
371
|
+
const prd: PRD = {
|
|
372
|
+
project: "test-project",
|
|
373
|
+
feature: "test-feature",
|
|
374
|
+
branchName: "test-branch",
|
|
375
|
+
createdAt: new Date().toISOString(),
|
|
376
|
+
updatedAt: new Date().toISOString(),
|
|
377
|
+
userStories: [createStory("US-001", "simple", "test-after"), createStory("US-002", "simple", "test-after")],
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
// Write PRD
|
|
381
|
+
await Bun.write(prdPath, JSON.stringify(prd, null, 2));
|
|
382
|
+
|
|
383
|
+
// Verify file size is within limit
|
|
384
|
+
const stats = await Bun.file(prdPath).stat();
|
|
385
|
+
expect(stats.size).toBeLessThan(PRD_MAX_FILE_SIZE);
|
|
386
|
+
|
|
387
|
+
// Should load successfully
|
|
388
|
+
const loaded = await loadPRD(prdPath);
|
|
389
|
+
expect(loaded.userStories).toHaveLength(2);
|
|
390
|
+
expect(loaded.project).toBe("test-project");
|
|
391
|
+
|
|
392
|
+
// Cleanup
|
|
393
|
+
await Bun.spawn(["rm", "-rf", tmpDir], { stdout: "pipe" }).exited;
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("PRD_MAX_FILE_SIZE constant is 5MB", () => {
|
|
397
|
+
expect(PRD_MAX_FILE_SIZE).toBe(5 * 1024 * 1024);
|
|
398
|
+
});
|
|
399
|
+
});
|