@nathapp/nax 0.27.1 → 0.29.0
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/CHANGELOG.md +13 -2
- package/dist/nax.js +72691 -0
- package/package.json +12 -4
- package/src/cli/config.ts +42 -1
- package/src/cli/prompts.ts +18 -6
- package/src/config/defaults.ts +2 -0
- package/src/config/schemas.ts +11 -0
- package/src/config/types.ts +8 -0
- package/src/context/builder.ts +10 -1
- package/src/pipeline/stages/execution.ts +5 -0
- package/src/pipeline/stages/prompt.ts +13 -4
- package/src/precheck/checks-warnings.ts +37 -0
- package/src/precheck/checks.ts +1 -0
- package/src/precheck/index.ts +14 -7
- package/src/prompts/builder.ts +178 -0
- package/src/prompts/index.ts +2 -0
- package/src/prompts/loader.ts +43 -0
- package/src/prompts/sections/conventions.ts +15 -0
- package/src/prompts/sections/index.ts +11 -0
- package/src/prompts/sections/isolation.ts +24 -0
- package/src/prompts/sections/role-task.ts +34 -0
- package/src/prompts/sections/story.ts +13 -0
- package/src/prompts/sections/verdict.ts +70 -0
- package/src/prompts/templates/implementer.ts +6 -0
- package/src/prompts/templates/single-session.ts +6 -0
- package/src/prompts/templates/test-writer.ts +6 -0
- package/src/prompts/templates/verifier.ts +6 -0
- package/src/prompts/types.ts +21 -0
- package/src/review/runner.ts +6 -1
- package/src/tdd/session-runner.ts +12 -12
- package/src/version.ts +2 -1
- package/.claude/rules/01-project-conventions.md +0 -34
- package/.claude/rules/02-test-architecture.md +0 -39
- package/.claude/rules/03-test-writing.md +0 -58
- package/.claude/rules/04-forbidden-patterns.md +0 -29
- package/.claude/settings.json +0 -15
- package/.githooks/pre-commit +0 -16
- package/.gitlab-ci.yml +0 -103
- package/.mcp.json +0 -8
- package/BRIEF.md +0 -140
- package/CLAUDE.md +0 -143
- package/US-007-IMPLEMENTATION.md +0 -139
- package/biome.json +0 -14
- package/bun.lock +0 -163
- package/bunfig.toml +0 -12
- package/docker-compose.test.yml +0 -15
- package/docs/20260216-fix-plan-context-review.md +0 -56
- package/docs/20260216-relentless-vs-ngent-comparison.md +0 -208
- package/docs/20260216-v02-plan.md +0 -136
- package/docs/20260216-v02-review.md +0 -685
- package/docs/20260217-dogfood-findings.md +0 -56
- package/docs/20260217-p2-plus-plan.md +0 -117
- package/docs/20260217-partial-fixes-plan.md +0 -62
- package/docs/20260217-plan-analyze-spec.md +0 -117
- package/docs/20260217-post-impl-review.md +0 -1137
- package/docs/20260217-quick-wins-plan.md +0 -66
- package/docs/20260217-split-runner-plan.md +0 -75
- package/docs/20260217-v03-impl-plan.md +0 -80
- package/docs/20260217-v03-post-impl-review.md +0 -589
- package/docs/20260217-v04-impl-plan.md +0 -86
- package/docs/20260217-v05-post-impl-review.md +0 -850
- package/docs/20260217-v06-post-impl-review.md +0 -817
- package/docs/20260218-adr003-port-plan.md +0 -151
- package/docs/20260218-review-adr003-verification.md +0 -175
- package/docs/20260219-fix-plan-bug16-19.md +0 -79
- package/docs/20260219-fix-plan-bug20-22.md +0 -114
- package/docs/20260219-plan-llm-routing.md +0 -116
- package/docs/20260219-review-bug20-22-fixes.md +0 -135
- package/docs/20260219-routing-baseline-keyword.md +0 -63
- package/docs/20260220-plan-structured-logging-p1.md +0 -80
- package/docs/20260220-plan-structured-logging-p2.md +0 -37
- package/docs/20260220-review-llm-routing.md +0 -180
- package/docs/20260220-review-post-fix-llm-routing.md +0 -70
- package/docs/20260221-fix-plan-relevantfiles-split.md +0 -101
- package/docs/20260221-fix-plan-routing-mode.md +0 -125
- package/docs/20260221-review-v0.9-implementation.md +0 -379
- package/docs/20260222-fix-plan-v091-routing-isolation.md +0 -197
- package/docs/20260223-fix-plan-prompt-audit.md +0 -62
- package/docs/20260224-nax-roadmap-phases.md +0 -189
- package/docs/20260225-phase2-llm-service-layer.md +0 -401
- package/docs/20260225-review-v0.10.1.md +0 -187
- package/docs/20260303-v010-implementation-plan.md +0 -165
- package/docs/20260304-review-nax.md +0 -492
- package/docs/CLAUDE.md.bak +0 -191
- package/docs/ROADMAP.md +0 -364
- package/docs/SPEC-rectification.md +0 -0
- package/docs/SPEC.md +0 -324
- package/docs/US-001-plugin-loading-verification.md +0 -152
- package/docs/adr/ADR-005-implementation-plan.md +0 -655
- package/docs/adr/ADR-005-pipeline-re-architecture.md +0 -464
- package/docs/architecture-analysis.md +0 -1076
- package/docs/bugs/BUG-21-escalation-null-attempts.md +0 -48
- package/docs/bugs-from-dogfood-run-c.md +0 -243
- package/docs/code-review-20260228.md +0 -612
- package/docs/code-review-v0.15.0.md +0 -629
- package/docs/hook-lifecycle-test-plan.md +0 -149
- package/docs/releases/v0.11.0-and-earlier.md +0 -20
- package/docs/releases/v0.12.0.md +0 -15
- package/docs/releases/v0.13.0.md +0 -14
- package/docs/releases/v0.14.0.md +0 -20
- package/docs/releases/v0.14.1.md +0 -36
- package/docs/releases/v0.14.2.md +0 -51
- package/docs/releases/v0.14.3.md +0 -174
- package/docs/releases/v0.14.4.md +0 -94
- package/docs/releases/v0.15.0.md +0 -502
- package/docs/releases/v0.15.1.md +0 -170
- package/docs/releases/v0.15.3.md +0 -193
- package/docs/specs/bug-039-orphan-processes.md +0 -131
- package/docs/specs/bug-040-review-rectification.md +0 -82
- package/docs/specs/bug-041-cross-story-test-isolation.md +0 -88
- package/docs/specs/bug-042-verifier-failure-capture.md +0 -117
- package/docs/specs/bun-pty-migration.md +0 -171
- package/docs/specs/central-run-registry.md +0 -116
- package/docs/specs/feat-010-smart-runner-git-history.md +0 -96
- package/docs/specs/feat-011-file-context-strategy.md +0 -73
- package/docs/specs/feat-012-tdd-writer-tier.md +0 -79
- package/docs/specs/feat-013-test-after-review.md +0 -89
- package/docs/specs/feat-014-heartbeat-observability.md +0 -127
- package/docs/specs/status-file-consolidation.md +0 -93
- package/docs/specs/status-file-v0.10.1.md +0 -812
- package/docs/specs/trigger-completion.md +0 -145
- package/docs/specs/verification-architecture-v2.md +0 -343
- package/docs/tdd/strategies.md +0 -97
- package/docs/v0.10-global-config.md +0 -206
- package/docs/v0.10-plugin-system.md +0 -415
- package/docs/v0.10-prompt-optimizer.md +0 -234
- package/docs/v0.3-spec.md +0 -244
- package/docs/v0.4-spec.md +0 -140
- package/docs/v0.5-spec.md +0 -237
- package/docs/v0.6-spec.md +0 -371
- package/docs/v0.7-spec.md +0 -177
- package/docs/v0.8-llm-routing.md +0 -206
- package/docs/v0.8-structured-logging.md +0 -132
- package/docs/v0.9.3-prompt-audit.md +0 -112
- package/examples/plugins/console-reporter/index.test.ts +0 -207
- package/examples/plugins/console-reporter/index.ts +0 -110
- package/memory/topic/feat-010-baseref.md +0 -28
- package/memory/topic/feat-013-test-after-deprecation.md +0 -22
- package/nax/config.json +0 -154
- package/nax/features/bug-039-medium/prd.json +0 -45
- package/nax/features/bugfix-v0171/prd.json +0 -52
- package/nax/features/central-run-registry/prd.json +0 -105
- package/nax/features/config-management/prd.json +0 -108
- package/nax/features/config-management/progress.txt +0 -5
- package/nax/features/diagnose/acceptance.test.ts +0 -414
- package/nax/features/diagnose/prd.json +0 -41
- package/nax/features/nax-compliance/prd.json +0 -52
- package/nax/features/nax-compliance/progress.txt +0 -1
- package/nax/features/orchestration-fixes/prd.json +0 -89
- package/nax/features/orchestration-fixes/progress.txt +0 -1
- package/nax/features/plugin-integration/US-007-VERIFICATION.md +0 -259
- package/nax/features/plugin-integration/prd.json +0 -208
- package/nax/features/plugin-integration/progress.txt +0 -5
- package/nax/features/post-rearch-bugfix/prd.json +0 -137
- package/nax/features/precheck/prd.json +0 -205
- package/nax/features/precheck/progress.txt +0 -15
- package/nax/features/review-quality/prd.json +0 -55
- package/nax/features/routing-persistence/prd.json +0 -104
- package/nax/features/routing-persistence/progress.txt +0 -1
- package/nax/features/smart-test-runner/plan.md +0 -7
- package/nax/features/smart-test-runner/prd.json +0 -203
- package/nax/features/smart-test-runner/progress.txt +0 -13
- package/nax/features/smart-test-runner/spec.md +0 -7
- package/nax/features/smart-test-runner/tasks.md +0 -8
- package/nax/features/status-file-consolidation/prd.json +0 -106
- package/nax/features/structured-logging/prd.json +0 -199
- package/nax/features/trigger-completion/prd.json +0 -150
- package/nax/features/trigger-completion/progress.txt +0 -7
- package/nax/features/unlock/prd.json +0 -36
- package/nax/features/v0.18.3-execution-reliability/prd.json +0 -80
- package/nax/features/v0.18.3-execution-reliability/progress.txt +0 -3
- package/nax/features/v0.19.0-hardening/plan.md +0 -7
- package/nax/features/v0.19.0-hardening/prd.json +0 -84
- package/nax/features/v0.19.0-hardening/progress.txt +0 -7
- package/nax/features/v0.19.0-hardening/spec.md +0 -18
- package/nax/features/v0.19.0-hardening/tasks.md +0 -8
- package/nax/features/verify-v2/prd.json +0 -79
- package/nax/features/verify-v2/progress.txt +0 -3
- package/nax/status.json +0 -36
- package/test/COVERAGE-GAPS.md +0 -333
- package/test/e2e/cm-003-default-view.test.ts +0 -195
- package/test/e2e/plan-analyze-run.test.ts +0 -902
- package/test/helpers/helpers.test.ts +0 -295
- package/test/helpers/timeout.ts +0 -42
- package/test/integration/US-002-TEST-SUMMARY.md +0 -107
- package/test/integration/US-003-TEST-SUMMARY.md +0 -149
- package/test/integration/US-004-TEST-SUMMARY.md +0 -106
- package/test/integration/US-005-TEST-SUMMARY.md +0 -138
- package/test/integration/US-007-TEST-SUMMARY.md +0 -100
- package/test/integration/cli/agent-validation.test.ts +0 -439
- package/test/integration/cli/cli-config-default-edge-cases.test.ts +0 -223
- package/test/integration/cli/cli-config-default-view.test.ts +0 -230
- package/test/integration/cli/cli-config-diff.test.ts +0 -461
- package/test/integration/cli/cli-config.test.ts +0 -737
- package/test/integration/cli/cli-diagnose.test.ts +0 -595
- package/test/integration/cli/cli-logs.test.ts +0 -346
- package/test/integration/cli/cli-plugins.test.ts +0 -679
- package/test/integration/cli/cli-precheck.test.ts +0 -372
- package/test/integration/cli/cli-run-headless.test.ts +0 -174
- package/test/integration/cli/cli.test.ts +0 -76
- package/test/integration/cli/precheck-integration.test.ts +0 -476
- package/test/integration/cli/precheck-orchestrator.test.ts +0 -247
- package/test/integration/cli/precheck.test.ts +0 -806
- package/test/integration/config/config-loader.test.ts +0 -266
- package/test/integration/config/config.test.ts +0 -444
- package/test/integration/config/merger.test.ts +0 -466
- package/test/integration/config/paths.test.ts +0 -52
- package/test/integration/config/security-loader.test.ts +0 -83
- package/test/integration/context/context-integration.test.ts +0 -703
- package/test/integration/context/context-path-security.test.ts +0 -173
- package/test/integration/context/context-provider-injection.test.ts +0 -507
- package/test/integration/context/context-verification-integration.test.ts +0 -296
- package/test/integration/context/s5-greenfield-fallback.test.ts +0 -298
- package/test/integration/execution/execution-isolation.test.ts +0 -143
- package/test/integration/execution/execution.test.ts +0 -634
- package/test/integration/execution/feature-status-write.test.ts +0 -302
- package/test/integration/execution/parallel.test.ts +0 -251
- package/test/integration/execution/prd-pause.test.ts +0 -205
- package/test/integration/execution/prd-resolvers.test.ts +0 -186
- package/test/integration/execution/progress.test.ts +0 -34
- package/test/integration/execution/runner-batching.test.ts +0 -682
- package/test/integration/execution/runner-config-plugins.test.ts +0 -462
- package/test/integration/execution/runner-escalation.test.ts +0 -561
- package/test/integration/execution/runner-fixes.test.ts +0 -400
- package/test/integration/execution/runner-plugin-integration.test.ts +0 -544
- package/test/integration/execution/runner-queue-and-attempts.test.ts +0 -476
- package/test/integration/execution/status-file-integration.test.ts +0 -289
- package/test/integration/execution/status-file.test.ts +0 -380
- package/test/integration/execution/status-writer.test.ts +0 -447
- package/test/integration/execution/story-id-in-events.test.ts +0 -274
- package/test/integration/interaction/interaction-chain-pipeline.test.ts +0 -476
- package/test/integration/pipeline/hooks.test.ts +0 -363
- package/test/integration/pipeline/pipeline-acceptance.test.ts +0 -303
- package/test/integration/pipeline/pipeline-events.test.ts +0 -476
- package/test/integration/pipeline/pipeline.test.ts +0 -660
- package/test/integration/pipeline/reporter-lifecycle.test.ts +0 -862
- package/test/integration/pipeline/verify-stage.test.ts +0 -286
- package/test/integration/plan/analyze-integration.test.ts +0 -262
- package/test/integration/plan/analyze-scanner.test.ts +0 -132
- package/test/integration/plan/logger.test.ts +0 -461
- package/test/integration/plan/plan.test.ts +0 -157
- package/test/integration/plugins/config-integration.test.ts +0 -173
- package/test/integration/plugins/config-resolution.test.ts +0 -523
- package/test/integration/plugins/loader.test.ts +0 -644
- package/test/integration/plugins/plugins-registry.test.ts +0 -747
- package/test/integration/plugins/validator.test.ts +0 -564
- package/test/integration/review/review-config-commands.test.ts +0 -320
- package/test/integration/review/review-config-schema.test.ts +0 -117
- package/test/integration/review/review-plugin-integration.test.ts +0 -729
- package/test/integration/review/review.test.ts +0 -150
- package/test/integration/routing/plugin-routing-advanced.test.ts +0 -461
- package/test/integration/routing/plugin-routing-core.test.ts +0 -527
- package/test/integration/routing/routing-stage-bug-021.test.ts +0 -275
- package/test/integration/routing/routing-stage-greenfield.test.ts +0 -287
- package/test/integration/tdd/tdd-cleanup.test.ts +0 -246
- package/test/integration/tdd/tdd-orchestrator-core.test.ts +0 -565
- package/test/integration/tdd/tdd-orchestrator-failureCategory.test.ts +0 -355
- package/test/integration/tdd/tdd-orchestrator-fallback.test.ts +0 -311
- package/test/integration/tdd/tdd-orchestrator-lite.test.ts +0 -289
- package/test/integration/tdd/tdd-orchestrator-prompts.test.ts +0 -260
- package/test/integration/tdd/tdd-orchestrator-verdict.test.ts +0 -536
- package/test/integration/tmp/headless-test/test.jsonl +0 -30
- package/test/integration/verification/test-scanner.test.ts +0 -403
- package/test/integration/verification/verification-asset-check.test.ts +0 -143
- package/test/integration/worktree/manager.test.ts +0 -218
- package/test/integration/worktree/worktree-merge.test.ts +0 -341
- package/test/manual/logging-formatter-demo.ts +0 -158
- package/test/ui/tui-agent-panel.test.tsx +0 -99
- package/test/ui/tui-pty-integration.test.tsx +0 -146
- package/test/unit/acceptance.test.ts +0 -187
- package/test/unit/agent-stderr-capture.test.ts +0 -147
- package/test/unit/agents/claude.test.ts +0 -107
- package/test/unit/analyze-classifier.test.ts +0 -216
- package/test/unit/analyze.test.ts +0 -224
- package/test/unit/auto-detect.test.ts +0 -250
- package/test/unit/cli-status-project-level.test.ts +0 -283
- package/test/unit/cli-status.test.ts +0 -418
- package/test/unit/commands/common.test.ts +0 -321
- package/test/unit/commands/logs.test.ts +0 -458
- package/test/unit/commands/runs.test.ts +0 -303
- package/test/unit/commands/unlock.test.ts +0 -320
- package/test/unit/config/defaults.test.ts +0 -70
- package/test/unit/config/quality-commands-schema.test.ts +0 -72
- package/test/unit/config/regression-gate-schema.test.ts +0 -160
- package/test/unit/config/smart-runner-flag.test.ts +0 -250
- package/test/unit/constitution-generators.test.ts +0 -161
- package/test/unit/constitution.test.ts +0 -210
- package/test/unit/context/context-autodetect.test.ts +0 -297
- package/test/unit/context/context-build.test.ts +0 -575
- package/test/unit/context/context-coverage.test.ts +0 -236
- package/test/unit/context/context-error.test.ts +0 -93
- package/test/unit/context/context-estimate-tokens.test.ts +0 -201
- package/test/unit/context/context-format.test.ts +0 -302
- package/test/unit/context/context-isolation.test.ts +0 -267
- package/test/unit/context/context-sort.test.ts +0 -93
- package/test/unit/context/context-story.test.ts +0 -108
- package/test/unit/context/prior-failures.test.ts +0 -463
- package/test/unit/context.test.ts +0 -1726
- package/test/unit/cost.test.ts +0 -231
- package/test/unit/crash-recovery.test.ts +0 -309
- package/test/unit/escalation.test.ts +0 -127
- package/test/unit/execution/lifecycle/run-completion.test.ts +0 -240
- package/test/unit/execution/lifecycle/run-regression.test.ts +0 -420
- package/test/unit/execution/pid-registry.test.ts +0 -241
- package/test/unit/execution/sequential-executor.test.ts +0 -235
- package/test/unit/execution/sfc-004-dead-code-cleanup.test.ts +0 -89
- package/test/unit/execution/structured-failure.test.ts +0 -415
- package/test/unit/execution-logging-stderr.test.ts +0 -157
- package/test/unit/execution-stage.test.ts +0 -123
- package/test/unit/fix-generator.test.ts +0 -276
- package/test/unit/formatters.test.ts +0 -468
- package/test/unit/greenfield.test.ts +0 -180
- package/test/unit/hooks/shell-security.test.ts +0 -40
- package/test/unit/interaction/auto-plugin.test.ts +0 -162
- package/test/unit/interaction/human-review-trigger.test.ts +0 -165
- package/test/unit/interaction-network-failures.test.ts +0 -390
- package/test/unit/interaction-plugins.test.ts +0 -472
- package/test/unit/logging/formatter.test.ts +0 -456
- package/test/unit/merge.test.ts +0 -269
- package/test/unit/metrics/aggregator.test.ts +0 -164
- package/test/unit/metrics/tracker.test.ts +0 -186
- package/test/unit/metrics.test.ts +0 -276
- package/test/unit/optimizer/noop.optimizer.test.ts +0 -125
- package/test/unit/optimizer/rule-based.optimizer.test.ts +0 -358
- package/test/unit/pipeline/event-bus.test.ts +0 -105
- package/test/unit/pipeline/routing-partial-override.test.ts +0 -121
- package/test/unit/pipeline/runner-retry.test.ts +0 -89
- package/test/unit/pipeline/stages/autofix.test.ts +0 -97
- package/test/unit/pipeline/stages/completion-review-gate.test.ts +0 -218
- package/test/unit/pipeline/stages/execution-ambiguity.test.ts +0 -311
- package/test/unit/pipeline/stages/execution-merge-conflict.test.ts +0 -218
- package/test/unit/pipeline/stages/rectify.test.ts +0 -101
- package/test/unit/pipeline/stages/regression-stage.test.ts +0 -69
- package/test/unit/pipeline/stages/review.test.ts +0 -201
- package/test/unit/pipeline/stages/routing-idempotence.test.ts +0 -139
- package/test/unit/pipeline/stages/routing-initial-complexity.test.ts +0 -321
- package/test/unit/pipeline/stages/routing-persistence.test.ts +0 -380
- package/test/unit/pipeline/stages/verify.test.ts +0 -267
- package/test/unit/pipeline/subscribers/events-writer.test.ts +0 -227
- package/test/unit/pipeline/subscribers/hooks.test.ts +0 -84
- package/test/unit/pipeline/subscribers/interaction.test.ts +0 -313
- package/test/unit/pipeline/subscribers/registry.test.ts +0 -149
- package/test/unit/pipeline/subscribers/reporters.test.ts +0 -90
- package/test/unit/pipeline/verify-smart-runner.test.ts +0 -345
- package/test/unit/prd-auto-default.test.ts +0 -291
- package/test/unit/prd-failure-category.test.ts +0 -177
- package/test/unit/prd-get-next-story.test.ts +0 -215
- package/test/unit/precheck-checks.test.ts +0 -841
- package/test/unit/precheck-story-size-gate.test.ts +0 -288
- package/test/unit/precheck-types.test.ts +0 -143
- package/test/unit/prompts.test.ts +0 -476
- package/test/unit/queue.test.ts +0 -237
- package/test/unit/rectification.test.ts +0 -285
- package/test/unit/registry.test.ts +0 -288
- package/test/unit/review/runner.test.ts +0 -117
- package/test/unit/routing/content-hash.test.ts +0 -99
- package/test/unit/routing/routing-stability.test.ts +0 -208
- package/test/unit/routing/strategies/llm.test.ts +0 -306
- package/test/unit/routing-advanced.test.ts +0 -313
- package/test/unit/routing-core.test.ts +0 -341
- package/test/unit/routing-strategies.test.ts +0 -440
- package/test/unit/storyid-events.test.ts +0 -213
- package/test/unit/tdd-verdict.test.ts +0 -492
- package/test/unit/test-output-parser.test.ts +0 -377
- package/test/unit/ui/tui-controls.test.ts +0 -335
- package/test/unit/ui/tui-cost-and-pty.test.ts +0 -190
- package/test/unit/ui/tui-layout.test.ts +0 -379
- package/test/unit/ui/tui-stories.test.ts +0 -333
- package/test/unit/unit-isolation.test.ts +0 -135
- package/test/unit/utils/git.test.ts +0 -50
- package/test/unit/utils/path-security.test.ts +0 -47
- package/test/unit/utils-helpers.test.ts +0 -318
- package/test/unit/verdict.test.ts +0 -325
- package/test/unit/verification/orchestrator-types.test.ts +0 -54
- package/test/unit/verification/orchestrator.test.ts +0 -66
- package/test/unit/verification/smart-runner-config.test.ts +0 -163
- package/test/unit/verification/smart-runner-discovery.test.ts +0 -354
- package/test/unit/verification/smart-runner.test.ts +0 -262
- package/test/unit/verification/strategies/acceptance.test.ts +0 -33
- package/test/unit/verification/strategies/regression.test.ts +0 -87
- package/test/unit/verification/strategies/scoped.test.ts +0 -100
- package/test/unit/worktree-manager.test.ts +0 -159
- package/tsconfig.json +0 -27
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
// RE-ARCH: keep
|
|
2
|
-
/**
|
|
3
|
-
* PRD Auto-Default Tests (US-006 / BUG-004)
|
|
4
|
-
*
|
|
5
|
-
* Tests for PRD loader auto-defaulting and router defensive fallbacks.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
9
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
10
|
-
import { tmpdir } from "node:os";
|
|
11
|
-
import { join } from "node:path";
|
|
12
|
-
import { DEFAULT_CONFIG } from "../../src/config";
|
|
13
|
-
import { loadPRD, savePRD } from "../../src/prd";
|
|
14
|
-
import type { PRD } from "../../src/prd/types";
|
|
15
|
-
import { routeTask } from "../../src/routing";
|
|
16
|
-
|
|
17
|
-
describe("PRD Auto-Default (BUG-004)", () => {
|
|
18
|
-
let testDir: string;
|
|
19
|
-
let prdPath: string;
|
|
20
|
-
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
testDir = mkdtempSync(join(tmpdir(), "nax-test-prd-"));
|
|
23
|
-
prdPath = join(testDir, "test-prd.json");
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
afterEach(() => {
|
|
27
|
-
rmSync(testDir, { recursive: true, force: true });
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test("loadPRD auto-defaults missing tags to []", async () => {
|
|
31
|
-
const prd: PRD = {
|
|
32
|
-
project: "test-project",
|
|
33
|
-
feature: "test-feature",
|
|
34
|
-
branchName: "test-branch",
|
|
35
|
-
createdAt: new Date().toISOString(),
|
|
36
|
-
updatedAt: new Date().toISOString(),
|
|
37
|
-
userStories: [
|
|
38
|
-
{
|
|
39
|
-
id: "US-001",
|
|
40
|
-
title: "Test story",
|
|
41
|
-
description: "Test description",
|
|
42
|
-
acceptanceCriteria: ["AC1"],
|
|
43
|
-
// tags intentionally omitted
|
|
44
|
-
dependencies: [],
|
|
45
|
-
status: "pending",
|
|
46
|
-
passes: false,
|
|
47
|
-
escalations: [],
|
|
48
|
-
attempts: 0,
|
|
49
|
-
} as any, // Cast to bypass type check for missing field
|
|
50
|
-
],
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
await savePRD(prd, prdPath);
|
|
54
|
-
const loaded = await loadPRD(prdPath);
|
|
55
|
-
|
|
56
|
-
expect(loaded.userStories[0].tags).toEqual([]);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("loadPRD auto-defaults missing status to pending", async () => {
|
|
60
|
-
const prd: PRD = {
|
|
61
|
-
project: "test-project",
|
|
62
|
-
feature: "test-feature",
|
|
63
|
-
branchName: "test-branch",
|
|
64
|
-
createdAt: new Date().toISOString(),
|
|
65
|
-
updatedAt: new Date().toISOString(),
|
|
66
|
-
userStories: [
|
|
67
|
-
{
|
|
68
|
-
id: "US-001",
|
|
69
|
-
title: "Test story",
|
|
70
|
-
description: "Test description",
|
|
71
|
-
acceptanceCriteria: ["AC1"],
|
|
72
|
-
tags: [],
|
|
73
|
-
dependencies: [],
|
|
74
|
-
// status intentionally omitted
|
|
75
|
-
passes: false,
|
|
76
|
-
escalations: [],
|
|
77
|
-
attempts: 0,
|
|
78
|
-
} as any,
|
|
79
|
-
],
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
await savePRD(prd, prdPath);
|
|
83
|
-
const loaded = await loadPRD(prdPath);
|
|
84
|
-
|
|
85
|
-
expect(loaded.userStories[0].status).toBe("pending");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test("loadPRD auto-defaults missing acceptanceCriteria to []", async () => {
|
|
89
|
-
const prd: PRD = {
|
|
90
|
-
project: "test-project",
|
|
91
|
-
feature: "test-feature",
|
|
92
|
-
branchName: "test-branch",
|
|
93
|
-
createdAt: new Date().toISOString(),
|
|
94
|
-
updatedAt: new Date().toISOString(),
|
|
95
|
-
userStories: [
|
|
96
|
-
{
|
|
97
|
-
id: "US-001",
|
|
98
|
-
title: "Test story",
|
|
99
|
-
description: "Test description",
|
|
100
|
-
// acceptanceCriteria intentionally omitted
|
|
101
|
-
tags: [],
|
|
102
|
-
dependencies: [],
|
|
103
|
-
status: "pending",
|
|
104
|
-
passes: false,
|
|
105
|
-
escalations: [],
|
|
106
|
-
attempts: 0,
|
|
107
|
-
} as any,
|
|
108
|
-
],
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
await savePRD(prd, prdPath);
|
|
112
|
-
const loaded = await loadPRD(prdPath);
|
|
113
|
-
|
|
114
|
-
expect(loaded.userStories[0].acceptanceCriteria).toEqual([]);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test("loadPRD auto-defaults missing storyPoints to 1", async () => {
|
|
118
|
-
const prd: PRD = {
|
|
119
|
-
project: "test-project",
|
|
120
|
-
feature: "test-feature",
|
|
121
|
-
branchName: "test-branch",
|
|
122
|
-
createdAt: new Date().toISOString(),
|
|
123
|
-
updatedAt: new Date().toISOString(),
|
|
124
|
-
userStories: [
|
|
125
|
-
{
|
|
126
|
-
id: "US-001",
|
|
127
|
-
title: "Test story",
|
|
128
|
-
description: "Test description",
|
|
129
|
-
acceptanceCriteria: ["AC1"],
|
|
130
|
-
tags: [],
|
|
131
|
-
dependencies: [],
|
|
132
|
-
status: "pending",
|
|
133
|
-
passes: false,
|
|
134
|
-
escalations: [],
|
|
135
|
-
attempts: 0,
|
|
136
|
-
// storyPoints intentionally omitted
|
|
137
|
-
} as any,
|
|
138
|
-
],
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
await savePRD(prd, prdPath);
|
|
142
|
-
const loaded = await loadPRD(prdPath);
|
|
143
|
-
|
|
144
|
-
expect(loaded.userStories[0].storyPoints).toBe(1);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test("loadPRD preserves existing values", async () => {
|
|
148
|
-
const prd: PRD = {
|
|
149
|
-
project: "test-project",
|
|
150
|
-
feature: "test-feature",
|
|
151
|
-
branchName: "test-branch",
|
|
152
|
-
createdAt: new Date().toISOString(),
|
|
153
|
-
updatedAt: new Date().toISOString(),
|
|
154
|
-
userStories: [
|
|
155
|
-
{
|
|
156
|
-
id: "US-001",
|
|
157
|
-
title: "Test story",
|
|
158
|
-
description: "Test description",
|
|
159
|
-
acceptanceCriteria: ["AC1", "AC2"],
|
|
160
|
-
tags: ["security", "auth"],
|
|
161
|
-
dependencies: ["US-000"],
|
|
162
|
-
status: "in-progress",
|
|
163
|
-
passes: false,
|
|
164
|
-
escalations: [],
|
|
165
|
-
attempts: 2,
|
|
166
|
-
storyPoints: 5,
|
|
167
|
-
},
|
|
168
|
-
],
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
await savePRD(prd, prdPath);
|
|
172
|
-
const loaded = await loadPRD(prdPath);
|
|
173
|
-
|
|
174
|
-
expect(loaded.userStories[0].acceptanceCriteria).toEqual(["AC1", "AC2"]);
|
|
175
|
-
expect(loaded.userStories[0].tags).toEqual(["security", "auth"]);
|
|
176
|
-
expect(loaded.userStories[0].dependencies).toEqual(["US-000"]);
|
|
177
|
-
expect(loaded.userStories[0].status).toBe("in-progress");
|
|
178
|
-
expect(loaded.userStories[0].attempts).toBe(2);
|
|
179
|
-
expect(loaded.userStories[0].storyPoints).toBe(5);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test("loadPRD does not modify PRD file on disk", async () => {
|
|
183
|
-
const prd: PRD = {
|
|
184
|
-
project: "test-project",
|
|
185
|
-
feature: "test-feature",
|
|
186
|
-
branchName: "test-branch",
|
|
187
|
-
createdAt: new Date().toISOString(),
|
|
188
|
-
updatedAt: new Date().toISOString(),
|
|
189
|
-
userStories: [
|
|
190
|
-
{
|
|
191
|
-
id: "US-001",
|
|
192
|
-
title: "Test story",
|
|
193
|
-
description: "Test description",
|
|
194
|
-
acceptanceCriteria: ["AC1"],
|
|
195
|
-
// tags intentionally omitted
|
|
196
|
-
dependencies: [],
|
|
197
|
-
status: "pending",
|
|
198
|
-
passes: false,
|
|
199
|
-
escalations: [],
|
|
200
|
-
attempts: 0,
|
|
201
|
-
} as any,
|
|
202
|
-
],
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
await savePRD(prd, prdPath);
|
|
206
|
-
const originalContent = await Bun.file(prdPath).text();
|
|
207
|
-
|
|
208
|
-
// Load the PRD (which will auto-default in-memory)
|
|
209
|
-
await loadPRD(prdPath);
|
|
210
|
-
|
|
211
|
-
// Verify file content unchanged
|
|
212
|
-
const afterLoadContent = await Bun.file(prdPath).text();
|
|
213
|
-
expect(afterLoadContent).toBe(originalContent);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
test("loadPRD handles all missing fields simultaneously", async () => {
|
|
217
|
-
const prd: PRD = {
|
|
218
|
-
project: "test-project",
|
|
219
|
-
feature: "test-feature",
|
|
220
|
-
branchName: "test-branch",
|
|
221
|
-
createdAt: new Date().toISOString(),
|
|
222
|
-
updatedAt: new Date().toISOString(),
|
|
223
|
-
userStories: [
|
|
224
|
-
{
|
|
225
|
-
id: "US-001",
|
|
226
|
-
title: "Test story",
|
|
227
|
-
description: "Test description",
|
|
228
|
-
// All optional fields omitted
|
|
229
|
-
passes: false,
|
|
230
|
-
} as any,
|
|
231
|
-
],
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
await savePRD(prd, prdPath);
|
|
235
|
-
const loaded = await loadPRD(prdPath);
|
|
236
|
-
|
|
237
|
-
expect(loaded.userStories[0].tags).toEqual([]);
|
|
238
|
-
expect(loaded.userStories[0].status).toBe("pending");
|
|
239
|
-
expect(loaded.userStories[0].acceptanceCriteria).toEqual([]);
|
|
240
|
-
expect(loaded.userStories[0].dependencies).toEqual([]);
|
|
241
|
-
expect(loaded.userStories[0].attempts).toBe(0);
|
|
242
|
-
expect(loaded.userStories[0].priorErrors).toEqual([]);
|
|
243
|
-
expect(loaded.userStories[0].escalations).toEqual([]);
|
|
244
|
-
expect(loaded.userStories[0].storyPoints).toBe(1);
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
describe("Router Tags Defensive Fallback (BUG-004)", () => {
|
|
249
|
-
test("routeTask handles undefined tags gracefully", () => {
|
|
250
|
-
const result = routeTask(
|
|
251
|
-
"Fix typo",
|
|
252
|
-
"Fix a typo in README",
|
|
253
|
-
["Typo fixed"],
|
|
254
|
-
undefined as any, // Simulate missing tags
|
|
255
|
-
DEFAULT_CONFIG,
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
expect(result.complexity).toBe("simple");
|
|
259
|
-
expect(result.modelTier).toBe("fast");
|
|
260
|
-
expect(result.testStrategy).toBe("test-after");
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
test("routeTask handles null tags gracefully", () => {
|
|
264
|
-
const result = routeTask(
|
|
265
|
-
"Fix typo",
|
|
266
|
-
"Fix a typo in README",
|
|
267
|
-
["Typo fixed"],
|
|
268
|
-
null as any, // Simulate null tags
|
|
269
|
-
DEFAULT_CONFIG,
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
expect(result.complexity).toBe("simple");
|
|
273
|
-
expect(result.modelTier).toBe("fast");
|
|
274
|
-
expect(result.testStrategy).toBe("test-after");
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
test("routeTask with undefined tags does not crash on spread operation", () => {
|
|
278
|
-
// This test specifically verifies line ~277 in router.ts doesn't crash
|
|
279
|
-
expect(() => {
|
|
280
|
-
routeTask("Add feature", "Add new feature", ["AC1", "AC2", "AC3"], undefined as any, DEFAULT_CONFIG);
|
|
281
|
-
}).not.toThrow();
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
test("routeTask preserves existing tags behavior", () => {
|
|
285
|
-
const result = routeTask("Auth fix", "Fix JWT auth bypass", ["Auth works"], ["security", "auth"], DEFAULT_CONFIG);
|
|
286
|
-
|
|
287
|
-
expect(result.complexity).toBe("complex");
|
|
288
|
-
expect(result.testStrategy).toBe("three-session-tdd");
|
|
289
|
-
expect(result.reasoning).toContain("security-critical");
|
|
290
|
-
});
|
|
291
|
-
});
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
// RE-ARCH: keep
|
|
2
|
-
/**
|
|
3
|
-
* PRD failureCategory Tests
|
|
4
|
-
*
|
|
5
|
-
* Tests for failureCategory field on UserStory and the markStoryFailed()
|
|
6
|
-
* function storing the category.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { describe, expect, test } from "bun:test";
|
|
10
|
-
import type { FailureCategory } from "../../src/execution";
|
|
11
|
-
import { markStoryFailed, markStoryPassed, markStoryPaused } from "../../src/prd";
|
|
12
|
-
import type { PRD, UserStory } from "../../src/prd/types";
|
|
13
|
-
|
|
14
|
-
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
15
|
-
|
|
16
|
-
function makeStory(id: string): UserStory {
|
|
17
|
-
return {
|
|
18
|
-
id,
|
|
19
|
-
title: `Story ${id}`,
|
|
20
|
-
description: "Test story",
|
|
21
|
-
acceptanceCriteria: ["AC1"],
|
|
22
|
-
tags: [],
|
|
23
|
-
dependencies: [],
|
|
24
|
-
status: "pending",
|
|
25
|
-
passes: false,
|
|
26
|
-
escalations: [],
|
|
27
|
-
attempts: 0,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function makePrd(stories: UserStory[]): PRD {
|
|
32
|
-
return {
|
|
33
|
-
project: "test",
|
|
34
|
-
feature: "test-feature",
|
|
35
|
-
branchName: "feature/test",
|
|
36
|
-
createdAt: new Date().toISOString(),
|
|
37
|
-
updatedAt: new Date().toISOString(),
|
|
38
|
-
userStories: stories,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ── UserStory interface ───────────────────────────────────────────────────────
|
|
43
|
-
|
|
44
|
-
describe("UserStory.failureCategory field", () => {
|
|
45
|
-
test("is optional — not present by default", () => {
|
|
46
|
-
const story = makeStory("US-001");
|
|
47
|
-
expect(story.failureCategory).toBeUndefined();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("can be assigned a FailureCategory value", () => {
|
|
51
|
-
const story = makeStory("US-001");
|
|
52
|
-
story.failureCategory = "session-failure";
|
|
53
|
-
expect(story.failureCategory).toBe("session-failure");
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test("accepts all four FailureCategory values", () => {
|
|
57
|
-
const categories: FailureCategory[] = [
|
|
58
|
-
"isolation-violation",
|
|
59
|
-
"session-failure",
|
|
60
|
-
"tests-failing",
|
|
61
|
-
"verifier-rejected",
|
|
62
|
-
];
|
|
63
|
-
const story = makeStory("US-001");
|
|
64
|
-
for (const cat of categories) {
|
|
65
|
-
story.failureCategory = cat;
|
|
66
|
-
expect(story.failureCategory).toBe(cat);
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// ── markStoryFailed() ─────────────────────────────────────────────────────────
|
|
72
|
-
|
|
73
|
-
describe("markStoryFailed()", () => {
|
|
74
|
-
test("marks story as failed (backward compat — no failureCategory)", () => {
|
|
75
|
-
const prd = makePrd([makeStory("US-001")]);
|
|
76
|
-
markStoryFailed(prd, "US-001");
|
|
77
|
-
expect(prd.userStories[0].status).toBe("failed");
|
|
78
|
-
expect(prd.userStories[0].failureCategory).toBeUndefined();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("increments attempts when no failureCategory given", () => {
|
|
82
|
-
const prd = makePrd([makeStory("US-001")]);
|
|
83
|
-
markStoryFailed(prd, "US-001");
|
|
84
|
-
expect(prd.userStories[0].attempts).toBe(1);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("stores failureCategory='session-failure'", () => {
|
|
88
|
-
const prd = makePrd([makeStory("US-001")]);
|
|
89
|
-
markStoryFailed(prd, "US-001", "session-failure");
|
|
90
|
-
expect(prd.userStories[0].failureCategory).toBe("session-failure");
|
|
91
|
-
expect(prd.userStories[0].status).toBe("failed");
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test("stores failureCategory='isolation-violation'", () => {
|
|
95
|
-
const prd = makePrd([makeStory("US-001")]);
|
|
96
|
-
markStoryFailed(prd, "US-001", "isolation-violation");
|
|
97
|
-
expect(prd.userStories[0].failureCategory).toBe("isolation-violation");
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test("stores failureCategory='tests-failing'", () => {
|
|
101
|
-
const prd = makePrd([makeStory("US-001")]);
|
|
102
|
-
markStoryFailed(prd, "US-001", "tests-failing");
|
|
103
|
-
expect(prd.userStories[0].failureCategory).toBe("tests-failing");
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test("stores failureCategory='verifier-rejected'", () => {
|
|
107
|
-
const prd = makePrd([makeStory("US-001")]);
|
|
108
|
-
markStoryFailed(prd, "US-001", "verifier-rejected");
|
|
109
|
-
expect(prd.userStories[0].failureCategory).toBe("verifier-rejected");
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test("increments attempts when failureCategory is given", () => {
|
|
113
|
-
const prd = makePrd([makeStory("US-001")]);
|
|
114
|
-
prd.userStories[0].attempts = 2;
|
|
115
|
-
markStoryFailed(prd, "US-001", "tests-failing");
|
|
116
|
-
expect(prd.userStories[0].attempts).toBe(3);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
test("does not overwrite failureCategory with undefined when not passed", () => {
|
|
120
|
-
const prd = makePrd([makeStory("US-001")]);
|
|
121
|
-
// Simulate a prior failure that set a category
|
|
122
|
-
prd.userStories[0].failureCategory = "session-failure";
|
|
123
|
-
// Call without a category — should NOT clear the existing value
|
|
124
|
-
markStoryFailed(prd, "US-001");
|
|
125
|
-
expect(prd.userStories[0].failureCategory).toBe("session-failure");
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test("does nothing when story not found", () => {
|
|
129
|
-
const prd = makePrd([makeStory("US-001")]);
|
|
130
|
-
// Should not throw
|
|
131
|
-
markStoryFailed(prd, "US-999", "session-failure");
|
|
132
|
-
expect(prd.userStories[0].status).toBe("pending");
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test("does not affect other stories", () => {
|
|
136
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
137
|
-
markStoryFailed(prd, "US-001", "tests-failing");
|
|
138
|
-
expect(prd.userStories[1].status).toBe("pending");
|
|
139
|
-
expect(prd.userStories[1].failureCategory).toBeUndefined();
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// ── FailureCategory type export from src/execution ───────────────────────────
|
|
144
|
-
|
|
145
|
-
describe("FailureCategory export from src/execution", () => {
|
|
146
|
-
test("FailureCategory is exported from src/execution index", () => {
|
|
147
|
-
// This test verifies the re-export compiles and can be used as a type
|
|
148
|
-
const cat: FailureCategory = "session-failure";
|
|
149
|
-
expect(cat).toBe("session-failure");
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
test("all four categories are valid FailureCategory values", () => {
|
|
153
|
-
const isolation: FailureCategory = "isolation-violation";
|
|
154
|
-
const session: FailureCategory = "session-failure";
|
|
155
|
-
const failing: FailureCategory = "tests-failing";
|
|
156
|
-
const rejected: FailureCategory = "verifier-rejected";
|
|
157
|
-
expect([isolation, session, failing, rejected]).toHaveLength(4);
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// ── markStoryPaused / markStoryPassed unaffected ─────────────────────────────
|
|
162
|
-
|
|
163
|
-
describe("markStoryPaused and markStoryPassed — failureCategory not affected", () => {
|
|
164
|
-
test("markStoryPaused does not set failureCategory", () => {
|
|
165
|
-
const prd = makePrd([makeStory("US-001")]);
|
|
166
|
-
markStoryPaused(prd, "US-001");
|
|
167
|
-
expect(prd.userStories[0].status).toBe("paused");
|
|
168
|
-
expect(prd.userStories[0].failureCategory).toBeUndefined();
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test("markStoryPassed does not set failureCategory", () => {
|
|
172
|
-
const prd = makePrd([makeStory("US-001")]);
|
|
173
|
-
markStoryPassed(prd, "US-001");
|
|
174
|
-
expect(prd.userStories[0].status).toBe("passed");
|
|
175
|
-
expect(prd.userStories[0].failureCategory).toBeUndefined();
|
|
176
|
-
});
|
|
177
|
-
});
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
// RE-ARCH: keep
|
|
2
|
-
/**
|
|
3
|
-
* getNextStory() Unit Tests (BUG-022)
|
|
4
|
-
*
|
|
5
|
-
* Tests for the story retry priority behavior:
|
|
6
|
-
* - Retries current failed story when attempts <= maxRetries
|
|
7
|
-
* - Moves to next pending story when retries exhausted
|
|
8
|
-
* - Preserves backward-compatible behavior when called without params
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { describe, expect, test } from "bun:test";
|
|
12
|
-
import { getNextStory, markStoryFailed } from "../../src/prd";
|
|
13
|
-
import type { PRD, UserStory } from "../../src/prd/types";
|
|
14
|
-
|
|
15
|
-
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
16
|
-
|
|
17
|
-
function makeStory(id: string, overrides: Partial<UserStory> = {}): UserStory {
|
|
18
|
-
return {
|
|
19
|
-
id,
|
|
20
|
-
title: `Story ${id}`,
|
|
21
|
-
description: "Test story",
|
|
22
|
-
acceptanceCriteria: ["AC1"],
|
|
23
|
-
tags: [],
|
|
24
|
-
dependencies: [],
|
|
25
|
-
status: "pending",
|
|
26
|
-
passes: false,
|
|
27
|
-
escalations: [],
|
|
28
|
-
attempts: 0,
|
|
29
|
-
...overrides,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function makePrd(stories: UserStory[]): PRD {
|
|
34
|
-
return {
|
|
35
|
-
project: "test",
|
|
36
|
-
feature: "test-feature",
|
|
37
|
-
branchName: "feature/test",
|
|
38
|
-
createdAt: new Date().toISOString(),
|
|
39
|
-
updatedAt: new Date().toISOString(),
|
|
40
|
-
userStories: stories,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// ── Backward-compatible behavior (no params) ─────────────────────────────────
|
|
45
|
-
|
|
46
|
-
describe("getNextStory() — backward compat (no currentStoryId/maxRetries)", () => {
|
|
47
|
-
test("returns first pending story", () => {
|
|
48
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
49
|
-
expect(getNextStory(prd)?.id).toBe("US-001");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test("skips failed stories (existing behavior)", () => {
|
|
53
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
54
|
-
markStoryFailed(prd, "US-001");
|
|
55
|
-
|
|
56
|
-
expect(getNextStory(prd)?.id).toBe("US-002");
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("returns null when all stories are failed or passed", () => {
|
|
60
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
61
|
-
markStoryFailed(prd, "US-001");
|
|
62
|
-
prd.userStories[1].status = "passed";
|
|
63
|
-
prd.userStories[1].passes = true;
|
|
64
|
-
|
|
65
|
-
expect(getNextStory(prd)).toBeNull();
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// ── Retry priority (BUG-022) ─────────────────────────────────────────────────
|
|
70
|
-
|
|
71
|
-
describe("getNextStory() — retry priority (BUG-022)", () => {
|
|
72
|
-
test("returns failed current story when attempts <= maxRetries", () => {
|
|
73
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
74
|
-
markStoryFailed(prd, "US-001"); // attempts = 1
|
|
75
|
-
|
|
76
|
-
// With maxRetries=2 and attempts=1, S1 should be retried
|
|
77
|
-
const next = getNextStory(prd, "US-001", 2);
|
|
78
|
-
expect(next?.id).toBe("US-001");
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("returns failed current story on last allowed retry (attempts == maxRetries)", () => {
|
|
82
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
83
|
-
markStoryFailed(prd, "US-001"); // attempts = 1
|
|
84
|
-
markStoryFailed(prd, "US-001"); // attempts = 2
|
|
85
|
-
|
|
86
|
-
// With maxRetries=2 and attempts=2, still within limit
|
|
87
|
-
const next = getNextStory(prd, "US-001", 2);
|
|
88
|
-
expect(next?.id).toBe("US-001");
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test("moves to next story when attempts exceed maxRetries", () => {
|
|
92
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
93
|
-
markStoryFailed(prd, "US-001"); // attempts = 1
|
|
94
|
-
markStoryFailed(prd, "US-001"); // attempts = 2
|
|
95
|
-
markStoryFailed(prd, "US-001"); // attempts = 3
|
|
96
|
-
|
|
97
|
-
// With maxRetries=2 and attempts=3, move to US-002
|
|
98
|
-
const next = getNextStory(prd, "US-001", 2);
|
|
99
|
-
expect(next?.id).toBe("US-002");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("moves to next story when current story is not failed", () => {
|
|
103
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
104
|
-
// US-001 is pending (not failed)
|
|
105
|
-
|
|
106
|
-
const next = getNextStory(prd, "US-001", 2);
|
|
107
|
-
expect(next?.id).toBe("US-001"); // Normal pending story picked up
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test("skips retry when maxRetries=0", () => {
|
|
111
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
112
|
-
markStoryFailed(prd, "US-001"); // attempts = 1
|
|
113
|
-
|
|
114
|
-
// maxRetries=0 disables retry — move to US-002
|
|
115
|
-
const next = getNextStory(prd, "US-001", 0);
|
|
116
|
-
expect(next?.id).toBe("US-002");
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
test("skips retry when currentStoryId is null", () => {
|
|
120
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
121
|
-
markStoryFailed(prd, "US-001"); // attempts = 1
|
|
122
|
-
|
|
123
|
-
// null currentStoryId — no retry priority, picks next pending
|
|
124
|
-
const next = getNextStory(prd, null, 2);
|
|
125
|
-
expect(next?.id).toBe("US-002");
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test("skips retry when currentStoryId not found in PRD", () => {
|
|
129
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
130
|
-
markStoryFailed(prd, "US-001");
|
|
131
|
-
|
|
132
|
-
// Unknown story ID — falls through to normal logic
|
|
133
|
-
const next = getNextStory(prd, "US-999", 2);
|
|
134
|
-
expect(next?.id).toBe("US-002");
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// ── Run order (AC-2) ─────────────────────────────────────────────────────────
|
|
139
|
-
|
|
140
|
-
describe("getNextStory() — run order S1-I1 -> S1-I2 (retry) -> S2-I1", () => {
|
|
141
|
-
test("enforces retry-before-next-story order across simulated iterations", () => {
|
|
142
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
143
|
-
let lastId: string | null = null;
|
|
144
|
-
const maxRetries = 1;
|
|
145
|
-
const order: string[] = [];
|
|
146
|
-
|
|
147
|
-
// Iteration 1: S1 first attempt
|
|
148
|
-
const pick1 = getNextStory(prd, lastId, maxRetries);
|
|
149
|
-
expect(pick1?.id).toBe("US-001");
|
|
150
|
-
order.push(pick1!.id);
|
|
151
|
-
lastId = pick1!.id;
|
|
152
|
-
markStoryFailed(prd, "US-001"); // S1 fails (attempts = 1)
|
|
153
|
-
|
|
154
|
-
// Iteration 2: S1 retry (attempts=1 <= maxRetries=1)
|
|
155
|
-
const pick2 = getNextStory(prd, lastId, maxRetries);
|
|
156
|
-
expect(pick2?.id).toBe("US-001");
|
|
157
|
-
order.push(pick2!.id);
|
|
158
|
-
lastId = pick2!.id;
|
|
159
|
-
markStoryFailed(prd, "US-001"); // S1 fails again (attempts = 2)
|
|
160
|
-
|
|
161
|
-
// Iteration 3: S1 exhausted (attempts=2 > maxRetries=1), move to S2
|
|
162
|
-
const pick3 = getNextStory(prd, lastId, maxRetries);
|
|
163
|
-
expect(pick3?.id).toBe("US-002");
|
|
164
|
-
order.push(pick3!.id);
|
|
165
|
-
|
|
166
|
-
expect(order).toEqual(["US-001", "US-001", "US-002"]);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test("moves to S2 after S1 passes (no retry needed)", () => {
|
|
170
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
171
|
-
let lastId: string | null = null;
|
|
172
|
-
const maxRetries = 2;
|
|
173
|
-
|
|
174
|
-
// Iteration 1: S1 picked
|
|
175
|
-
const pick1 = getNextStory(prd, lastId, maxRetries);
|
|
176
|
-
expect(pick1?.id).toBe("US-001");
|
|
177
|
-
lastId = pick1!.id;
|
|
178
|
-
|
|
179
|
-
// S1 passes
|
|
180
|
-
prd.userStories[0].status = "passed";
|
|
181
|
-
prd.userStories[0].passes = true;
|
|
182
|
-
|
|
183
|
-
// Iteration 2: S2 picked (S1 done)
|
|
184
|
-
const pick2 = getNextStory(prd, lastId, maxRetries);
|
|
185
|
-
expect(pick2?.id).toBe("US-002");
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test("BUG-029: prioritizes escalated story (pending + attempts > 0) over other pending stories", () => {
|
|
189
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002"), makeStory("US-003")]);
|
|
190
|
-
const maxRetries = 2;
|
|
191
|
-
|
|
192
|
-
// Simulate: US-001 was escalated — status reset to "pending" but has prior attempts
|
|
193
|
-
prd.userStories[0].status = "pending";
|
|
194
|
-
prd.userStories[0].attempts = 1;
|
|
195
|
-
prd.userStories[0].routing = { complexity: "simple", modelTier: "balanced", testStrategy: "test-after" };
|
|
196
|
-
|
|
197
|
-
// getNextStory should prioritize US-001 (escalated, pending with attempts)
|
|
198
|
-
const pick = getNextStory(prd, "US-001", maxRetries);
|
|
199
|
-
expect(pick?.id).toBe("US-001");
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
test("BUG-029: does not reprioritize story with 0 attempts (fresh pending)", () => {
|
|
203
|
-
const prd = makePrd([makeStory("US-001"), makeStory("US-002")]);
|
|
204
|
-
const maxRetries = 2;
|
|
205
|
-
|
|
206
|
-
// US-001 is fresh pending (no prior attempts) — normal ordering applies
|
|
207
|
-
prd.userStories[0].status = "pending";
|
|
208
|
-
prd.userStories[0].attempts = 0;
|
|
209
|
-
|
|
210
|
-
// Should still pick US-001 (first pending), but via normal path not escalation path
|
|
211
|
-
const pick = getNextStory(prd, "US-002", maxRetries);
|
|
212
|
-
expect(pick?.id).toBe("US-001");
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
});
|