@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,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run Completion — Final Metrics and Status Updates
|
|
3
|
+
*
|
|
4
|
+
* Handles the final steps after sequential execution completes:
|
|
5
|
+
* - Save run metrics
|
|
6
|
+
* - Log completion summary with per-story metrics
|
|
7
|
+
* - Update final status
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { getSafeLogger } from "../../logger";
|
|
11
|
+
import type { StoryMetrics } from "../../metrics";
|
|
12
|
+
import { saveRunMetrics } from "../../metrics";
|
|
13
|
+
import { countStories, isComplete, isStalled } from "../../prd";
|
|
14
|
+
import type { PRD } from "../../prd";
|
|
15
|
+
import type { StatusWriter } from "../status-writer";
|
|
16
|
+
|
|
17
|
+
export interface RunCompletionOptions {
|
|
18
|
+
runId: string;
|
|
19
|
+
feature: string;
|
|
20
|
+
startedAt: string;
|
|
21
|
+
prd: PRD;
|
|
22
|
+
allStoryMetrics: StoryMetrics[];
|
|
23
|
+
totalCost: number;
|
|
24
|
+
storiesCompleted: number;
|
|
25
|
+
iterations: number;
|
|
26
|
+
startTime: number;
|
|
27
|
+
workdir: string;
|
|
28
|
+
statusWriter: StatusWriter;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RunCompletionResult {
|
|
32
|
+
durationMs: number;
|
|
33
|
+
runCompletedAt: string;
|
|
34
|
+
finalCounts: {
|
|
35
|
+
total: number;
|
|
36
|
+
passed: number;
|
|
37
|
+
failed: number;
|
|
38
|
+
skipped: number;
|
|
39
|
+
pending: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Handle final run completion: save metrics, log summary, update status
|
|
45
|
+
*/
|
|
46
|
+
export async function handleRunCompletion(options: RunCompletionOptions): Promise<RunCompletionResult> {
|
|
47
|
+
const logger = getSafeLogger();
|
|
48
|
+
const {
|
|
49
|
+
runId,
|
|
50
|
+
feature,
|
|
51
|
+
startedAt,
|
|
52
|
+
prd,
|
|
53
|
+
allStoryMetrics,
|
|
54
|
+
totalCost,
|
|
55
|
+
storiesCompleted,
|
|
56
|
+
iterations,
|
|
57
|
+
startTime,
|
|
58
|
+
workdir,
|
|
59
|
+
statusWriter,
|
|
60
|
+
} = options;
|
|
61
|
+
|
|
62
|
+
const durationMs = Date.now() - startTime;
|
|
63
|
+
const runCompletedAt = new Date().toISOString();
|
|
64
|
+
|
|
65
|
+
// Save run metrics
|
|
66
|
+
const runMetrics = {
|
|
67
|
+
runId,
|
|
68
|
+
feature,
|
|
69
|
+
startedAt,
|
|
70
|
+
completedAt: runCompletedAt,
|
|
71
|
+
totalCost,
|
|
72
|
+
totalStories: allStoryMetrics.length,
|
|
73
|
+
storiesCompleted,
|
|
74
|
+
storiesFailed: countStories(prd).failed,
|
|
75
|
+
totalDurationMs: durationMs,
|
|
76
|
+
stories: allStoryMetrics,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
await saveRunMetrics(workdir, runMetrics);
|
|
80
|
+
|
|
81
|
+
// Log run completion
|
|
82
|
+
const finalCounts = countStories(prd);
|
|
83
|
+
|
|
84
|
+
// Prepare per-story metrics summary
|
|
85
|
+
const storyMetricsSummary = allStoryMetrics.map((sm) => ({
|
|
86
|
+
storyId: sm.storyId,
|
|
87
|
+
complexity: sm.complexity,
|
|
88
|
+
modelTier: sm.modelTier,
|
|
89
|
+
modelUsed: sm.modelUsed,
|
|
90
|
+
attempts: sm.attempts,
|
|
91
|
+
finalTier: sm.finalTier,
|
|
92
|
+
success: sm.success,
|
|
93
|
+
cost: sm.cost,
|
|
94
|
+
durationMs: sm.durationMs,
|
|
95
|
+
firstPassSuccess: sm.firstPassSuccess,
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
logger?.info("run.complete", "Feature execution completed", {
|
|
99
|
+
runId,
|
|
100
|
+
feature,
|
|
101
|
+
success: isComplete(prd),
|
|
102
|
+
iterations,
|
|
103
|
+
totalStories: finalCounts.total,
|
|
104
|
+
storiesCompleted,
|
|
105
|
+
storiesFailed: finalCounts.failed,
|
|
106
|
+
storiesPending: finalCounts.pending,
|
|
107
|
+
totalCost,
|
|
108
|
+
durationMs,
|
|
109
|
+
storyMetrics: storyMetricsSummary,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Update final status
|
|
113
|
+
statusWriter.setPrd(prd);
|
|
114
|
+
statusWriter.setCurrentStory(null);
|
|
115
|
+
statusWriter.setRunStatus(isComplete(prd) ? "completed" : isStalled(prd) ? "stalled" : "running");
|
|
116
|
+
await statusWriter.update(totalCost, iterations);
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
durationMs,
|
|
120
|
+
runCompletedAt,
|
|
121
|
+
finalCounts: {
|
|
122
|
+
total: finalCounts.total,
|
|
123
|
+
passed: finalCounts.passed,
|
|
124
|
+
failed: finalCounts.failed,
|
|
125
|
+
skipped: finalCounts.skipped,
|
|
126
|
+
pending: finalCounts.pending,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run Initialization
|
|
3
|
+
*
|
|
4
|
+
* Handles initialization tasks before the main execution loop starts:
|
|
5
|
+
* 1. State reconciliation (failed stories with commits)
|
|
6
|
+
* 2. Agent installation check
|
|
7
|
+
* 3. Story count validation
|
|
8
|
+
* 4. Initial PRD analysis
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import { getAgent } from "../../agents";
|
|
13
|
+
import type { NaxConfig } from "../../config";
|
|
14
|
+
import { AgentNotFoundError, AgentNotInstalledError, StoryLimitExceededError } from "../../errors";
|
|
15
|
+
import { getSafeLogger } from "../../logger";
|
|
16
|
+
import { countStories, loadPRD, markStoryPassed, savePRD } from "../../prd";
|
|
17
|
+
import type { PRD } from "../../prd/types";
|
|
18
|
+
import { hasCommitsForStory } from "../../utils/git";
|
|
19
|
+
|
|
20
|
+
export interface InitializationContext {
|
|
21
|
+
config: NaxConfig;
|
|
22
|
+
prdPath: string;
|
|
23
|
+
workdir: string;
|
|
24
|
+
dryRun: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface InitializationResult {
|
|
28
|
+
prd: PRD;
|
|
29
|
+
storyCounts: {
|
|
30
|
+
total: number;
|
|
31
|
+
pending: number;
|
|
32
|
+
passed: number;
|
|
33
|
+
failed: number;
|
|
34
|
+
skipped: number;
|
|
35
|
+
paused: number;
|
|
36
|
+
blocked: number;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Reconcile PRD state with git history
|
|
42
|
+
*
|
|
43
|
+
* Checks if failed stories have commits in git history and marks them as passed.
|
|
44
|
+
* This handles the case where TDD failed but agent already committed code.
|
|
45
|
+
*/
|
|
46
|
+
async function reconcileState(prd: PRD, prdPath: string, workdir: string): Promise<PRD> {
|
|
47
|
+
const logger = getSafeLogger();
|
|
48
|
+
let reconciledCount = 0;
|
|
49
|
+
let modified = false;
|
|
50
|
+
|
|
51
|
+
for (const story of prd.userStories) {
|
|
52
|
+
if (story.status === "failed") {
|
|
53
|
+
const hasCommits = await hasCommitsForStory(workdir, story.id);
|
|
54
|
+
if (hasCommits) {
|
|
55
|
+
logger?.warn("reconciliation", "Failed story has commits in git history, marking as passed", {
|
|
56
|
+
storyId: story.id,
|
|
57
|
+
title: story.title,
|
|
58
|
+
});
|
|
59
|
+
markStoryPassed(prd, story.id);
|
|
60
|
+
reconciledCount++;
|
|
61
|
+
modified = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (reconciledCount > 0) {
|
|
67
|
+
logger?.info("reconciliation", `Reconciled ${reconciledCount} failed stories from git history`);
|
|
68
|
+
await savePRD(prd, prdPath);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return prd;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Validate agent installation
|
|
76
|
+
*/
|
|
77
|
+
async function checkAgentInstalled(config: NaxConfig, dryRun: boolean): Promise<void> {
|
|
78
|
+
if (dryRun) return;
|
|
79
|
+
|
|
80
|
+
const logger = getSafeLogger();
|
|
81
|
+
const agent = getAgent(config.autoMode.defaultAgent);
|
|
82
|
+
|
|
83
|
+
if (!agent) {
|
|
84
|
+
logger?.error("execution", "Agent not found", {
|
|
85
|
+
agent: config.autoMode.defaultAgent,
|
|
86
|
+
});
|
|
87
|
+
throw new AgentNotFoundError(config.autoMode.defaultAgent);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const installed = await agent.isInstalled();
|
|
91
|
+
if (!installed) {
|
|
92
|
+
logger?.error("execution", "Agent is not installed or not in PATH", {
|
|
93
|
+
agent: config.autoMode.defaultAgent,
|
|
94
|
+
binary: agent.binary,
|
|
95
|
+
});
|
|
96
|
+
logger?.error("execution", "Please install the agent and try again");
|
|
97
|
+
throw new AgentNotInstalledError(config.autoMode.defaultAgent, agent.binary);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Validate story count doesn't exceed limit
|
|
103
|
+
*/
|
|
104
|
+
function validateStoryCount(counts: ReturnType<typeof countStories>, config: NaxConfig): void {
|
|
105
|
+
const logger = getSafeLogger();
|
|
106
|
+
|
|
107
|
+
if (counts.total > config.execution.maxStoriesPerFeature) {
|
|
108
|
+
logger?.error("execution", "Feature exceeds story limit", {
|
|
109
|
+
totalStories: counts.total,
|
|
110
|
+
limit: config.execution.maxStoriesPerFeature,
|
|
111
|
+
});
|
|
112
|
+
logger?.error("execution", "Split this feature into smaller features or increase maxStoriesPerFeature in config");
|
|
113
|
+
throw new StoryLimitExceededError(counts.total, config.execution.maxStoriesPerFeature);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Initialize execution: validate agent, reconcile state, check limits
|
|
119
|
+
*/
|
|
120
|
+
export async function initializeRun(ctx: InitializationContext): Promise<InitializationResult> {
|
|
121
|
+
const logger = getSafeLogger();
|
|
122
|
+
|
|
123
|
+
// Check agent installation
|
|
124
|
+
await checkAgentInstalled(ctx.config, ctx.dryRun);
|
|
125
|
+
|
|
126
|
+
// Load and reconcile PRD
|
|
127
|
+
let prd = await loadPRD(ctx.prdPath);
|
|
128
|
+
prd = await reconcileState(prd, ctx.prdPath, ctx.workdir);
|
|
129
|
+
|
|
130
|
+
// Validate story counts
|
|
131
|
+
const counts = countStories(prd);
|
|
132
|
+
validateStoryCount(counts, ctx.config);
|
|
133
|
+
|
|
134
|
+
logger?.info("execution", "Run initialization complete", {
|
|
135
|
+
totalStories: counts.total,
|
|
136
|
+
doneStories: counts.passed,
|
|
137
|
+
pendingStories: counts.pending,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return { prd, storyCounts: counts };
|
|
141
|
+
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run Lifecycle — Setup & Teardown Logic
|
|
3
|
+
*
|
|
4
|
+
* Encapsulates the bookend operations for a nax execution run:
|
|
5
|
+
* - Setup: lock acquisition, PRD loading, plugin initialization, reporter setup
|
|
6
|
+
* - Teardown: metrics computation, final status write, lock release, plugin cleanup
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as os from "node:os";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { getAgent } from "../../agents";
|
|
12
|
+
import type { NaxConfig } from "../../config";
|
|
13
|
+
import {
|
|
14
|
+
AgentNotFoundError,
|
|
15
|
+
AgentNotInstalledError,
|
|
16
|
+
LockAcquisitionError,
|
|
17
|
+
StoryLimitExceededError,
|
|
18
|
+
} from "../../errors";
|
|
19
|
+
import { type LoadedHooksConfig, fireHook } from "../../hooks";
|
|
20
|
+
import { getSafeLogger } from "../../logger";
|
|
21
|
+
import { type StoryMetrics, saveRunMetrics } from "../../metrics";
|
|
22
|
+
import { loadPlugins } from "../../plugins/loader";
|
|
23
|
+
import type { PluginRegistry } from "../../plugins/registry";
|
|
24
|
+
import type { PRD } from "../../prd";
|
|
25
|
+
import { countStories, isComplete, isStalled, loadPRD } from "../../prd";
|
|
26
|
+
import { clearCache as clearLlmCache, routeBatch as llmRouteBatch } from "../../routing/strategies/llm";
|
|
27
|
+
import { type StoryBatch, precomputeBatchPlan } from "../batching";
|
|
28
|
+
import { acquireLock, getAllReadyStories, hookCtx, releaseLock } from "../helpers";
|
|
29
|
+
import type { StatusWriter } from "../status-writer";
|
|
30
|
+
|
|
31
|
+
/** Setup result containing initialized state */
|
|
32
|
+
export interface SetupResult {
|
|
33
|
+
prd: PRD;
|
|
34
|
+
pluginRegistry: PluginRegistry;
|
|
35
|
+
batchPlan: StoryBatch[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Teardown options */
|
|
39
|
+
export interface TeardownOptions {
|
|
40
|
+
runId: string;
|
|
41
|
+
feature: string;
|
|
42
|
+
startedAt: string;
|
|
43
|
+
prd: PRD;
|
|
44
|
+
allStoryMetrics: StoryMetrics[];
|
|
45
|
+
totalCost: number;
|
|
46
|
+
storiesCompleted: number;
|
|
47
|
+
startTime: number;
|
|
48
|
+
workdir: string;
|
|
49
|
+
pluginRegistry: PluginRegistry;
|
|
50
|
+
statusWriter: StatusWriter;
|
|
51
|
+
iterations: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Run lifecycle manager — handles setup and teardown for nax execution
|
|
56
|
+
*/
|
|
57
|
+
export class RunLifecycle {
|
|
58
|
+
constructor(
|
|
59
|
+
private readonly prdPath: string,
|
|
60
|
+
private readonly workdir: string,
|
|
61
|
+
private readonly config: NaxConfig,
|
|
62
|
+
private readonly hooks: LoadedHooksConfig,
|
|
63
|
+
private readonly feature: string,
|
|
64
|
+
private readonly dryRun: boolean,
|
|
65
|
+
private readonly useBatch: boolean,
|
|
66
|
+
private readonly statusWriter: StatusWriter,
|
|
67
|
+
private readonly runId: string,
|
|
68
|
+
private readonly startedAt: string,
|
|
69
|
+
) {}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Setup: Acquire lock, load PRD, initialize plugins, setup reporters
|
|
73
|
+
*/
|
|
74
|
+
async setup(): Promise<SetupResult> {
|
|
75
|
+
const logger = getSafeLogger();
|
|
76
|
+
|
|
77
|
+
// Acquire lock to prevent concurrent execution
|
|
78
|
+
const lockAcquired = await acquireLock(this.workdir);
|
|
79
|
+
if (!lockAcquired) {
|
|
80
|
+
logger?.error("execution", "Another nax process is already running in this directory");
|
|
81
|
+
logger?.error("execution", "If you believe this is an error, remove nax.lock manually");
|
|
82
|
+
throw new LockAcquisitionError(this.workdir);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Load plugins
|
|
86
|
+
const globalPluginsDir = path.join(os.homedir(), ".nax", "plugins");
|
|
87
|
+
const projectPluginsDir = path.join(this.workdir, "nax", "plugins");
|
|
88
|
+
const configPlugins = this.config.plugins || [];
|
|
89
|
+
const pluginRegistry = await loadPlugins(globalPluginsDir, projectPluginsDir, configPlugins, this.workdir);
|
|
90
|
+
const reporters = pluginRegistry.getReporters();
|
|
91
|
+
|
|
92
|
+
logger?.info("plugins", `Loaded ${pluginRegistry.plugins.length} plugins`, {
|
|
93
|
+
plugins: pluginRegistry.plugins.map((p) => ({ name: p.name, version: p.version, provides: p.provides })),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Log run start
|
|
97
|
+
const routingMode = this.config.routing.llm?.mode ?? "hybrid";
|
|
98
|
+
logger?.info("run.start", `Starting feature: ${this.feature}`, {
|
|
99
|
+
runId: this.runId,
|
|
100
|
+
feature: this.feature,
|
|
101
|
+
workdir: this.workdir,
|
|
102
|
+
dryRun: this.dryRun,
|
|
103
|
+
useBatch: this.useBatch,
|
|
104
|
+
routingMode,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Fire on-start hook
|
|
108
|
+
await fireHook(this.hooks, "on-start", hookCtx(this.feature), this.workdir);
|
|
109
|
+
|
|
110
|
+
// Check agent installation before starting
|
|
111
|
+
const agent = getAgent(this.config.autoMode.defaultAgent);
|
|
112
|
+
if (!agent) {
|
|
113
|
+
logger?.error("execution", "Agent not found", {
|
|
114
|
+
agent: this.config.autoMode.defaultAgent,
|
|
115
|
+
});
|
|
116
|
+
throw new AgentNotFoundError(this.config.autoMode.defaultAgent);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const installed = await agent.isInstalled();
|
|
120
|
+
if (!installed) {
|
|
121
|
+
logger?.error("execution", "Agent is not installed or not in PATH", {
|
|
122
|
+
agent: this.config.autoMode.defaultAgent,
|
|
123
|
+
binary: agent.binary,
|
|
124
|
+
});
|
|
125
|
+
logger?.error("execution", "Please install the agent and try again");
|
|
126
|
+
throw new AgentNotInstalledError(this.config.autoMode.defaultAgent, agent.binary);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Load PRD
|
|
130
|
+
const prd = await loadPRD(this.prdPath);
|
|
131
|
+
const counts = countStories(prd);
|
|
132
|
+
|
|
133
|
+
// Status write point: run started
|
|
134
|
+
this.statusWriter.setPrd(prd);
|
|
135
|
+
this.statusWriter.setRunStatus("running");
|
|
136
|
+
this.statusWriter.setCurrentStory(null);
|
|
137
|
+
await this.statusWriter.update(0, 0);
|
|
138
|
+
|
|
139
|
+
// Update reporters with correct totalStories count
|
|
140
|
+
for (const reporter of reporters) {
|
|
141
|
+
if (reporter.onRunStart) {
|
|
142
|
+
try {
|
|
143
|
+
await reporter.onRunStart({
|
|
144
|
+
runId: this.runId,
|
|
145
|
+
feature: this.feature,
|
|
146
|
+
totalStories: counts.total,
|
|
147
|
+
startTime: this.startedAt,
|
|
148
|
+
});
|
|
149
|
+
} catch (error) {
|
|
150
|
+
logger?.warn("plugins", `Reporter '${reporter.name}' onRunStart failed`, { error });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// MEM-1: Validate story count doesn't exceed limit
|
|
156
|
+
if (counts.total > this.config.execution.maxStoriesPerFeature) {
|
|
157
|
+
logger?.error("execution", "Feature exceeds story limit", {
|
|
158
|
+
totalStories: counts.total,
|
|
159
|
+
limit: this.config.execution.maxStoriesPerFeature,
|
|
160
|
+
});
|
|
161
|
+
logger?.error("execution", "Split this feature into smaller features or increase maxStoriesPerFeature in config");
|
|
162
|
+
throw new StoryLimitExceededError(counts.total, this.config.execution.maxStoriesPerFeature);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
logger?.info("execution", `Starting ${this.feature}`, {
|
|
166
|
+
totalStories: counts.total,
|
|
167
|
+
doneStories: counts.passed,
|
|
168
|
+
pendingStories: counts.pending,
|
|
169
|
+
batchingEnabled: this.useBatch,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Clear LLM routing cache at start of new run
|
|
173
|
+
clearLlmCache();
|
|
174
|
+
|
|
175
|
+
// PERF-1: Precompute batch plan once from ready stories
|
|
176
|
+
let batchPlan: StoryBatch[] = [];
|
|
177
|
+
if (this.useBatch) {
|
|
178
|
+
const readyStories = getAllReadyStories(prd);
|
|
179
|
+
batchPlan = precomputeBatchPlan(readyStories, 4);
|
|
180
|
+
|
|
181
|
+
// Initial batch routing
|
|
182
|
+
const mode = this.config.routing.llm?.mode ?? "hybrid";
|
|
183
|
+
if (this.config.routing.strategy === "llm" && mode !== "per-story" && readyStories.length > 0) {
|
|
184
|
+
try {
|
|
185
|
+
logger?.debug("routing", "LLM batch routing: routing", { storyCount: readyStories.length, mode });
|
|
186
|
+
await llmRouteBatch(readyStories, { config: this.config });
|
|
187
|
+
logger?.debug("routing", "LLM batch routing complete", { label: "routing" });
|
|
188
|
+
} catch (err) {
|
|
189
|
+
logger?.warn("routing", "LLM batch routing failed, falling back to individual routing", {
|
|
190
|
+
error: (err as Error).message,
|
|
191
|
+
label: "routing",
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
prd,
|
|
199
|
+
pluginRegistry,
|
|
200
|
+
batchPlan,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Teardown: Compute final metrics, write final status, release lock, cleanup plugins
|
|
206
|
+
*/
|
|
207
|
+
async teardown(options: TeardownOptions): Promise<void> {
|
|
208
|
+
const logger = getSafeLogger();
|
|
209
|
+
const {
|
|
210
|
+
runId,
|
|
211
|
+
feature,
|
|
212
|
+
startedAt,
|
|
213
|
+
prd,
|
|
214
|
+
allStoryMetrics,
|
|
215
|
+
totalCost,
|
|
216
|
+
storiesCompleted,
|
|
217
|
+
startTime,
|
|
218
|
+
workdir,
|
|
219
|
+
pluginRegistry,
|
|
220
|
+
statusWriter,
|
|
221
|
+
iterations,
|
|
222
|
+
} = options;
|
|
223
|
+
|
|
224
|
+
const durationMs = Date.now() - startTime;
|
|
225
|
+
|
|
226
|
+
// Save run metrics
|
|
227
|
+
const runCompletedAt = new Date().toISOString();
|
|
228
|
+
const runMetrics = {
|
|
229
|
+
runId,
|
|
230
|
+
feature,
|
|
231
|
+
startedAt,
|
|
232
|
+
completedAt: runCompletedAt,
|
|
233
|
+
totalCost,
|
|
234
|
+
totalStories: allStoryMetrics.length,
|
|
235
|
+
storiesCompleted,
|
|
236
|
+
storiesFailed: countStories(prd).failed,
|
|
237
|
+
totalDurationMs: durationMs,
|
|
238
|
+
stories: allStoryMetrics,
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
await saveRunMetrics(workdir, runMetrics);
|
|
242
|
+
|
|
243
|
+
// Log run completion
|
|
244
|
+
const finalCounts = countStories(prd);
|
|
245
|
+
|
|
246
|
+
// Prepare per-story metrics summary
|
|
247
|
+
const storyMetricsSummary = allStoryMetrics.map((sm) => ({
|
|
248
|
+
storyId: sm.storyId,
|
|
249
|
+
complexity: sm.complexity,
|
|
250
|
+
modelTier: sm.modelTier,
|
|
251
|
+
modelUsed: sm.modelUsed,
|
|
252
|
+
attempts: sm.attempts,
|
|
253
|
+
finalTier: sm.finalTier,
|
|
254
|
+
success: sm.success,
|
|
255
|
+
cost: sm.cost,
|
|
256
|
+
durationMs: sm.durationMs,
|
|
257
|
+
firstPassSuccess: sm.firstPassSuccess,
|
|
258
|
+
}));
|
|
259
|
+
|
|
260
|
+
logger?.info("run.complete", "Feature execution completed", {
|
|
261
|
+
runId,
|
|
262
|
+
feature,
|
|
263
|
+
success: isComplete(prd),
|
|
264
|
+
iterations,
|
|
265
|
+
totalStories: finalCounts.total,
|
|
266
|
+
storiesCompleted,
|
|
267
|
+
storiesFailed: finalCounts.failed,
|
|
268
|
+
storiesPending: finalCounts.pending,
|
|
269
|
+
totalCost,
|
|
270
|
+
durationMs,
|
|
271
|
+
storyMetrics: storyMetricsSummary,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Emit onRunEnd to reporters
|
|
275
|
+
const reporters = pluginRegistry.getReporters();
|
|
276
|
+
for (const reporter of reporters) {
|
|
277
|
+
if (reporter.onRunEnd) {
|
|
278
|
+
try {
|
|
279
|
+
await reporter.onRunEnd({
|
|
280
|
+
runId,
|
|
281
|
+
totalDurationMs: durationMs,
|
|
282
|
+
totalCost,
|
|
283
|
+
storySummary: {
|
|
284
|
+
completed: storiesCompleted,
|
|
285
|
+
failed: finalCounts.failed,
|
|
286
|
+
skipped: finalCounts.skipped,
|
|
287
|
+
paused: finalCounts.paused,
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
} catch (error) {
|
|
291
|
+
logger?.warn("plugins", `Reporter '${reporter.name}' onRunEnd failed`, { error });
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Status write point: run end
|
|
297
|
+
statusWriter.setPrd(prd);
|
|
298
|
+
statusWriter.setCurrentStory(null);
|
|
299
|
+
statusWriter.setRunStatus(isComplete(prd) ? "completed" : isStalled(prd) ? "stalled" : "running");
|
|
300
|
+
await statusWriter.update(totalCost, iterations);
|
|
301
|
+
|
|
302
|
+
// Teardown plugins
|
|
303
|
+
try {
|
|
304
|
+
await pluginRegistry.teardownAll();
|
|
305
|
+
} catch (error) {
|
|
306
|
+
logger?.warn("plugins", "Plugin teardown failed", { error });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Always release lock, even if execution fails
|
|
310
|
+
await releaseLock(workdir);
|
|
311
|
+
}
|
|
312
|
+
}
|