@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
package/src/tdd/index.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
TddSessionRole,
|
|
3
|
+
FailureCategory,
|
|
4
|
+
IsolationCheck,
|
|
5
|
+
TddSessionResult,
|
|
6
|
+
ThreeSessionTddResult,
|
|
7
|
+
} from "./types";
|
|
8
|
+
export {
|
|
9
|
+
isTestFile,
|
|
10
|
+
isSourceFile,
|
|
11
|
+
getChangedFiles,
|
|
12
|
+
verifyTestWriterIsolation,
|
|
13
|
+
verifyImplementerIsolation,
|
|
14
|
+
} from "./isolation";
|
|
15
|
+
export { runThreeSessionTdd } from "./orchestrator";
|
|
16
|
+
export { cleanupProcessTree, getPgid } from "./cleanup";
|
|
17
|
+
export {
|
|
18
|
+
buildTestWriterPrompt,
|
|
19
|
+
buildImplementerPrompt,
|
|
20
|
+
buildVerifierPrompt,
|
|
21
|
+
} from "./prompts";
|
|
22
|
+
export type { VerifierVerdict, VerdictCategorization } from "./verdict";
|
|
23
|
+
export { VERDICT_FILE, readVerdict, cleanupVerdict, categorizeVerdict } from "./verdict";
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isolation Verification
|
|
3
|
+
*
|
|
4
|
+
* Checks that TDD sessions respect their boundaries:
|
|
5
|
+
* - Session 1 (test writer): only test/ files modified
|
|
6
|
+
* - Session 2 (implementer): no test/ files modified
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { IsolationCheck } from "./types";
|
|
10
|
+
|
|
11
|
+
/** Common test directory patterns */
|
|
12
|
+
const TEST_PATTERNS = [/^test\//, /^tests\//, /^__tests__\//, /\.spec\.\w+$/, /\.test\.\w+$/, /\.e2e-spec\.\w+$/];
|
|
13
|
+
|
|
14
|
+
/** Common source directory patterns */
|
|
15
|
+
const SRC_PATTERNS = [/^src\//, /^lib\//, /^packages\//];
|
|
16
|
+
|
|
17
|
+
/** Check if a file path is a test file */
|
|
18
|
+
export function isTestFile(filePath: string): boolean {
|
|
19
|
+
return TEST_PATTERNS.some((pattern) => pattern.test(filePath));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Check if a file path is a source file */
|
|
23
|
+
export function isSourceFile(filePath: string): boolean {
|
|
24
|
+
return SRC_PATTERNS.some((pattern) => pattern.test(filePath));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Get changed files from git diff */
|
|
28
|
+
export async function getChangedFiles(workdir: string, fromRef = "HEAD"): Promise<string[]> {
|
|
29
|
+
const proc = Bun.spawn(["git", "diff", "--name-only", fromRef], {
|
|
30
|
+
cwd: workdir,
|
|
31
|
+
stdout: "pipe",
|
|
32
|
+
stderr: "pipe",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await proc.exited;
|
|
36
|
+
const output = await new Response(proc.stdout).text();
|
|
37
|
+
return output.trim().split("\n").filter(Boolean);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Get staged files */
|
|
41
|
+
export async function getStagedFiles(workdir: string): Promise<string[]> {
|
|
42
|
+
const proc = Bun.spawn(["git", "diff", "--name-only", "--cached"], {
|
|
43
|
+
cwd: workdir,
|
|
44
|
+
stdout: "pipe",
|
|
45
|
+
stderr: "pipe",
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await proc.exited;
|
|
49
|
+
const output = await new Response(proc.stdout).text();
|
|
50
|
+
return output.trim().split("\n").filter(Boolean);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Check if a file path matches any of the allowed patterns (glob-like) */
|
|
54
|
+
function matchesAllowedPath(filePath: string, allowedPaths: string[]): boolean {
|
|
55
|
+
return allowedPaths.some((pattern) => {
|
|
56
|
+
// Simple glob matching: ** = any directory, * = any filename segment
|
|
57
|
+
const regexPattern = pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\//g, "\\/");
|
|
58
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
59
|
+
return regex.test(filePath);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Verify test writer isolation:
|
|
65
|
+
* Only test files should be created/modified.
|
|
66
|
+
* No source files should be touched.
|
|
67
|
+
*
|
|
68
|
+
* @param workdir - Working directory
|
|
69
|
+
* @param beforeRef - Git ref to diff against
|
|
70
|
+
* @param allowedPaths - Glob patterns for files that can be modified (soft violations)
|
|
71
|
+
*/
|
|
72
|
+
export async function verifyTestWriterIsolation(
|
|
73
|
+
workdir: string,
|
|
74
|
+
beforeRef: string,
|
|
75
|
+
allowedPaths: string[] = ["src/index.ts", "src/**/index.ts"],
|
|
76
|
+
): Promise<IsolationCheck> {
|
|
77
|
+
const changed = await getChangedFiles(workdir, beforeRef);
|
|
78
|
+
const sourceFiles = changed.filter((f) => isSourceFile(f) && !isTestFile(f));
|
|
79
|
+
|
|
80
|
+
// Separate hard violations from soft violations (allowed paths)
|
|
81
|
+
const softViolations: string[] = [];
|
|
82
|
+
const violations: string[] = [];
|
|
83
|
+
|
|
84
|
+
for (const file of sourceFiles) {
|
|
85
|
+
if (matchesAllowedPath(file, allowedPaths)) {
|
|
86
|
+
softViolations.push(file);
|
|
87
|
+
} else {
|
|
88
|
+
violations.push(file);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
passed: violations.length === 0,
|
|
94
|
+
violations,
|
|
95
|
+
softViolations,
|
|
96
|
+
description: "Test writer should only modify test files, not source files",
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Verify implementer isolation:
|
|
102
|
+
* No test files should be modified.
|
|
103
|
+
* Only source files should be touched.
|
|
104
|
+
*/
|
|
105
|
+
export async function verifyImplementerIsolation(workdir: string, beforeRef: string): Promise<IsolationCheck> {
|
|
106
|
+
const changed = await getChangedFiles(workdir, beforeRef);
|
|
107
|
+
const testFiles = changed.filter((f) => isTestFile(f));
|
|
108
|
+
|
|
109
|
+
if (testFiles.length > 0) {
|
|
110
|
+
return {
|
|
111
|
+
passed: true, // Warn but pass
|
|
112
|
+
violations: [],
|
|
113
|
+
warnings: testFiles,
|
|
114
|
+
description: "Implementer modified test files (warning: should be minimal fixes only)",
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
passed: true,
|
|
120
|
+
violations: [],
|
|
121
|
+
description: "Implementer should not modify test files",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Three-Session TDD Orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the three-session TDD pipeline:
|
|
5
|
+
* 1. Session 1 (test-writer): Write tests only
|
|
6
|
+
* 2. Session 2 (implementer): Implement code to pass tests
|
|
7
|
+
* 3. Session 3 (verifier): Verify tests pass and changes are legitimate
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { AgentAdapter } from "../agents";
|
|
11
|
+
import type { ModelTier, NaxConfig } from "../config";
|
|
12
|
+
import { resolveModel } from "../config";
|
|
13
|
+
import { isGreenfieldStory } from "../context/greenfield";
|
|
14
|
+
import { getLogger } from "../logger";
|
|
15
|
+
import type { UserStory } from "../prd";
|
|
16
|
+
import { captureGitRef } from "../utils/git";
|
|
17
|
+
import { executeWithTimeout } from "../verification";
|
|
18
|
+
import { runFullSuiteGate } from "./rectification-gate";
|
|
19
|
+
import { rollbackToRef, runTddSession, truncateTestOutput } from "./session-runner";
|
|
20
|
+
import type { FailureCategory, TddSessionResult, ThreeSessionTddResult } from "./types";
|
|
21
|
+
import { categorizeVerdict, cleanupVerdict, readVerdict } from "./verdict";
|
|
22
|
+
|
|
23
|
+
/** Options for three-session TDD */
|
|
24
|
+
export interface ThreeSessionTddOptions {
|
|
25
|
+
agent: AgentAdapter;
|
|
26
|
+
story: UserStory;
|
|
27
|
+
config: NaxConfig;
|
|
28
|
+
workdir: string;
|
|
29
|
+
modelTier: ModelTier;
|
|
30
|
+
contextMarkdown?: string;
|
|
31
|
+
dryRun?: boolean;
|
|
32
|
+
lite?: boolean;
|
|
33
|
+
_recursionDepth?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Run the full three-session TDD pipeline for a user story.
|
|
38
|
+
*/
|
|
39
|
+
export async function runThreeSessionTdd(options: ThreeSessionTddOptions): Promise<ThreeSessionTddResult> {
|
|
40
|
+
const {
|
|
41
|
+
agent,
|
|
42
|
+
story,
|
|
43
|
+
config,
|
|
44
|
+
workdir,
|
|
45
|
+
modelTier,
|
|
46
|
+
contextMarkdown,
|
|
47
|
+
dryRun = false,
|
|
48
|
+
lite = false,
|
|
49
|
+
_recursionDepth = 0,
|
|
50
|
+
} = options;
|
|
51
|
+
const logger = getLogger();
|
|
52
|
+
|
|
53
|
+
// MED-7: Recursion guard to prevent infinite loops
|
|
54
|
+
const MAX_RECURSION_DEPTH = 2;
|
|
55
|
+
if (_recursionDepth >= MAX_RECURSION_DEPTH) {
|
|
56
|
+
logger.error("tdd", "Recursion depth limit reached", {
|
|
57
|
+
storyId: story.id,
|
|
58
|
+
depth: _recursionDepth,
|
|
59
|
+
maxDepth: MAX_RECURSION_DEPTH,
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
success: false,
|
|
63
|
+
sessions: [],
|
|
64
|
+
needsHumanReview: true,
|
|
65
|
+
reviewReason: "Recursion depth limit exceeded (max 2 fallbacks)",
|
|
66
|
+
failureCategory: "session-failure",
|
|
67
|
+
totalCost: 0,
|
|
68
|
+
lite,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
logger.info("tdd", "Three-Session TDD", {
|
|
73
|
+
storyId: story?.id,
|
|
74
|
+
title: story?.title,
|
|
75
|
+
lite,
|
|
76
|
+
recursionDepth: _recursionDepth,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Dry-run mode
|
|
80
|
+
if (dryRun) {
|
|
81
|
+
const modelDef = resolveModel(config.models[modelTier]);
|
|
82
|
+
logger.info("tdd", "[DRY RUN] Would run 3-session TDD", {
|
|
83
|
+
storyId: story.id,
|
|
84
|
+
lite,
|
|
85
|
+
session1: { role: "test-writer", model: modelDef.model },
|
|
86
|
+
session2: { role: "implementer", model: modelDef.model },
|
|
87
|
+
session3: { role: "verifier", model: modelDef.model },
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
success: true,
|
|
92
|
+
sessions: [],
|
|
93
|
+
needsHumanReview: false,
|
|
94
|
+
totalCost: 0,
|
|
95
|
+
lite,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const sessions: TddSessionResult[] = [];
|
|
100
|
+
let needsHumanReview = false;
|
|
101
|
+
let reviewReason: string | undefined;
|
|
102
|
+
|
|
103
|
+
const initialRef = (await captureGitRef(workdir)) ?? "HEAD";
|
|
104
|
+
const shouldRollbackOnFailure = config.tdd.rollbackOnFailure ?? true;
|
|
105
|
+
|
|
106
|
+
// Session 1: Test Writer
|
|
107
|
+
// BUG-018 Fix: Skip test-writer on retry iterations — tests already exist from first attempt.
|
|
108
|
+
// Saves ~3min per escalation by avoiding a no-op Claude Code session.
|
|
109
|
+
const isRetry = (story.attempts ?? 0) > 0;
|
|
110
|
+
const session1Ref = initialRef;
|
|
111
|
+
|
|
112
|
+
if (isRetry) {
|
|
113
|
+
logger.info("tdd", "Skipping test-writer on retry (attempt > 0, tests already exist)", {
|
|
114
|
+
storyId: story.id,
|
|
115
|
+
attempt: story.attempts,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let session1: TddSessionResult | undefined;
|
|
120
|
+
|
|
121
|
+
if (!isRetry) {
|
|
122
|
+
const testWriterTier = config.tdd.sessionTiers?.testWriter ?? "balanced";
|
|
123
|
+
session1 = await runTddSession(
|
|
124
|
+
"test-writer",
|
|
125
|
+
agent,
|
|
126
|
+
story,
|
|
127
|
+
config,
|
|
128
|
+
workdir,
|
|
129
|
+
testWriterTier,
|
|
130
|
+
session1Ref,
|
|
131
|
+
contextMarkdown,
|
|
132
|
+
lite,
|
|
133
|
+
lite,
|
|
134
|
+
);
|
|
135
|
+
sessions.push(session1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (session1 && !session1.success) {
|
|
139
|
+
needsHumanReview = true;
|
|
140
|
+
reviewReason = "Test writer session failed or violated isolation";
|
|
141
|
+
const failureCategory: FailureCategory =
|
|
142
|
+
session1.isolation && !session1.isolation.passed ? "isolation-violation" : "session-failure";
|
|
143
|
+
logger.warn("tdd", "[WARN] Test writer session failed", { storyId: story.id, reviewReason, failureCategory });
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
success: false,
|
|
147
|
+
sessions,
|
|
148
|
+
needsHumanReview,
|
|
149
|
+
reviewReason,
|
|
150
|
+
failureCategory,
|
|
151
|
+
totalCost: sessions.reduce((sum, s) => sum + s.estimatedCost, 0),
|
|
152
|
+
lite,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// BUG-20 Fix: Verify that test-writer session actually created test files
|
|
157
|
+
// On retry (BUG-018 fix), session1 is undefined — skip this check entirely
|
|
158
|
+
const testFilePatterns = /\.(test|spec)\.(ts|js|tsx|jsx)$/;
|
|
159
|
+
const testFilesCreated = session1 ? session1.filesChanged.filter((f) => testFilePatterns.test(f)) : [];
|
|
160
|
+
|
|
161
|
+
if (!isRetry && testFilesCreated.length === 0) {
|
|
162
|
+
// BUG-012 Fix: Before declaring greenfield, check if test files already exist in the repo.
|
|
163
|
+
// The test-writer may have produced 0 new files because tests were pre-written and committed
|
|
164
|
+
// separately (e.g. during dogfooding or manual setup). If tests already exist, skip
|
|
165
|
+
// test-writer phase and proceed directly to the implementer.
|
|
166
|
+
const testPatternGlob = config.context?.testCoverage?.testPattern ?? "**/*.{test,spec}.{ts,js,tsx,jsx}";
|
|
167
|
+
|
|
168
|
+
// Scan directly for existing test files — don't use isGreenfieldStory() here because its
|
|
169
|
+
// "safe fallback" returns false (not greenfield) on scan errors, which would incorrectly
|
|
170
|
+
// allow proceeding to the implementer when the workdir is unreadable.
|
|
171
|
+
let hasPreExistingTests = false;
|
|
172
|
+
try {
|
|
173
|
+
// isGreenfieldStory returns true when NO tests exist; we want the inverse
|
|
174
|
+
hasPreExistingTests = !(await isGreenfieldStory(story, workdir, testPatternGlob));
|
|
175
|
+
// Sanity check: if workdir doesn't exist, isGreenfieldStory returns false (safe fallback),
|
|
176
|
+
// meaning hasPreExistingTests = true — wrong. Validate by checking if workdir is readable.
|
|
177
|
+
const { existsSync } = await import("node:fs");
|
|
178
|
+
if (!existsSync(workdir)) {
|
|
179
|
+
hasPreExistingTests = false;
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
hasPreExistingTests = false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (hasPreExistingTests) {
|
|
186
|
+
// Tests exist in repo — test-writer correctly produced no new files.
|
|
187
|
+
// Skip the pause, proceed to implementer.
|
|
188
|
+
logger.info(
|
|
189
|
+
"tdd",
|
|
190
|
+
"Test writer created no new files but tests already exist in repo — skipping test-writer, proceeding to implementer (BUG-012 fix)",
|
|
191
|
+
{
|
|
192
|
+
storyId: story.id,
|
|
193
|
+
},
|
|
194
|
+
);
|
|
195
|
+
} else {
|
|
196
|
+
// Genuinely greenfield — no tests anywhere. Pause for human review.
|
|
197
|
+
needsHumanReview = true;
|
|
198
|
+
reviewReason = "Test writer session created no test files (greenfield project)";
|
|
199
|
+
logger.warn("tdd", "[WARN] Test writer created no test files - greenfield detected", {
|
|
200
|
+
storyId: story.id,
|
|
201
|
+
reviewReason,
|
|
202
|
+
filesChanged: session1?.filesChanged,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
success: false,
|
|
207
|
+
sessions,
|
|
208
|
+
needsHumanReview,
|
|
209
|
+
reviewReason,
|
|
210
|
+
failureCategory: "greenfield-no-tests",
|
|
211
|
+
totalCost: sessions.reduce((sum, s) => sum + s.estimatedCost, 0),
|
|
212
|
+
lite,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
logger.info("tdd", "Created test files", {
|
|
218
|
+
storyId: story.id,
|
|
219
|
+
testFilesCount: testFilesCreated.length,
|
|
220
|
+
testFiles: testFilesCreated,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const session2Ref = (await captureGitRef(workdir)) ?? "HEAD";
|
|
224
|
+
|
|
225
|
+
// Session 2: Implementer
|
|
226
|
+
const implementerTier = config.tdd.sessionTiers?.implementer ?? modelTier;
|
|
227
|
+
const session2 = await runTddSession(
|
|
228
|
+
"implementer",
|
|
229
|
+
agent,
|
|
230
|
+
story,
|
|
231
|
+
config,
|
|
232
|
+
workdir,
|
|
233
|
+
implementerTier,
|
|
234
|
+
session2Ref,
|
|
235
|
+
contextMarkdown,
|
|
236
|
+
lite,
|
|
237
|
+
lite,
|
|
238
|
+
);
|
|
239
|
+
sessions.push(session2);
|
|
240
|
+
|
|
241
|
+
if (!session2.success) {
|
|
242
|
+
needsHumanReview = true;
|
|
243
|
+
reviewReason = "Implementer session failed or violated isolation";
|
|
244
|
+
logger.warn("tdd", "[WARN] Implementer session failed", { storyId: story.id, reviewReason });
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
success: false,
|
|
248
|
+
sessions,
|
|
249
|
+
needsHumanReview,
|
|
250
|
+
reviewReason,
|
|
251
|
+
failureCategory: "session-failure",
|
|
252
|
+
totalCost: sessions.reduce((sum, s) => sum + s.estimatedCost, 0),
|
|
253
|
+
lite,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Full-Suite Gate (v0.11 Rectification)
|
|
258
|
+
await runFullSuiteGate(story, config, workdir, agent, implementerTier, contextMarkdown, lite, logger);
|
|
259
|
+
|
|
260
|
+
// Session 3: Verifier
|
|
261
|
+
const session3Ref = (await captureGitRef(workdir)) ?? "HEAD";
|
|
262
|
+
const verifierTier = config.tdd.sessionTiers?.verifier ?? "fast";
|
|
263
|
+
const session3 = await runTddSession(
|
|
264
|
+
"verifier",
|
|
265
|
+
agent,
|
|
266
|
+
story,
|
|
267
|
+
config,
|
|
268
|
+
workdir,
|
|
269
|
+
verifierTier,
|
|
270
|
+
session3Ref,
|
|
271
|
+
undefined,
|
|
272
|
+
false,
|
|
273
|
+
false,
|
|
274
|
+
);
|
|
275
|
+
sessions.push(session3);
|
|
276
|
+
|
|
277
|
+
// T9: Verdict-based post-TDD verification
|
|
278
|
+
const verdict = await readVerdict(workdir);
|
|
279
|
+
await cleanupVerdict(workdir);
|
|
280
|
+
|
|
281
|
+
let allSuccessful = sessions.every((s) => s.success);
|
|
282
|
+
let finalFailureCategory: FailureCategory | undefined;
|
|
283
|
+
|
|
284
|
+
if (verdict !== null) {
|
|
285
|
+
const categorization = categorizeVerdict(verdict, verdict.tests.allPassing);
|
|
286
|
+
|
|
287
|
+
if (categorization.success) {
|
|
288
|
+
logger.info("tdd", "[OK] Verifier verdict: approved", {
|
|
289
|
+
storyId: story.id,
|
|
290
|
+
verdictApproved: verdict.approved,
|
|
291
|
+
testsAllPassing: verdict.tests.allPassing,
|
|
292
|
+
});
|
|
293
|
+
allSuccessful = true;
|
|
294
|
+
needsHumanReview = false;
|
|
295
|
+
reviewReason = undefined;
|
|
296
|
+
} else {
|
|
297
|
+
logger.warn("tdd", "[WARN] Verifier verdict: rejected", {
|
|
298
|
+
storyId: story.id,
|
|
299
|
+
verdictApproved: verdict.approved,
|
|
300
|
+
failureCategory: categorization.failureCategory,
|
|
301
|
+
reviewReason: categorization.reviewReason,
|
|
302
|
+
});
|
|
303
|
+
allSuccessful = false;
|
|
304
|
+
finalFailureCategory = categorization.failureCategory;
|
|
305
|
+
needsHumanReview = true;
|
|
306
|
+
reviewReason = categorization.reviewReason;
|
|
307
|
+
}
|
|
308
|
+
} else {
|
|
309
|
+
// Fallback path: no verdict file
|
|
310
|
+
if (!allSuccessful) {
|
|
311
|
+
logger.info("tdd", "-> Running post-TDD test verification (no verdict file)", { storyId: story.id });
|
|
312
|
+
|
|
313
|
+
const testCmd = config.quality?.commands?.test ?? "bun test";
|
|
314
|
+
const timeoutSeconds = 120;
|
|
315
|
+
|
|
316
|
+
const postVerify = await executeWithTimeout(testCmd, timeoutSeconds, undefined, { cwd: workdir });
|
|
317
|
+
const testsActuallyPass = postVerify.success && postVerify.exitCode === 0;
|
|
318
|
+
|
|
319
|
+
const truncatedStdout = postVerify.output ? truncateTestOutput(postVerify.output) : "";
|
|
320
|
+
const truncatedStderr = postVerify.error ? truncateTestOutput(postVerify.error) : "";
|
|
321
|
+
|
|
322
|
+
if (testsActuallyPass) {
|
|
323
|
+
logger.info("tdd", "Sessions had non-zero exits but tests pass - treating as success", {
|
|
324
|
+
storyId: story.id,
|
|
325
|
+
stdout: truncatedStdout,
|
|
326
|
+
});
|
|
327
|
+
allSuccessful = true;
|
|
328
|
+
needsHumanReview = false;
|
|
329
|
+
reviewReason = undefined;
|
|
330
|
+
} else {
|
|
331
|
+
logger.warn("tdd", "[WARN] Post-TDD verification: tests still failing", {
|
|
332
|
+
storyId: story.id,
|
|
333
|
+
stdout: truncatedStdout,
|
|
334
|
+
stderr: truncatedStderr,
|
|
335
|
+
});
|
|
336
|
+
needsHumanReview = true;
|
|
337
|
+
reviewReason = "Verifier session identified issues and tests still fail";
|
|
338
|
+
finalFailureCategory = "tests-failing";
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
needsHumanReview = false;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const totalCost = sessions.reduce((sum, s) => sum + s.estimatedCost, 0);
|
|
346
|
+
|
|
347
|
+
logger.info("tdd", allSuccessful ? "[OK] Three-session TDD complete" : "[WARN] Three-session TDD needs review", {
|
|
348
|
+
storyId: story.id,
|
|
349
|
+
success: allSuccessful,
|
|
350
|
+
totalCost,
|
|
351
|
+
needsHumanReview,
|
|
352
|
+
reviewReason,
|
|
353
|
+
lite,
|
|
354
|
+
verdictAvailable: verdict !== null,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// Rollback git changes if TDD failed
|
|
358
|
+
if (!allSuccessful && shouldRollbackOnFailure) {
|
|
359
|
+
try {
|
|
360
|
+
await rollbackToRef(workdir, initialRef);
|
|
361
|
+
logger.info("tdd", "Rolled back git changes due to TDD failure", {
|
|
362
|
+
storyId: story.id,
|
|
363
|
+
failureCategory: finalFailureCategory,
|
|
364
|
+
});
|
|
365
|
+
} catch (error) {
|
|
366
|
+
logger.error("tdd", "Failed to rollback git changes after TDD failure", {
|
|
367
|
+
storyId: story.id,
|
|
368
|
+
error: error instanceof Error ? error.message : String(error),
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
success: allSuccessful,
|
|
375
|
+
sessions,
|
|
376
|
+
needsHumanReview,
|
|
377
|
+
reviewReason,
|
|
378
|
+
...(finalFailureCategory !== undefined ? { failureCategory: finalFailureCategory } : {}),
|
|
379
|
+
verdict,
|
|
380
|
+
totalCost,
|
|
381
|
+
lite,
|
|
382
|
+
};
|
|
383
|
+
}
|