@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,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusWriter Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for src/execution/status-writer.ts:
|
|
5
|
+
* - Construction and defaults
|
|
6
|
+
* - setRunStatus / setPrd / setCurrentStory setters
|
|
7
|
+
* - getSnapshot() builds correct RunStateSnapshot
|
|
8
|
+
* - update() writes via writeStatusFile (no-op guard, success path, failure counter BUG-2)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
12
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
13
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import type { NaxConfig } from "../../src/config";
|
|
17
|
+
import type { NaxStatusFile } from "../../src/execution/status-file";
|
|
18
|
+
import { StatusWriter, type StatusWriterContext } from "../../src/execution/status-writer";
|
|
19
|
+
import type { PRD, UserStory } from "../../src/prd";
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Helpers
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
function makeStory(id: string, status: UserStory["status"] = "pending"): UserStory {
|
|
26
|
+
return {
|
|
27
|
+
id,
|
|
28
|
+
title: `Story ${id}`,
|
|
29
|
+
description: `Description for ${id}`,
|
|
30
|
+
acceptanceCriteria: ["AC-1"],
|
|
31
|
+
tags: [],
|
|
32
|
+
dependencies: [],
|
|
33
|
+
status,
|
|
34
|
+
passes: status === "passed",
|
|
35
|
+
escalations: [],
|
|
36
|
+
attempts: 0,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function makePrd(count = 1): PRD {
|
|
41
|
+
return {
|
|
42
|
+
project: "test-project",
|
|
43
|
+
feature: "test-feature",
|
|
44
|
+
branchName: "feat/test",
|
|
45
|
+
createdAt: "2026-02-25T10:00:00.000Z",
|
|
46
|
+
updatedAt: "2026-02-25T10:00:00.000Z",
|
|
47
|
+
userStories: Array.from({ length: count }, (_, i) => makeStory(`US-00${i + 1}`)),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function makeConfig(costLimit = 5.0): NaxConfig {
|
|
52
|
+
return {
|
|
53
|
+
execution: {
|
|
54
|
+
costLimit,
|
|
55
|
+
maxIterations: 10,
|
|
56
|
+
maxStoriesPerFeature: 50,
|
|
57
|
+
iterationDelayMs: 0,
|
|
58
|
+
},
|
|
59
|
+
} as unknown as NaxConfig;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function makeCtx(overrides: Partial<StatusWriterContext> = {}): StatusWriterContext {
|
|
63
|
+
return {
|
|
64
|
+
runId: "run-test-001",
|
|
65
|
+
feature: "auth-feature",
|
|
66
|
+
startedAt: "2026-02-25T10:00:00.000Z",
|
|
67
|
+
dryRun: false,
|
|
68
|
+
startTimeMs: Date.now() - 1000,
|
|
69
|
+
pid: process.pid,
|
|
70
|
+
...overrides,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// Construction
|
|
76
|
+
// ============================================================================
|
|
77
|
+
|
|
78
|
+
describe("StatusWriter construction", () => {
|
|
79
|
+
test("constructs without error when statusFile is defined", () => {
|
|
80
|
+
expect(() => new StatusWriter("/tmp/status.json", makeConfig(), makeCtx())).not.toThrow();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("constructs without error when statusFile is undefined (no-op mode)", () => {
|
|
84
|
+
expect(() => new StatusWriter(undefined, makeConfig(), makeCtx())).not.toThrow();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("costLimit Infinity → stored as null in snapshot", async () => {
|
|
88
|
+
const dir = await mkdtemp(join(tmpdir(), "sw-test-"));
|
|
89
|
+
const path = join(dir, "status.json");
|
|
90
|
+
const sw = new StatusWriter(path, makeConfig(Number.POSITIVE_INFINITY), makeCtx());
|
|
91
|
+
sw.setPrd(makePrd());
|
|
92
|
+
await sw.update(0, 0);
|
|
93
|
+
const content = JSON.parse(readFileSync(path, "utf8")) as NaxStatusFile;
|
|
94
|
+
expect(content.cost.limit).toBeNull();
|
|
95
|
+
await rm(dir, { recursive: true, force: true });
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// ============================================================================
|
|
100
|
+
// Setters
|
|
101
|
+
// ============================================================================
|
|
102
|
+
|
|
103
|
+
describe("StatusWriter setters", () => {
|
|
104
|
+
test("setRunStatus changes run status in snapshot", async () => {
|
|
105
|
+
const dir = await mkdtemp(join(tmpdir(), "sw-test-"));
|
|
106
|
+
const path = join(dir, "status.json");
|
|
107
|
+
const sw = new StatusWriter(path, makeConfig(), makeCtx());
|
|
108
|
+
sw.setPrd(makePrd());
|
|
109
|
+
sw.setRunStatus("completed");
|
|
110
|
+
await sw.update(0, 0);
|
|
111
|
+
const content = JSON.parse(readFileSync(path, "utf8")) as NaxStatusFile;
|
|
112
|
+
expect(content.run.status).toBe("completed");
|
|
113
|
+
await rm(dir, { recursive: true, force: true });
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("setPrd enables writes (no-op without prd)", async () => {
|
|
117
|
+
const dir = await mkdtemp(join(tmpdir(), "sw-test-"));
|
|
118
|
+
const path = join(dir, "status.json");
|
|
119
|
+
const sw = new StatusWriter(path, makeConfig(), makeCtx());
|
|
120
|
+
// Without setPrd, update should be a no-op
|
|
121
|
+
await sw.update(0, 0);
|
|
122
|
+
expect(existsSync(path)).toBe(false);
|
|
123
|
+
// After setPrd, write happens
|
|
124
|
+
sw.setPrd(makePrd());
|
|
125
|
+
await sw.update(0, 0);
|
|
126
|
+
expect(existsSync(path)).toBe(true);
|
|
127
|
+
await rm(dir, { recursive: true, force: true });
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("setCurrentStory sets active story in snapshot", async () => {
|
|
131
|
+
const dir = await mkdtemp(join(tmpdir(), "sw-test-"));
|
|
132
|
+
const path = join(dir, "status.json");
|
|
133
|
+
const sw = new StatusWriter(path, makeConfig(), makeCtx());
|
|
134
|
+
sw.setPrd(makePrd());
|
|
135
|
+
sw.setCurrentStory({
|
|
136
|
+
storyId: "US-001",
|
|
137
|
+
title: "Test story",
|
|
138
|
+
complexity: "simple",
|
|
139
|
+
tddStrategy: "test-after",
|
|
140
|
+
model: "balanced",
|
|
141
|
+
attempt: 1,
|
|
142
|
+
phase: "routing",
|
|
143
|
+
});
|
|
144
|
+
await sw.update(0, 0);
|
|
145
|
+
const content = JSON.parse(readFileSync(path, "utf8")) as NaxStatusFile;
|
|
146
|
+
expect(content.current?.storyId).toBe("US-001");
|
|
147
|
+
expect(content.current?.phase).toBe("routing");
|
|
148
|
+
await rm(dir, { recursive: true, force: true });
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("setCurrentStory(null) clears active story", async () => {
|
|
152
|
+
const dir = await mkdtemp(join(tmpdir(), "sw-test-"));
|
|
153
|
+
const path = join(dir, "status.json");
|
|
154
|
+
const sw = new StatusWriter(path, makeConfig(), makeCtx());
|
|
155
|
+
sw.setPrd(makePrd());
|
|
156
|
+
sw.setCurrentStory({
|
|
157
|
+
storyId: "US-001",
|
|
158
|
+
title: "T",
|
|
159
|
+
complexity: "simple",
|
|
160
|
+
tddStrategy: "test-after",
|
|
161
|
+
model: "balanced",
|
|
162
|
+
attempt: 1,
|
|
163
|
+
phase: "routing",
|
|
164
|
+
});
|
|
165
|
+
sw.setCurrentStory(null);
|
|
166
|
+
await sw.update(0, 0);
|
|
167
|
+
const content = JSON.parse(readFileSync(path, "utf8")) as NaxStatusFile;
|
|
168
|
+
expect(content.current).toBeNull();
|
|
169
|
+
await rm(dir, { recursive: true, force: true });
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// getSnapshot
|
|
175
|
+
// ============================================================================
|
|
176
|
+
|
|
177
|
+
describe("StatusWriter.getSnapshot", () => {
|
|
178
|
+
test("returns null when prd not set", () => {
|
|
179
|
+
const sw = new StatusWriter("/tmp/x.json", makeConfig(), makeCtx());
|
|
180
|
+
expect(sw.getSnapshot(0, 0)).toBeNull();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("includes totalCost and iterations from call args", () => {
|
|
184
|
+
const sw = new StatusWriter("/tmp/x.json", makeConfig(), makeCtx());
|
|
185
|
+
sw.setPrd(makePrd());
|
|
186
|
+
const snap = sw.getSnapshot(3.75, 7);
|
|
187
|
+
expect(snap?.totalCost).toBe(3.75);
|
|
188
|
+
expect(snap?.iterations).toBe(7);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("includes fixed context values from constructor", () => {
|
|
192
|
+
const ctx = makeCtx({ runId: "run-abc", feature: "my-feature", dryRun: true, pid: 12345 });
|
|
193
|
+
const sw = new StatusWriter("/tmp/x.json", makeConfig(), ctx);
|
|
194
|
+
sw.setPrd(makePrd());
|
|
195
|
+
const snap = sw.getSnapshot(0, 0);
|
|
196
|
+
expect(snap?.runId).toBe("run-abc");
|
|
197
|
+
expect(snap?.feature).toBe("my-feature");
|
|
198
|
+
expect(snap?.dryRun).toBe(true);
|
|
199
|
+
expect(snap?.pid).toBe(12345);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("includes PID for crash detection", () => {
|
|
203
|
+
const testPid = 99999;
|
|
204
|
+
const sw = new StatusWriter("/tmp/x.json", makeConfig(), makeCtx({ pid: testPid }));
|
|
205
|
+
sw.setPrd(makePrd());
|
|
206
|
+
const snap = sw.getSnapshot(0, 0);
|
|
207
|
+
expect(snap?.pid).toBe(testPid);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// ============================================================================
|
|
212
|
+
// update — no-op guards
|
|
213
|
+
// ============================================================================
|
|
214
|
+
|
|
215
|
+
describe("StatusWriter.update no-op guards", () => {
|
|
216
|
+
test("no-op when statusFile is undefined (even with prd set)", async () => {
|
|
217
|
+
const sw = new StatusWriter(undefined, makeConfig(), makeCtx());
|
|
218
|
+
sw.setPrd(makePrd());
|
|
219
|
+
// Should not throw
|
|
220
|
+
await expect(sw.update(0, 0)).resolves.toBeUndefined();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("no-op when prd not yet set", async () => {
|
|
224
|
+
const dir = await mkdtemp(join(tmpdir(), "sw-test-"));
|
|
225
|
+
const path = join(dir, "status.json");
|
|
226
|
+
const sw = new StatusWriter(path, makeConfig(), makeCtx());
|
|
227
|
+
await sw.update(0, 0);
|
|
228
|
+
expect(existsSync(path)).toBe(false);
|
|
229
|
+
await rm(dir, { recursive: true, force: true });
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// ============================================================================
|
|
234
|
+
// update — success path
|
|
235
|
+
// ============================================================================
|
|
236
|
+
|
|
237
|
+
describe("StatusWriter.update success path", () => {
|
|
238
|
+
let tmpDir: string;
|
|
239
|
+
|
|
240
|
+
beforeEach(async () => {
|
|
241
|
+
tmpDir = await mkdtemp(join(tmpdir(), "sw-test-"));
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
afterEach(async () => {
|
|
245
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("writes valid JSON status file", async () => {
|
|
249
|
+
const path = join(tmpDir, "status.json");
|
|
250
|
+
const sw = new StatusWriter(path, makeConfig(), makeCtx());
|
|
251
|
+
sw.setPrd(makePrd(2));
|
|
252
|
+
await sw.update(1.5, 3);
|
|
253
|
+
|
|
254
|
+
expect(existsSync(path)).toBe(true);
|
|
255
|
+
const content = JSON.parse(readFileSync(path, "utf8")) as NaxStatusFile;
|
|
256
|
+
expect(content.version).toBe(1);
|
|
257
|
+
expect(content.cost.spent).toBe(1.5);
|
|
258
|
+
expect(content.iterations).toBe(3);
|
|
259
|
+
expect(content.progress.total).toBe(2);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("overrides are applied on top of base snapshot", async () => {
|
|
263
|
+
const path = join(tmpDir, "status.json");
|
|
264
|
+
const sw = new StatusWriter(path, makeConfig(), makeCtx());
|
|
265
|
+
sw.setPrd(makePrd());
|
|
266
|
+
await sw.update(0, 0, { runStatus: "completed" });
|
|
267
|
+
|
|
268
|
+
const content = JSON.parse(readFileSync(path, "utf8")) as NaxStatusFile;
|
|
269
|
+
expect(content.run.status).toBe("completed");
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("multiple updates overwrite the file", async () => {
|
|
273
|
+
const path = join(tmpDir, "status.json");
|
|
274
|
+
const sw = new StatusWriter(path, makeConfig(), makeCtx());
|
|
275
|
+
sw.setPrd(makePrd());
|
|
276
|
+
sw.setRunStatus("running");
|
|
277
|
+
await sw.update(0, 1);
|
|
278
|
+
|
|
279
|
+
sw.setRunStatus("completed");
|
|
280
|
+
await sw.update(2.0, 5);
|
|
281
|
+
|
|
282
|
+
const content = JSON.parse(readFileSync(path, "utf8")) as NaxStatusFile;
|
|
283
|
+
expect(content.run.status).toBe("completed");
|
|
284
|
+
expect(content.cost.spent).toBe(2.0);
|
|
285
|
+
expect(content.iterations).toBe(5);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("no .tmp file remains after successful write", async () => {
|
|
289
|
+
const path = join(tmpDir, "status.json");
|
|
290
|
+
const sw = new StatusWriter(path, makeConfig(), makeCtx());
|
|
291
|
+
sw.setPrd(makePrd());
|
|
292
|
+
await sw.update(0, 0);
|
|
293
|
+
expect(existsSync(`${path}.tmp`)).toBe(false);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("PID is written to status file for crash detection", async () => {
|
|
297
|
+
const path = join(tmpDir, "status.json");
|
|
298
|
+
const testPid = 88888;
|
|
299
|
+
const sw = new StatusWriter(path, makeConfig(), makeCtx({ pid: testPid }));
|
|
300
|
+
sw.setPrd(makePrd());
|
|
301
|
+
await sw.update(0, 0);
|
|
302
|
+
|
|
303
|
+
const content = JSON.parse(readFileSync(path, "utf8")) as NaxStatusFile;
|
|
304
|
+
expect(content.run.pid).toBe(testPid);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// ============================================================================
|
|
309
|
+
// update — BUG-2 failure counter
|
|
310
|
+
// ============================================================================
|
|
311
|
+
|
|
312
|
+
describe("StatusWriter.update BUG-2 failure counter", () => {
|
|
313
|
+
test("write to a non-existent directory fails gracefully (non-fatal)", async () => {
|
|
314
|
+
const sw = new StatusWriter("/does/not/exist/status.json", makeConfig(), makeCtx());
|
|
315
|
+
sw.setPrd(makePrd());
|
|
316
|
+
// Should not throw — failure is logged, not re-thrown
|
|
317
|
+
await expect(sw.update(0, 0)).resolves.toBeUndefined();
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test("failure counter increments on consecutive failures", async () => {
|
|
321
|
+
// Use an invalid path to force failures
|
|
322
|
+
const sw = new StatusWriter("/no/such/dir/status.json", makeConfig(), makeCtx());
|
|
323
|
+
sw.setPrd(makePrd());
|
|
324
|
+
|
|
325
|
+
// Three consecutive failures should trigger error-level logging
|
|
326
|
+
// We can't easily introspect the counter directly, but we can verify
|
|
327
|
+
// that update() never throws
|
|
328
|
+
for (let i = 0; i < 5; i++) {
|
|
329
|
+
await expect(sw.update(0, i)).resolves.toBeUndefined();
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test("counter resets to 0 after successful write following failures", async () => {
|
|
334
|
+
const dir = await mkdtemp(join(tmpdir(), "sw-test-"));
|
|
335
|
+
const validPath = join(dir, "status.json");
|
|
336
|
+
const sw = new StatusWriter(validPath, makeConfig(), makeCtx());
|
|
337
|
+
sw.setPrd(makePrd());
|
|
338
|
+
|
|
339
|
+
// Cause some failures first by temporarily checking invalid path...
|
|
340
|
+
// We can do this by verifying a successful write after errors doesn't throw
|
|
341
|
+
await sw.update(0, 0);
|
|
342
|
+
expect(existsSync(validPath)).toBe(true);
|
|
343
|
+
await rm(dir, { recursive: true, force: true });
|
|
344
|
+
});
|
|
345
|
+
});
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test: Verify storyId presence in JSONL events (BUG-020)
|
|
3
|
+
*
|
|
4
|
+
* Ensures that key events (agent.start, agent.complete, verify, tdd, execution, escalation)
|
|
5
|
+
* include storyId in their data when a story is active.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
9
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import type { NaxConfig } from "../../src/config/schema";
|
|
13
|
+
import { initLogger, resetLogger } from "../../src/logger";
|
|
14
|
+
import { getLogger } from "../../src/logger/logger";
|
|
15
|
+
import type { LogEntry } from "../../src/logger/types";
|
|
16
|
+
import { executionStage } from "../../src/pipeline/stages/execution";
|
|
17
|
+
import { verifyStage } from "../../src/pipeline/stages/verify";
|
|
18
|
+
import type { PipelineContext } from "../../src/pipeline/types";
|
|
19
|
+
import type { PRD, UserStory } from "../../src/prd/types";
|
|
20
|
+
|
|
21
|
+
/** Captured log entries */
|
|
22
|
+
let capturedLogs: LogEntry[] = [];
|
|
23
|
+
|
|
24
|
+
/** Custom logger that captures entries */
|
|
25
|
+
function captureLogger() {
|
|
26
|
+
const originalLogger = getLogger();
|
|
27
|
+
capturedLogs = [];
|
|
28
|
+
|
|
29
|
+
// Override logger methods to capture entries
|
|
30
|
+
const captureLog = (stage: string, message: string, data?: Record<string, unknown>) => {
|
|
31
|
+
capturedLogs.push({
|
|
32
|
+
timestamp: new Date().toISOString(),
|
|
33
|
+
stage,
|
|
34
|
+
message,
|
|
35
|
+
data: data || {},
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// @ts-expect-error - Intentionally mocking logger for tests
|
|
40
|
+
originalLogger.info = captureLog;
|
|
41
|
+
// @ts-expect-error - Intentionally mocking logger for tests
|
|
42
|
+
originalLogger.warn = captureLog;
|
|
43
|
+
// @ts-expect-error - Intentionally mocking logger for tests
|
|
44
|
+
originalLogger.error = captureLog;
|
|
45
|
+
// @ts-expect-error - Intentionally mocking logger for tests
|
|
46
|
+
originalLogger.debug = captureLog;
|
|
47
|
+
|
|
48
|
+
return originalLogger;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Helper: Create minimal test context */
|
|
52
|
+
function createTestContext(overrides?: Partial<PipelineContext>): PipelineContext {
|
|
53
|
+
const story: UserStory = {
|
|
54
|
+
id: "US-001",
|
|
55
|
+
title: "Test Story",
|
|
56
|
+
description: "Test description",
|
|
57
|
+
acceptanceCriteria: ["Test passes"],
|
|
58
|
+
tags: [],
|
|
59
|
+
dependencies: [],
|
|
60
|
+
status: "pending",
|
|
61
|
+
passes: false,
|
|
62
|
+
escalations: [],
|
|
63
|
+
attempts: 0,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const prd: PRD = {
|
|
67
|
+
project: "test-project",
|
|
68
|
+
feature: "test-feature",
|
|
69
|
+
branchName: "test-branch",
|
|
70
|
+
createdAt: new Date().toISOString(),
|
|
71
|
+
updatedAt: new Date().toISOString(),
|
|
72
|
+
userStories: [story],
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const config: NaxConfig = {
|
|
76
|
+
version: 1,
|
|
77
|
+
models: {
|
|
78
|
+
fast: "claude-sonnet-4-5",
|
|
79
|
+
balanced: "claude-sonnet-4-5",
|
|
80
|
+
powerful: "claude-opus-4-6",
|
|
81
|
+
},
|
|
82
|
+
autoMode: {
|
|
83
|
+
enabled: true,
|
|
84
|
+
defaultAgent: "nax-agent-claude",
|
|
85
|
+
fallbackOrder: ["nax-agent-claude"],
|
|
86
|
+
complexityRouting: {
|
|
87
|
+
simple: "fast",
|
|
88
|
+
medium: "balanced",
|
|
89
|
+
complex: "powerful",
|
|
90
|
+
expert: "powerful",
|
|
91
|
+
},
|
|
92
|
+
escalation: {
|
|
93
|
+
enabled: true,
|
|
94
|
+
maxAttempts: 3,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
execution: {
|
|
98
|
+
maxIterations: 100,
|
|
99
|
+
iterationDelayMs: 1000,
|
|
100
|
+
costLimit: 50,
|
|
101
|
+
sessionTimeoutSeconds: 600,
|
|
102
|
+
maxStoriesPerFeature: 50,
|
|
103
|
+
verificationTimeoutSeconds: 120,
|
|
104
|
+
dangerouslySkipPermissions: false,
|
|
105
|
+
},
|
|
106
|
+
quality: {
|
|
107
|
+
requireTypecheck: false,
|
|
108
|
+
requireLint: false,
|
|
109
|
+
requireTests: true,
|
|
110
|
+
commands: {},
|
|
111
|
+
},
|
|
112
|
+
tdd: {
|
|
113
|
+
maxRetries: 3,
|
|
114
|
+
autoVerifyIsolation: true,
|
|
115
|
+
autoApproveVerifier: true,
|
|
116
|
+
},
|
|
117
|
+
constitution: {
|
|
118
|
+
enabled: false,
|
|
119
|
+
path: "constitution.md",
|
|
120
|
+
maxTokens: 2000,
|
|
121
|
+
},
|
|
122
|
+
analyze: {
|
|
123
|
+
llmEnhanced: false,
|
|
124
|
+
model: "balanced",
|
|
125
|
+
fallbackToKeywords: true,
|
|
126
|
+
maxCodebaseSummaryTokens: 4000,
|
|
127
|
+
},
|
|
128
|
+
review: {
|
|
129
|
+
enabled: true,
|
|
130
|
+
checks: ["test"],
|
|
131
|
+
commands: {},
|
|
132
|
+
},
|
|
133
|
+
plan: {
|
|
134
|
+
model: "balanced",
|
|
135
|
+
outputPath: "features",
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
config,
|
|
141
|
+
prd,
|
|
142
|
+
story,
|
|
143
|
+
stories: [story],
|
|
144
|
+
routing: {
|
|
145
|
+
complexity: "simple",
|
|
146
|
+
modelTier: "fast",
|
|
147
|
+
testStrategy: "test-after",
|
|
148
|
+
reasoning: "Test routing",
|
|
149
|
+
},
|
|
150
|
+
workdir: "/test/workdir",
|
|
151
|
+
hooks: { hooks: {} },
|
|
152
|
+
...overrides,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
describe("StoryId in JSONL events (BUG-020)", () => {
|
|
157
|
+
let tempDir: string;
|
|
158
|
+
|
|
159
|
+
beforeEach(() => {
|
|
160
|
+
tempDir = mkdtempSync(join(tmpdir(), "nax-storyid-test-"));
|
|
161
|
+
initLogger({ level: "debug", useChalk: false });
|
|
162
|
+
captureLogger();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
afterEach(() => {
|
|
166
|
+
resetLogger();
|
|
167
|
+
if (tempDir) {
|
|
168
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("verify stage events include storyId", async () => {
|
|
173
|
+
const ctx = createTestContext({
|
|
174
|
+
workdir: tempDir,
|
|
175
|
+
config: {
|
|
176
|
+
...createTestContext().config,
|
|
177
|
+
review: {
|
|
178
|
+
enabled: true,
|
|
179
|
+
checks: ["test"],
|
|
180
|
+
commands: {
|
|
181
|
+
test: "echo 'Tests passed'",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await verifyStage.execute(ctx);
|
|
188
|
+
|
|
189
|
+
// Check that verify events have storyId
|
|
190
|
+
const verifyEvents = capturedLogs.filter((log) => log.stage === "verify");
|
|
191
|
+
expect(verifyEvents.length).toBeGreaterThan(0);
|
|
192
|
+
|
|
193
|
+
for (const event of verifyEvents) {
|
|
194
|
+
expect(event.data).toHaveProperty("storyId");
|
|
195
|
+
expect(event.data.storyId).toBe("US-001");
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("verify stage skip events include storyId", async () => {
|
|
200
|
+
const ctx = createTestContext({
|
|
201
|
+
workdir: tempDir,
|
|
202
|
+
config: {
|
|
203
|
+
...createTestContext().config,
|
|
204
|
+
quality: {
|
|
205
|
+
requireTypecheck: false,
|
|
206
|
+
requireLint: false,
|
|
207
|
+
requireTests: false, // Skip verification
|
|
208
|
+
commands: {},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
await verifyStage.execute(ctx);
|
|
214
|
+
|
|
215
|
+
// Check that skip debug events have storyId
|
|
216
|
+
const verifyEvents = capturedLogs.filter((log) => log.stage === "verify");
|
|
217
|
+
expect(verifyEvents.length).toBeGreaterThan(0);
|
|
218
|
+
|
|
219
|
+
for (const event of verifyEvents) {
|
|
220
|
+
expect(event.data).toHaveProperty("storyId");
|
|
221
|
+
expect(event.data.storyId).toBe("US-001");
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("execution stage agent failure events include storyId", async () => {
|
|
226
|
+
const ctx = createTestContext({
|
|
227
|
+
workdir: tempDir,
|
|
228
|
+
prompt: "Test prompt for execution",
|
|
229
|
+
config: {
|
|
230
|
+
...createTestContext().config,
|
|
231
|
+
autoMode: {
|
|
232
|
+
...createTestContext().config.autoMode,
|
|
233
|
+
defaultAgent: "nonexistent-agent", // Will trigger agent not found
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const result = await executionStage.execute(ctx);
|
|
239
|
+
|
|
240
|
+
// Check that execution failure events have storyId
|
|
241
|
+
// The agent not found path logs a failure without storyId in the message
|
|
242
|
+
// This test verifies the agent failure path is reachable
|
|
243
|
+
expect(result.action).toBe("fail");
|
|
244
|
+
expect(result.reason).toContain("Agent");
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("verify stage failure events include storyId", async () => {
|
|
248
|
+
const ctx = createTestContext({
|
|
249
|
+
workdir: tempDir,
|
|
250
|
+
config: {
|
|
251
|
+
...createTestContext().config,
|
|
252
|
+
review: {
|
|
253
|
+
enabled: true,
|
|
254
|
+
checks: ["test"],
|
|
255
|
+
commands: {
|
|
256
|
+
test: "sh -c 'exit 1'", // Fail
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
await verifyStage.execute(ctx);
|
|
263
|
+
|
|
264
|
+
// Check that error events have storyId
|
|
265
|
+
const verifyErrorEvents = capturedLogs.filter((log) => log.stage === "verify");
|
|
266
|
+
expect(verifyErrorEvents.length).toBeGreaterThan(0);
|
|
267
|
+
|
|
268
|
+
for (const event of verifyErrorEvents) {
|
|
269
|
+
expect(event.data).toHaveProperty("storyId");
|
|
270
|
+
expect(event.data.storyId).toBe("US-001");
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
});
|