@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,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Tier Escalation (ADR-003)
|
|
3
|
+
*
|
|
4
|
+
* Handles escalating model tiers through configurable tier chain
|
|
5
|
+
* with per-tier attempt budgets.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { TierConfig } from "../../config";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Escalate to the next tier in the configured order.
|
|
12
|
+
*
|
|
13
|
+
* @param currentTier - Current tier name
|
|
14
|
+
* @param tierOrder - Ordered tier config array from config (e.g., [{tier:"fast",attempts:5}, ...])
|
|
15
|
+
* @returns Next tier name, or null if at max tier
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const tiers = [{tier:"fast",attempts:5}, {tier:"balanced",attempts:3}, {tier:"powerful",attempts:2}];
|
|
20
|
+
* escalateTier("fast", tiers); // => "balanced"
|
|
21
|
+
* escalateTier("powerful", tiers); // => null
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function escalateTier(currentTier: string, tierOrder: TierConfig[]): string | null {
|
|
25
|
+
const currentIndex = tierOrder.findIndex((t) => t.tier === currentTier);
|
|
26
|
+
if (currentIndex === -1 || currentIndex === tierOrder.length - 1) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return tierOrder[currentIndex + 1].tier;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get the tier config for a given tier name.
|
|
34
|
+
*/
|
|
35
|
+
export function getTierConfig(tierName: string, tierOrder: TierConfig[]): TierConfig | undefined {
|
|
36
|
+
return tierOrder.find((t) => t.tier === tierName);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Calculate total max iterations from tier order (sum of all attempts).
|
|
41
|
+
*/
|
|
42
|
+
export function calculateMaxIterations(tierOrder: TierConfig[]): number {
|
|
43
|
+
return tierOrder.reduce((sum, t) => sum + t.attempts, 0);
|
|
44
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escalation module exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { escalateTier, getTierConfig, calculateMaxIterations } from "./escalation";
|
|
6
|
+
export {
|
|
7
|
+
resolveMaxAttemptsOutcome,
|
|
8
|
+
preIterationTierCheck,
|
|
9
|
+
handleTierEscalation,
|
|
10
|
+
type PreIterationCheckResult,
|
|
11
|
+
type EscalationHandlerContext,
|
|
12
|
+
type EscalationHandlerResult,
|
|
13
|
+
} from "./tier-escalation";
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier Escalation Logic
|
|
3
|
+
*
|
|
4
|
+
* Handles model tier escalation when stories fail:
|
|
5
|
+
* - Pre-iteration tier budget checks
|
|
6
|
+
* - Tier escalation with attempt counter reset
|
|
7
|
+
* - Max attempts outcome resolution (pause vs fail)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { NaxConfig } from "../../config";
|
|
11
|
+
import { type LoadedHooksConfig, fireHook } from "../../hooks";
|
|
12
|
+
import { getSafeLogger } from "../../logger";
|
|
13
|
+
import type { PRD, UserStory } from "../../prd";
|
|
14
|
+
import { markStoryFailed, savePRD } from "../../prd";
|
|
15
|
+
import { routeBatch as llmRouteBatch } from "../../routing/strategies/llm";
|
|
16
|
+
import type { FailureCategory } from "../../tdd/types";
|
|
17
|
+
import { calculateMaxIterations, escalateTier, getTierConfig } from "../escalation";
|
|
18
|
+
import { hookCtx } from "../helpers";
|
|
19
|
+
import { appendProgress } from "../progress";
|
|
20
|
+
import { handleMaxAttemptsReached, handleNoTierAvailable } from "./tier-outcome";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Determine the outcome when max attempts are reached for an escalation.
|
|
24
|
+
*
|
|
25
|
+
* Returns 'pause' if the failure category requires human review
|
|
26
|
+
* (isolation-violation or verifier-rejected). For all other categories
|
|
27
|
+
* (session-failure, tests-failing, or no category) returns 'fail'.
|
|
28
|
+
*
|
|
29
|
+
* Exported for unit-testing without running the full runner loop.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveMaxAttemptsOutcome(failureCategory?: FailureCategory): "pause" | "fail" {
|
|
32
|
+
if (!failureCategory) {
|
|
33
|
+
return "fail";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
switch (failureCategory) {
|
|
37
|
+
case "isolation-violation":
|
|
38
|
+
case "verifier-rejected":
|
|
39
|
+
case "greenfield-no-tests":
|
|
40
|
+
return "pause";
|
|
41
|
+
case "session-failure":
|
|
42
|
+
case "tests-failing":
|
|
43
|
+
return "fail";
|
|
44
|
+
default:
|
|
45
|
+
// Exhaustive check: if a new FailureCategory is added, this will error
|
|
46
|
+
failureCategory satisfies never;
|
|
47
|
+
return "fail";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface PreIterationCheckResult {
|
|
52
|
+
shouldSkipIteration: boolean;
|
|
53
|
+
prdDirty: boolean;
|
|
54
|
+
prd: PRD;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Pre-iteration tier escalation check (BUG-16 + BUG-17 fix)
|
|
59
|
+
*
|
|
60
|
+
* Check if story has exceeded current tier's attempt budget BEFORE spawning agent.
|
|
61
|
+
* If exceeded, escalate to next tier or mark as failed.
|
|
62
|
+
*/
|
|
63
|
+
export async function preIterationTierCheck(
|
|
64
|
+
story: UserStory,
|
|
65
|
+
routing: { modelTier: string },
|
|
66
|
+
config: NaxConfig,
|
|
67
|
+
prd: PRD,
|
|
68
|
+
prdPath: string,
|
|
69
|
+
featureDir: string | undefined,
|
|
70
|
+
hooks: LoadedHooksConfig,
|
|
71
|
+
feature: string,
|
|
72
|
+
totalCost: number,
|
|
73
|
+
workdir: string,
|
|
74
|
+
): Promise<PreIterationCheckResult> {
|
|
75
|
+
const logger = getSafeLogger();
|
|
76
|
+
const currentTier = story.routing?.modelTier ?? routing.modelTier;
|
|
77
|
+
const tierOrder = config.autoMode.escalation?.tierOrder || [];
|
|
78
|
+
const tierCfg = tierOrder.length > 0 ? getTierConfig(currentTier, tierOrder) : undefined;
|
|
79
|
+
|
|
80
|
+
if (!tierCfg || (story.attempts ?? 0) < tierCfg.attempts) {
|
|
81
|
+
// Story still has budget in current tier
|
|
82
|
+
return { shouldSkipIteration: false, prdDirty: false, prd };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Exceeded current tier budget — try to escalate
|
|
86
|
+
const nextTier = escalateTier(currentTier, tierOrder);
|
|
87
|
+
const routingMode = config.routing.llm?.mode ?? "hybrid";
|
|
88
|
+
|
|
89
|
+
if (nextTier && config.autoMode.escalation.enabled) {
|
|
90
|
+
logger?.warn("escalation", "Story exceeded tier budget, escalating", {
|
|
91
|
+
storyId: story.id,
|
|
92
|
+
attempts: story.attempts,
|
|
93
|
+
tierAttempts: tierCfg.attempts,
|
|
94
|
+
currentTier,
|
|
95
|
+
nextTier,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Update story routing in PRD and reset attempts for new tier
|
|
99
|
+
const updatedPrd = {
|
|
100
|
+
...prd,
|
|
101
|
+
userStories: prd.userStories.map((s) =>
|
|
102
|
+
s.id === story.id
|
|
103
|
+
? {
|
|
104
|
+
...s,
|
|
105
|
+
attempts: 0, // Reset attempts for new tier
|
|
106
|
+
routing: s.routing ? { ...s.routing, modelTier: nextTier } : { ...routing, modelTier: nextTier },
|
|
107
|
+
}
|
|
108
|
+
: s,
|
|
109
|
+
) as PRD["userStories"],
|
|
110
|
+
} as PRD;
|
|
111
|
+
await savePRD(updatedPrd, prdPath);
|
|
112
|
+
|
|
113
|
+
// Hybrid mode: re-route story after escalation
|
|
114
|
+
if (routingMode === "hybrid") {
|
|
115
|
+
await tryLlmBatchRoute(config, [story], "hybrid-re-route");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Skip to next iteration (will reload PRD and use new tier)
|
|
119
|
+
return { shouldSkipIteration: true, prdDirty: true, prd: updatedPrd };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// No next tier or escalation disabled — mark story as failed
|
|
123
|
+
logger?.error("execution", "Story failed - all tiers exhausted", {
|
|
124
|
+
storyId: story.id,
|
|
125
|
+
attempts: story.attempts,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const failedPrd = { ...prd };
|
|
129
|
+
markStoryFailed(failedPrd, story.id);
|
|
130
|
+
await savePRD(failedPrd, prdPath);
|
|
131
|
+
|
|
132
|
+
if (featureDir) {
|
|
133
|
+
await appendProgress(featureDir, story.id, "failed", `${story.title} — All tiers exhausted`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
await fireHook(
|
|
137
|
+
hooks,
|
|
138
|
+
"on-story-fail",
|
|
139
|
+
hookCtx(feature, {
|
|
140
|
+
storyId: story.id,
|
|
141
|
+
status: "failed",
|
|
142
|
+
reason: `All tiers exhausted (${story.attempts} attempts)`,
|
|
143
|
+
cost: totalCost,
|
|
144
|
+
}),
|
|
145
|
+
workdir,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Skip to next iteration (will pick next story)
|
|
149
|
+
return { shouldSkipIteration: true, prdDirty: true, prd: failedPrd };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Try LLM batch routing for ready stories. Logs and swallows errors (falls back to per-story routing).
|
|
154
|
+
*/
|
|
155
|
+
async function tryLlmBatchRoute(config: NaxConfig, stories: UserStory[], label = "routing"): Promise<void> {
|
|
156
|
+
const mode = config.routing.llm?.mode ?? "hybrid";
|
|
157
|
+
if (config.routing.strategy !== "llm" || mode === "per-story" || stories.length === 0) return;
|
|
158
|
+
const logger = getSafeLogger();
|
|
159
|
+
try {
|
|
160
|
+
logger?.debug("routing", `LLM batch routing: ${label}`, { storyCount: stories.length, mode });
|
|
161
|
+
await llmRouteBatch(stories, { config });
|
|
162
|
+
logger?.debug("routing", "LLM batch routing complete", { label });
|
|
163
|
+
} catch (err) {
|
|
164
|
+
logger?.warn("routing", "LLM batch routing failed, falling back to individual routing", {
|
|
165
|
+
error: (err as Error).message,
|
|
166
|
+
label,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface EscalationHandlerContext {
|
|
172
|
+
story: UserStory;
|
|
173
|
+
storiesToExecute: UserStory[];
|
|
174
|
+
isBatchExecution: boolean;
|
|
175
|
+
routing: { modelTier: string; testStrategy: string };
|
|
176
|
+
pipelineResult: {
|
|
177
|
+
context: {
|
|
178
|
+
retryAsLite?: boolean;
|
|
179
|
+
tddFailureCategory?: FailureCategory;
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
config: NaxConfig;
|
|
183
|
+
prd: PRD;
|
|
184
|
+
prdPath: string;
|
|
185
|
+
featureDir?: string;
|
|
186
|
+
hooks: LoadedHooksConfig;
|
|
187
|
+
feature: string;
|
|
188
|
+
totalCost: number;
|
|
189
|
+
workdir: string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export interface EscalationHandlerResult {
|
|
193
|
+
outcome: "escalated" | "paused" | "failed";
|
|
194
|
+
prdDirty: boolean;
|
|
195
|
+
prd: PRD;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Handle tier escalation after pipeline escalation action
|
|
200
|
+
*
|
|
201
|
+
* Escalates to next tier or marks story as paused/failed based on failure category.
|
|
202
|
+
*/
|
|
203
|
+
export async function handleTierEscalation(ctx: EscalationHandlerContext): Promise<EscalationHandlerResult> {
|
|
204
|
+
const logger = getSafeLogger();
|
|
205
|
+
const nextTier = escalateTier(ctx.routing.modelTier, ctx.config.autoMode.escalation.tierOrder);
|
|
206
|
+
const escalateWholeBatch = ctx.config.autoMode.escalation.escalateEntireBatch ?? true;
|
|
207
|
+
const storiesToEscalate = ctx.isBatchExecution && escalateWholeBatch ? ctx.storiesToExecute : [ctx.story];
|
|
208
|
+
|
|
209
|
+
// Retrieve TDD-specific context flags set by executionStage
|
|
210
|
+
const escalateRetryAsLite = ctx.pipelineResult.context.retryAsLite === true;
|
|
211
|
+
const escalateFailureCategory = ctx.pipelineResult.context.tddFailureCategory;
|
|
212
|
+
// S5: Auto-switch to test-after on greenfield-no-tests
|
|
213
|
+
const escalateRetryAsTestAfter = escalateFailureCategory === "greenfield-no-tests";
|
|
214
|
+
const routingMode = ctx.config.routing.llm?.mode ?? "hybrid";
|
|
215
|
+
|
|
216
|
+
if (!nextTier || !ctx.config.autoMode.escalation.enabled) {
|
|
217
|
+
// No next tier or escalation disabled — pause or fail based on failure category
|
|
218
|
+
return await handleNoTierAvailable(ctx, escalateFailureCategory);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const maxAttempts = calculateMaxIterations(ctx.config.autoMode.escalation.tierOrder);
|
|
222
|
+
const canEscalate = storiesToEscalate.every((s) => (s.attempts ?? 0) < maxAttempts);
|
|
223
|
+
|
|
224
|
+
if (!canEscalate) {
|
|
225
|
+
// Max attempts reached — pause or fail based on failure category
|
|
226
|
+
return await handleMaxAttemptsReached(ctx, escalateFailureCategory);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Can escalate — log and update stories
|
|
230
|
+
for (const s of storiesToEscalate) {
|
|
231
|
+
const currentTestStrategy = s.routing?.testStrategy ?? ctx.routing.testStrategy;
|
|
232
|
+
const shouldSwitchToTestAfter = escalateRetryAsTestAfter && currentTestStrategy !== "test-after";
|
|
233
|
+
|
|
234
|
+
if (shouldSwitchToTestAfter) {
|
|
235
|
+
logger?.warn("escalation", "Switching strategy to test-after (greenfield-no-tests fallback)", {
|
|
236
|
+
storyId: s.id,
|
|
237
|
+
fromStrategy: currentTestStrategy,
|
|
238
|
+
toStrategy: "test-after",
|
|
239
|
+
});
|
|
240
|
+
} else {
|
|
241
|
+
logger?.warn("escalation", "Escalating story to next tier", {
|
|
242
|
+
storyId: s.id,
|
|
243
|
+
nextTier,
|
|
244
|
+
retryAsLite: escalateRetryAsLite,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const errorMessage = `Attempt ${ctx.story.attempts + 1} failed with model tier: ${ctx.routing.modelTier}${ctx.isBatchExecution ? " (in batch)" : ""}`;
|
|
250
|
+
|
|
251
|
+
const updatedPrd = {
|
|
252
|
+
...ctx.prd,
|
|
253
|
+
userStories: ctx.prd.userStories.map((s) => {
|
|
254
|
+
const shouldEscalate = storiesToEscalate.some((story) => story.id === s.id);
|
|
255
|
+
if (!shouldEscalate) return s;
|
|
256
|
+
|
|
257
|
+
// S5: Check if this is a one-time test-after switch
|
|
258
|
+
const currentTestStrategy = s.routing?.testStrategy ?? ctx.routing.testStrategy;
|
|
259
|
+
const shouldSwitchToTestAfter = escalateRetryAsTestAfter && currentTestStrategy !== "test-after";
|
|
260
|
+
|
|
261
|
+
const baseRouting = s.routing ?? { ...ctx.routing };
|
|
262
|
+
const updatedRouting = {
|
|
263
|
+
...baseRouting,
|
|
264
|
+
modelTier: shouldSwitchToTestAfter ? baseRouting.modelTier : nextTier,
|
|
265
|
+
...(escalateRetryAsLite ? { testStrategy: "three-session-tdd-lite" as const } : {}),
|
|
266
|
+
...(shouldSwitchToTestAfter ? { testStrategy: "test-after" as const } : {}),
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// BUG-011: Reset attempt counter on tier escalation
|
|
270
|
+
const currentStoryTier = s.routing?.modelTier ?? ctx.routing.modelTier;
|
|
271
|
+
const isChangingTier = currentStoryTier !== nextTier;
|
|
272
|
+
const shouldResetAttempts = isChangingTier || shouldSwitchToTestAfter;
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
...s,
|
|
276
|
+
attempts: shouldResetAttempts ? 0 : (s.attempts ?? 0) + 1,
|
|
277
|
+
routing: updatedRouting,
|
|
278
|
+
priorErrors: [...(s.priorErrors || []), errorMessage],
|
|
279
|
+
} as UserStory;
|
|
280
|
+
}) as PRD["userStories"],
|
|
281
|
+
} as PRD;
|
|
282
|
+
|
|
283
|
+
await savePRD(updatedPrd, ctx.prdPath);
|
|
284
|
+
|
|
285
|
+
// Hybrid mode: re-route escalated stories
|
|
286
|
+
if (routingMode === "hybrid") {
|
|
287
|
+
await tryLlmBatchRoute(ctx.config, storiesToEscalate, "hybrid-re-route-pipeline");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
outcome: "escalated",
|
|
292
|
+
prdDirty: true,
|
|
293
|
+
prd: updatedPrd,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier Escalation Outcome Handlers
|
|
3
|
+
*
|
|
4
|
+
* Extracted from tier-escalation.ts: handles outcomes when escalation
|
|
5
|
+
* is not possible (no tier available or max attempts reached).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { fireHook } from "../../hooks";
|
|
9
|
+
import { getSafeLogger } from "../../logger";
|
|
10
|
+
import { markStoryFailed, markStoryPaused, savePRD } from "../../prd";
|
|
11
|
+
import type { FailureCategory } from "../../tdd/types";
|
|
12
|
+
import { hookCtx } from "../helpers";
|
|
13
|
+
import { appendProgress } from "../progress";
|
|
14
|
+
import type { EscalationHandlerContext, EscalationHandlerResult } from "./tier-escalation";
|
|
15
|
+
import { resolveMaxAttemptsOutcome } from "./tier-escalation";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Handle case when no tier is available for escalation
|
|
19
|
+
*/
|
|
20
|
+
export async function handleNoTierAvailable(
|
|
21
|
+
ctx: EscalationHandlerContext,
|
|
22
|
+
failureCategory?: FailureCategory,
|
|
23
|
+
): Promise<EscalationHandlerResult> {
|
|
24
|
+
const logger = getSafeLogger();
|
|
25
|
+
const outcome = resolveMaxAttemptsOutcome(failureCategory);
|
|
26
|
+
|
|
27
|
+
if (outcome === "pause") {
|
|
28
|
+
const pausedPrd = { ...ctx.prd };
|
|
29
|
+
markStoryPaused(pausedPrd, ctx.story.id);
|
|
30
|
+
await savePRD(pausedPrd, ctx.prdPath);
|
|
31
|
+
|
|
32
|
+
logger?.warn("execution", "Story paused - no tier available (needs human review)", {
|
|
33
|
+
storyId: ctx.story.id,
|
|
34
|
+
failureCategory,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (ctx.featureDir) {
|
|
38
|
+
await appendProgress(
|
|
39
|
+
ctx.featureDir,
|
|
40
|
+
ctx.story.id,
|
|
41
|
+
"paused",
|
|
42
|
+
`${ctx.story.title} — Execution stopped (needs human review)`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await fireHook(
|
|
47
|
+
ctx.hooks,
|
|
48
|
+
"on-pause",
|
|
49
|
+
hookCtx(ctx.feature, {
|
|
50
|
+
storyId: ctx.story.id,
|
|
51
|
+
reason: `Execution stopped (${failureCategory ?? "unknown"} requires human review)`,
|
|
52
|
+
cost: ctx.totalCost,
|
|
53
|
+
}),
|
|
54
|
+
ctx.workdir,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return { outcome: "paused", prdDirty: true, prd: pausedPrd };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Outcome is "fail"
|
|
61
|
+
const failedPrd = { ...ctx.prd };
|
|
62
|
+
markStoryFailed(failedPrd, ctx.story.id, failureCategory);
|
|
63
|
+
await savePRD(failedPrd, ctx.prdPath);
|
|
64
|
+
|
|
65
|
+
logger?.error("execution", "Story failed - execution failed", {
|
|
66
|
+
storyId: ctx.story.id,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (ctx.featureDir) {
|
|
70
|
+
await appendProgress(ctx.featureDir, ctx.story.id, "failed", `${ctx.story.title} — Execution failed`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
await fireHook(
|
|
74
|
+
ctx.hooks,
|
|
75
|
+
"on-story-fail",
|
|
76
|
+
hookCtx(ctx.feature, {
|
|
77
|
+
storyId: ctx.story.id,
|
|
78
|
+
status: "failed",
|
|
79
|
+
reason: "Execution failed",
|
|
80
|
+
cost: ctx.totalCost,
|
|
81
|
+
}),
|
|
82
|
+
ctx.workdir,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return { outcome: "failed", prdDirty: true, prd: failedPrd };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Handle case when max attempts are reached
|
|
90
|
+
*/
|
|
91
|
+
export async function handleMaxAttemptsReached(
|
|
92
|
+
ctx: EscalationHandlerContext,
|
|
93
|
+
failureCategory?: FailureCategory,
|
|
94
|
+
): Promise<EscalationHandlerResult> {
|
|
95
|
+
const logger = getSafeLogger();
|
|
96
|
+
const outcome = resolveMaxAttemptsOutcome(failureCategory);
|
|
97
|
+
|
|
98
|
+
if (outcome === "pause") {
|
|
99
|
+
const pausedPrd = { ...ctx.prd };
|
|
100
|
+
markStoryPaused(pausedPrd, ctx.story.id);
|
|
101
|
+
await savePRD(pausedPrd, ctx.prdPath);
|
|
102
|
+
|
|
103
|
+
logger?.warn("execution", "Story paused - max attempts reached (needs human review)", {
|
|
104
|
+
storyId: ctx.story.id,
|
|
105
|
+
failureCategory,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (ctx.featureDir) {
|
|
109
|
+
await appendProgress(
|
|
110
|
+
ctx.featureDir,
|
|
111
|
+
ctx.story.id,
|
|
112
|
+
"paused",
|
|
113
|
+
`${ctx.story.title} — Max attempts reached (needs human review)`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
await fireHook(
|
|
118
|
+
ctx.hooks,
|
|
119
|
+
"on-pause",
|
|
120
|
+
hookCtx(ctx.feature, {
|
|
121
|
+
storyId: ctx.story.id,
|
|
122
|
+
reason: `Max attempts reached (${failureCategory ?? "unknown"} requires human review)`,
|
|
123
|
+
cost: ctx.totalCost,
|
|
124
|
+
}),
|
|
125
|
+
ctx.workdir,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
return { outcome: "paused", prdDirty: true, prd: pausedPrd };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Outcome is "fail"
|
|
132
|
+
const failedPrd = { ...ctx.prd };
|
|
133
|
+
markStoryFailed(failedPrd, ctx.story.id, failureCategory);
|
|
134
|
+
await savePRD(failedPrd, ctx.prdPath);
|
|
135
|
+
|
|
136
|
+
logger?.error("execution", "Story failed - max attempts reached", {
|
|
137
|
+
storyId: ctx.story.id,
|
|
138
|
+
failureCategory,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (ctx.featureDir) {
|
|
142
|
+
await appendProgress(ctx.featureDir, ctx.story.id, "failed", `${ctx.story.title} — Max attempts reached`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await fireHook(
|
|
146
|
+
ctx.hooks,
|
|
147
|
+
"on-story-fail",
|
|
148
|
+
hookCtx(ctx.feature, {
|
|
149
|
+
storyId: ctx.story.id,
|
|
150
|
+
status: "failed",
|
|
151
|
+
reason: "Max attempts reached",
|
|
152
|
+
cost: ctx.totalCost,
|
|
153
|
+
}),
|
|
154
|
+
ctx.workdir,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
return { outcome: "failed", prdDirty: true, prd: failedPrd };
|
|
158
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution Helper Functions
|
|
3
|
+
*
|
|
4
|
+
* Re-export barrel for backward compatibility.
|
|
5
|
+
* Story context: ./story-context
|
|
6
|
+
* Lock management: ./lock
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Error Handling Pattern for Ngent
|
|
11
|
+
*
|
|
12
|
+
* 1. Critical Errors (invalid config, missing required files, security violations):
|
|
13
|
+
* - Action: throw Error with descriptive message
|
|
14
|
+
*
|
|
15
|
+
* 2. Expected Conditions (no more stories, queue empty, optional feature unavailable):
|
|
16
|
+
* - Action: return null or undefined
|
|
17
|
+
*
|
|
18
|
+
* 3. Validation Issues (multiple collected errors, partial data problems):
|
|
19
|
+
* - Action: collect errors in array and return as { errors: string[] }
|
|
20
|
+
*
|
|
21
|
+
* 4. Non-Fatal Warnings (context build failures, optional file missing, rate limit):
|
|
22
|
+
* - Action: console.warn() + continue execution
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// Story context building
|
|
26
|
+
export {
|
|
27
|
+
type ExecutionResult,
|
|
28
|
+
hookCtx,
|
|
29
|
+
maybeGetContext,
|
|
30
|
+
buildStoryContext,
|
|
31
|
+
buildStoryContextFull,
|
|
32
|
+
getAllReadyStories,
|
|
33
|
+
type StoryCounts,
|
|
34
|
+
formatProgress,
|
|
35
|
+
} from "./story-context";
|
|
36
|
+
|
|
37
|
+
// Lock management
|
|
38
|
+
export { acquireLock, releaseLock } from "./lock";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type { RunOptions, RunResult } from "./runner";
|
|
2
|
+
export { run } from "./runner";
|
|
3
|
+
export type { FailureCategory } from "../tdd/types";
|
|
4
|
+
export { appendProgress } from "./progress";
|
|
5
|
+
export { buildSingleSessionPrompt, buildBatchPrompt } from "./prompts";
|
|
6
|
+
export { groupStoriesIntoBatches, type StoryBatch } from "./batching";
|
|
7
|
+
export { escalateTier, getTierConfig, calculateMaxIterations } from "./escalation";
|
|
8
|
+
export {
|
|
9
|
+
verifyAssets,
|
|
10
|
+
executeWithTimeout,
|
|
11
|
+
parseTestOutput,
|
|
12
|
+
getEnvironmentalEscalationThreshold,
|
|
13
|
+
normalizeEnvironment,
|
|
14
|
+
buildTestCommand,
|
|
15
|
+
appendOpenHandlesFlag,
|
|
16
|
+
appendForceExitFlag,
|
|
17
|
+
runVerification,
|
|
18
|
+
type VerificationResult,
|
|
19
|
+
type VerificationStatus,
|
|
20
|
+
type TestOutputAnalysis,
|
|
21
|
+
type AssetVerificationResult,
|
|
22
|
+
type TimeoutExecutionResult,
|
|
23
|
+
} from "./verification";
|
|
24
|
+
export { runPostAgentVerification, type PostVerifyOptions, type PostVerifyResult } from "./post-verify";
|
|
25
|
+
export { readQueueFile, clearQueueFile } from "./queue-handler";
|
|
26
|
+
export {
|
|
27
|
+
hookCtx,
|
|
28
|
+
maybeGetContext,
|
|
29
|
+
buildStoryContext,
|
|
30
|
+
getAllReadyStories,
|
|
31
|
+
acquireLock,
|
|
32
|
+
releaseLock,
|
|
33
|
+
formatProgress,
|
|
34
|
+
type ExecutionResult,
|
|
35
|
+
type StoryCounts,
|
|
36
|
+
} from "./helpers";
|
|
37
|
+
export {
|
|
38
|
+
installCrashHandlers,
|
|
39
|
+
startHeartbeat,
|
|
40
|
+
stopHeartbeat,
|
|
41
|
+
writeExitSummary,
|
|
42
|
+
resetCrashHandlers,
|
|
43
|
+
type CrashRecoveryContext,
|
|
44
|
+
} from "./crash-recovery";
|
|
45
|
+
export { PidRegistry } from "./pid-registry";
|