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