@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,355 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import type { AgentAdapter, AgentResult } from "../../../src/agents";
|
|
6
|
-
import { DEFAULT_CONFIG } from "../../../src/config";
|
|
7
|
-
import type { UserStory } from "../../../src/prd";
|
|
8
|
-
import { runThreeSessionTdd } from "../../../src/tdd/orchestrator";
|
|
9
|
-
import { VERDICT_FILE } from "../../../src/tdd/verdict";
|
|
10
|
-
|
|
11
|
-
let originalSpawn: typeof Bun.spawn;
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
originalSpawn = Bun.spawn;
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
afterEach(() => {
|
|
18
|
-
Bun.spawn = originalSpawn;
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
/** Create a mock agent that returns sequential results */
|
|
22
|
-
function createMockAgent(results: Partial<AgentResult>[]): AgentAdapter {
|
|
23
|
-
let callCount = 0;
|
|
24
|
-
return {
|
|
25
|
-
name: "mock",
|
|
26
|
-
displayName: "Mock Agent",
|
|
27
|
-
binary: "mock",
|
|
28
|
-
isInstalled: async () => true,
|
|
29
|
-
buildCommand: () => ["mock"],
|
|
30
|
-
run: mock(async () => {
|
|
31
|
-
const r = results[callCount] || {};
|
|
32
|
-
callCount++;
|
|
33
|
-
return {
|
|
34
|
-
success: r.success ?? true,
|
|
35
|
-
exitCode: r.exitCode ?? 0,
|
|
36
|
-
output: r.output ?? "",
|
|
37
|
-
rateLimited: r.rateLimited ?? false,
|
|
38
|
-
durationMs: r.durationMs ?? 100,
|
|
39
|
-
estimatedCost: r.estimatedCost ?? 0.01,
|
|
40
|
-
};
|
|
41
|
-
}),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Mock Bun.spawn to intercept git commands */
|
|
46
|
-
function mockGitSpawn(opts: {
|
|
47
|
-
/** Files returned by git diff for each session (indexed by git-diff call number) */
|
|
48
|
-
diffFiles: string[][];
|
|
49
|
-
/** Optional: mock test command success (default: true) */
|
|
50
|
-
testCommandSuccess?: boolean;
|
|
51
|
-
}) {
|
|
52
|
-
let revParseCount = 0;
|
|
53
|
-
let diffCount = 0;
|
|
54
|
-
const testSuccess = opts.testCommandSuccess ?? true;
|
|
55
|
-
|
|
56
|
-
// @ts-ignore — mocking global
|
|
57
|
-
Bun.spawn = mock((cmd: string[], spawnOpts?: any) => {
|
|
58
|
-
// Intercept test commands (bun test, npm test, etc.)
|
|
59
|
-
if ((cmd[0] === "/bin/sh" || cmd[0] === "/bin/bash" || cmd[0] === "/bin/zsh") && cmd[1] === "-c") {
|
|
60
|
-
return {
|
|
61
|
-
pid: 9999,
|
|
62
|
-
exited: Promise.resolve(testSuccess ? 0 : 1),
|
|
63
|
-
stdout: new Response(testSuccess ? "tests pass\n" : "tests fail\n").body,
|
|
64
|
-
stderr: new Response("").body,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
if (cmd[0] === "git" && cmd[1] === "rev-parse") {
|
|
68
|
-
revParseCount++;
|
|
69
|
-
return {
|
|
70
|
-
exited: Promise.resolve(0),
|
|
71
|
-
stdout: new Response(`ref-${revParseCount}\n`).body,
|
|
72
|
-
stderr: new Response("").body,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
if (cmd[0] === "git" && cmd[1] === "checkout") {
|
|
76
|
-
// Intercept git checkout (used in zero-file fallback) — silently succeed
|
|
77
|
-
return {
|
|
78
|
-
exited: Promise.resolve(0),
|
|
79
|
-
stdout: new Response("").body,
|
|
80
|
-
stderr: new Response("").body,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
if (cmd[0] === "git" && cmd[1] === "diff") {
|
|
84
|
-
const files = opts.diffFiles[diffCount] || [];
|
|
85
|
-
diffCount++;
|
|
86
|
-
return {
|
|
87
|
-
exited: Promise.resolve(0),
|
|
88
|
-
stdout: new Response(files.join("\n") + "\n").body,
|
|
89
|
-
stderr: new Response("").body,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
return originalSpawn(cmd, spawnOpts);
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const story: UserStory = {
|
|
97
|
-
id: "US-001",
|
|
98
|
-
title: "Add user validation",
|
|
99
|
-
description: "Add validation to user input",
|
|
100
|
-
acceptanceCriteria: ["Validation works", "Errors are clear"],
|
|
101
|
-
dependencies: [],
|
|
102
|
-
tags: [],
|
|
103
|
-
status: "pending",
|
|
104
|
-
passes: false,
|
|
105
|
-
escalations: [],
|
|
106
|
-
attempts: 0,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
describe("runThreeSessionTdd — failureCategory", () => {
|
|
111
|
-
test("test-writer isolation failure sets failureCategory='isolation-violation'", async () => {
|
|
112
|
-
// Test-writer modifies source files → isolation violation
|
|
113
|
-
mockGitSpawn({
|
|
114
|
-
diffFiles: [
|
|
115
|
-
// Isolation check: test-writer touched source files!
|
|
116
|
-
["src/user.ts", "test/user.test.ts"],
|
|
117
|
-
// getChangedFiles
|
|
118
|
-
["src/user.ts", "test/user.test.ts"],
|
|
119
|
-
],
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const agent = createMockAgent([{ success: true, estimatedCost: 0.01 }]);
|
|
123
|
-
|
|
124
|
-
const result = await runThreeSessionTdd({
|
|
125
|
-
agent,
|
|
126
|
-
story,
|
|
127
|
-
config: DEFAULT_CONFIG,
|
|
128
|
-
workdir: "/tmp/test",
|
|
129
|
-
modelTier: "balanced",
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
expect(result.success).toBe(false);
|
|
133
|
-
expect(result.failureCategory).toBe("isolation-violation");
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test("test-writer zero files (non-auto strategy) sets failureCategory='isolation-violation'", async () => {
|
|
137
|
-
// In strict strategy, zero test files → greenfield-no-tests category (BUG-010 behavior)
|
|
138
|
-
mockGitSpawn({
|
|
139
|
-
diffFiles: [
|
|
140
|
-
["requirements.md"], // s1 isolation — no source violations
|
|
141
|
-
["requirements.md"], // s1 getChangedFiles — 0 test files
|
|
142
|
-
],
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
const agent = createMockAgent([{ success: true, estimatedCost: 0.01 }]);
|
|
146
|
-
|
|
147
|
-
const configWithStrictStrategy = {
|
|
148
|
-
...DEFAULT_CONFIG,
|
|
149
|
-
tdd: { ...DEFAULT_CONFIG.tdd, strategy: "strict" as const },
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const result = await runThreeSessionTdd({
|
|
153
|
-
agent,
|
|
154
|
-
story,
|
|
155
|
-
config: configWithStrictStrategy,
|
|
156
|
-
workdir: "/tmp/test",
|
|
157
|
-
modelTier: "balanced",
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
expect(result.success).toBe(false);
|
|
161
|
-
expect(result.failureCategory).toBe("greenfield-no-tests");
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test("test-writer crash/timeout (non-isolation failure) sets failureCategory='session-failure'", async () => {
|
|
165
|
-
// Test-writer agent crashes/times out but isolation is clean
|
|
166
|
-
mockGitSpawn({
|
|
167
|
-
diffFiles: [
|
|
168
|
-
// Isolation check: only test files (passes)
|
|
169
|
-
["test/user.test.ts"],
|
|
170
|
-
// getChangedFiles
|
|
171
|
-
["test/user.test.ts"],
|
|
172
|
-
],
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
const agent = createMockAgent([
|
|
176
|
-
{ success: false, exitCode: 1, estimatedCost: 0.01 }, // Agent crash
|
|
177
|
-
]);
|
|
178
|
-
|
|
179
|
-
const result = await runThreeSessionTdd({
|
|
180
|
-
agent,
|
|
181
|
-
story,
|
|
182
|
-
config: DEFAULT_CONFIG,
|
|
183
|
-
workdir: "/tmp/test",
|
|
184
|
-
modelTier: "balanced",
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
expect(result.success).toBe(false);
|
|
188
|
-
// isolation.passed=true but agent failed → session-failure
|
|
189
|
-
expect(result.failureCategory).toBe("session-failure");
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
test("implementer failure sets failureCategory='session-failure'", async () => {
|
|
193
|
-
mockGitSpawn({
|
|
194
|
-
diffFiles: [
|
|
195
|
-
// Session 1 isolation: OK
|
|
196
|
-
["test/user.test.ts"],
|
|
197
|
-
// Session 1 getChangedFiles
|
|
198
|
-
["test/user.test.ts"],
|
|
199
|
-
// Session 2 isolation: OK
|
|
200
|
-
["src/user.ts"],
|
|
201
|
-
// Session 2 getChangedFiles
|
|
202
|
-
["src/user.ts"],
|
|
203
|
-
],
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
const agent = createMockAgent([
|
|
207
|
-
{ success: true, estimatedCost: 0.01 }, // test-writer OK
|
|
208
|
-
{ success: false, exitCode: 1, estimatedCost: 0.02 }, // implementer fails
|
|
209
|
-
]);
|
|
210
|
-
|
|
211
|
-
const result = await runThreeSessionTdd({
|
|
212
|
-
agent,
|
|
213
|
-
story,
|
|
214
|
-
config: DEFAULT_CONFIG,
|
|
215
|
-
workdir: "/tmp/test",
|
|
216
|
-
modelTier: "balanced",
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
expect(result.success).toBe(false);
|
|
220
|
-
expect(result.failureCategory).toBe("session-failure");
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
test("post-TDD test failure sets failureCategory='tests-failing'", async () => {
|
|
224
|
-
// Verifier session fails AND independent test run also fails
|
|
225
|
-
let revParseCount = 0;
|
|
226
|
-
let diffCount = 0;
|
|
227
|
-
|
|
228
|
-
const diffFiles = [["test/user.test.ts"], ["test/user.test.ts"], ["src/user.ts"], ["src/user.ts"], ["src/user.ts"]];
|
|
229
|
-
|
|
230
|
-
// @ts-ignore — mocking global
|
|
231
|
-
Bun.spawn = mock((cmd: string[], spawnOpts?: any) => {
|
|
232
|
-
if (cmd[0] === "/bin/sh" && cmd[2]?.includes("bun test")) {
|
|
233
|
-
return {
|
|
234
|
-
pid: 9999,
|
|
235
|
-
exited: Promise.resolve(1), // Tests FAIL
|
|
236
|
-
stdout: new Response("3 pass, 2 fail\n").body,
|
|
237
|
-
stderr: new Response("Test errors...\n").body,
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
if (cmd[0] === "git" && cmd[1] === "rev-parse") {
|
|
241
|
-
revParseCount++;
|
|
242
|
-
return {
|
|
243
|
-
exited: Promise.resolve(0),
|
|
244
|
-
stdout: new Response(`ref-${revParseCount}\n`).body,
|
|
245
|
-
stderr: new Response("").body,
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
if (cmd[0] === "git" && cmd[1] === "diff") {
|
|
249
|
-
const files = diffFiles[diffCount] || [];
|
|
250
|
-
diffCount++;
|
|
251
|
-
return {
|
|
252
|
-
exited: Promise.resolve(0),
|
|
253
|
-
stdout: new Response(files.join("\n") + "\n").body,
|
|
254
|
-
stderr: new Response("").body,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
return originalSpawn(cmd, spawnOpts);
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
const agent = createMockAgent([
|
|
261
|
-
{ success: true, estimatedCost: 0.01 },
|
|
262
|
-
{ success: true, estimatedCost: 0.02 },
|
|
263
|
-
{ success: false, exitCode: 1, estimatedCost: 0.01 }, // verifier fails
|
|
264
|
-
]);
|
|
265
|
-
|
|
266
|
-
const result = await runThreeSessionTdd({
|
|
267
|
-
agent,
|
|
268
|
-
story,
|
|
269
|
-
config: DEFAULT_CONFIG,
|
|
270
|
-
workdir: "/tmp/test",
|
|
271
|
-
modelTier: "balanced",
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
expect(result.success).toBe(false);
|
|
275
|
-
expect(result.failureCategory).toBe("tests-failing");
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
test("success path has no failureCategory", async () => {
|
|
279
|
-
mockGitSpawn({
|
|
280
|
-
diffFiles: [["test/user.test.ts"], ["test/user.test.ts"], ["src/user.ts"], ["src/user.ts"], ["src/user.ts"]],
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
const agent = createMockAgent([
|
|
284
|
-
{ success: true, estimatedCost: 0.01 },
|
|
285
|
-
{ success: true, estimatedCost: 0.02 },
|
|
286
|
-
{ success: true, estimatedCost: 0.01 },
|
|
287
|
-
]);
|
|
288
|
-
|
|
289
|
-
const result = await runThreeSessionTdd({
|
|
290
|
-
agent,
|
|
291
|
-
story,
|
|
292
|
-
config: DEFAULT_CONFIG,
|
|
293
|
-
workdir: "/tmp/test",
|
|
294
|
-
modelTier: "balanced",
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
expect(result.success).toBe(true);
|
|
298
|
-
expect(result.failureCategory).toBeUndefined();
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
test("zero-file scenario (auto strategy) returns greenfield-no-tests (BUG-010 removed auto-fallback)", async () => {
|
|
302
|
-
// BUG-010: In auto strategy, zero test files → return greenfield-no-tests (no more fallback)
|
|
303
|
-
let diffCount = 0;
|
|
304
|
-
|
|
305
|
-
const diffFiles = [
|
|
306
|
-
["requirements.md"], // s1 isolation (strict) — no source violations
|
|
307
|
-
["requirements.md"], // s1 getChangedFiles (strict) — 0 test files → return greenfield-no-tests
|
|
308
|
-
];
|
|
309
|
-
|
|
310
|
-
// @ts-ignore — mocking global
|
|
311
|
-
Bun.spawn = mock((cmd: string[], spawnOpts?: any) => {
|
|
312
|
-
if (cmd[0] === "git" && cmd[1] === "rev-parse") {
|
|
313
|
-
return {
|
|
314
|
-
exited: Promise.resolve(0),
|
|
315
|
-
stdout: new Response("ref-1\n").body,
|
|
316
|
-
stderr: new Response("").body,
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
if (cmd[0] === "git" && cmd[1] === "diff") {
|
|
320
|
-
const files = diffFiles[diffCount] || [];
|
|
321
|
-
diffCount++;
|
|
322
|
-
return {
|
|
323
|
-
exited: Promise.resolve(0),
|
|
324
|
-
stdout: new Response(files.join("\n") + "\n").body,
|
|
325
|
-
stderr: new Response("").body,
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
return originalSpawn(cmd, spawnOpts);
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
const agent = createMockAgent([
|
|
332
|
-
{ success: true, estimatedCost: 0.01 }, // s1 strict test-writer
|
|
333
|
-
]);
|
|
334
|
-
|
|
335
|
-
const configWithAutoStrategy = {
|
|
336
|
-
...DEFAULT_CONFIG,
|
|
337
|
-
tdd: { ...DEFAULT_CONFIG.tdd, strategy: "auto" as const },
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
const result = await runThreeSessionTdd({
|
|
341
|
-
agent,
|
|
342
|
-
story,
|
|
343
|
-
config: configWithAutoStrategy,
|
|
344
|
-
workdir: "/tmp/test",
|
|
345
|
-
modelTier: "balanced",
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
expect(result.success).toBe(false);
|
|
349
|
-
expect(result.lite).toBe(false);
|
|
350
|
-
expect(result.failureCategory).toBe("greenfield-no-tests");
|
|
351
|
-
});
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
// ─── T9: Verdict integration tests ───────────────────────────────────────────
|
|
355
|
-
|
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import type { AgentAdapter, AgentResult } from "../../../src/agents";
|
|
6
|
-
import { DEFAULT_CONFIG } from "../../../src/config";
|
|
7
|
-
import type { UserStory } from "../../../src/prd";
|
|
8
|
-
import { runThreeSessionTdd } from "../../../src/tdd/orchestrator";
|
|
9
|
-
import { VERDICT_FILE } from "../../../src/tdd/verdict";
|
|
10
|
-
|
|
11
|
-
let originalSpawn: typeof Bun.spawn;
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
originalSpawn = Bun.spawn;
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
afterEach(() => {
|
|
18
|
-
Bun.spawn = originalSpawn;
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
/** Create a mock agent that returns sequential results */
|
|
22
|
-
function createMockAgent(results: Partial<AgentResult>[]): AgentAdapter {
|
|
23
|
-
let callCount = 0;
|
|
24
|
-
return {
|
|
25
|
-
name: "mock",
|
|
26
|
-
displayName: "Mock Agent",
|
|
27
|
-
binary: "mock",
|
|
28
|
-
isInstalled: async () => true,
|
|
29
|
-
buildCommand: () => ["mock"],
|
|
30
|
-
run: mock(async () => {
|
|
31
|
-
const r = results[callCount] || {};
|
|
32
|
-
callCount++;
|
|
33
|
-
return {
|
|
34
|
-
success: r.success ?? true,
|
|
35
|
-
exitCode: r.exitCode ?? 0,
|
|
36
|
-
output: r.output ?? "",
|
|
37
|
-
rateLimited: r.rateLimited ?? false,
|
|
38
|
-
durationMs: r.durationMs ?? 100,
|
|
39
|
-
estimatedCost: r.estimatedCost ?? 0.01,
|
|
40
|
-
};
|
|
41
|
-
}),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Mock Bun.spawn to intercept git commands */
|
|
46
|
-
function mockGitSpawn(opts: {
|
|
47
|
-
/** Files returned by git diff for each session (indexed by git-diff call number) */
|
|
48
|
-
diffFiles: string[][];
|
|
49
|
-
/** Optional: mock test command success (default: true) */
|
|
50
|
-
testCommandSuccess?: boolean;
|
|
51
|
-
}) {
|
|
52
|
-
let revParseCount = 0;
|
|
53
|
-
let diffCount = 0;
|
|
54
|
-
const testSuccess = opts.testCommandSuccess ?? true;
|
|
55
|
-
|
|
56
|
-
// @ts-ignore — mocking global
|
|
57
|
-
Bun.spawn = mock((cmd: string[], spawnOpts?: any) => {
|
|
58
|
-
// Intercept test commands (bun test, npm test, etc.)
|
|
59
|
-
if ((cmd[0] === "/bin/sh" || cmd[0] === "/bin/bash" || cmd[0] === "/bin/zsh") && cmd[1] === "-c") {
|
|
60
|
-
return {
|
|
61
|
-
pid: 9999,
|
|
62
|
-
exited: Promise.resolve(testSuccess ? 0 : 1),
|
|
63
|
-
stdout: new Response(testSuccess ? "tests pass\n" : "tests fail\n").body,
|
|
64
|
-
stderr: new Response("").body,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
if (cmd[0] === "git" && cmd[1] === "rev-parse") {
|
|
68
|
-
revParseCount++;
|
|
69
|
-
return {
|
|
70
|
-
exited: Promise.resolve(0),
|
|
71
|
-
stdout: new Response(`ref-${revParseCount}\n`).body,
|
|
72
|
-
stderr: new Response("").body,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
if (cmd[0] === "git" && cmd[1] === "checkout") {
|
|
76
|
-
// Intercept git checkout (used in zero-file fallback) — silently succeed
|
|
77
|
-
return {
|
|
78
|
-
exited: Promise.resolve(0),
|
|
79
|
-
stdout: new Response("").body,
|
|
80
|
-
stderr: new Response("").body,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
if (cmd[0] === "git" && cmd[1] === "diff") {
|
|
84
|
-
const files = opts.diffFiles[diffCount] || [];
|
|
85
|
-
diffCount++;
|
|
86
|
-
return {
|
|
87
|
-
exited: Promise.resolve(0),
|
|
88
|
-
stdout: new Response(files.join("\n") + "\n").body,
|
|
89
|
-
stderr: new Response("").body,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
return originalSpawn(cmd, spawnOpts);
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const story: UserStory = {
|
|
97
|
-
id: "US-001",
|
|
98
|
-
title: "Add user validation",
|
|
99
|
-
description: "Add validation to user input",
|
|
100
|
-
acceptanceCriteria: ["Validation works", "Errors are clear"],
|
|
101
|
-
dependencies: [],
|
|
102
|
-
tags: [],
|
|
103
|
-
status: "pending",
|
|
104
|
-
passes: false,
|
|
105
|
-
escalations: [],
|
|
106
|
-
attempts: 0,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
describe("runThreeSessionTdd — zero-file fallback", () => {
|
|
111
|
-
/** Extended git mock that also handles `git checkout .` */
|
|
112
|
-
function mockGitSpawnWithCheckout(opts: {
|
|
113
|
-
diffFiles: string[][];
|
|
114
|
-
onCheckout?: () => void;
|
|
115
|
-
testCommandSuccess?: boolean;
|
|
116
|
-
}) {
|
|
117
|
-
let revParseCount = 0;
|
|
118
|
-
let diffCount = 0;
|
|
119
|
-
const testSuccess = opts.testCommandSuccess ?? true;
|
|
120
|
-
|
|
121
|
-
// @ts-ignore — mocking global
|
|
122
|
-
Bun.spawn = mock((cmd: string[], spawnOpts?: any) => {
|
|
123
|
-
// Intercept test commands
|
|
124
|
-
if ((cmd[0] === "/bin/sh" || cmd[0] === "/bin/bash" || cmd[0] === "/bin/zsh") && cmd[1] === "-c") {
|
|
125
|
-
return {
|
|
126
|
-
pid: 9999,
|
|
127
|
-
exited: Promise.resolve(testSuccess ? 0 : 1),
|
|
128
|
-
stdout: new Response(testSuccess ? "tests pass\n" : "tests fail\n").body,
|
|
129
|
-
stderr: new Response("").body,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
if (cmd[0] === "git" && cmd[1] === "rev-parse") {
|
|
133
|
-
revParseCount++;
|
|
134
|
-
return {
|
|
135
|
-
exited: Promise.resolve(0),
|
|
136
|
-
stdout: new Response(`ref-${revParseCount}\n`).body,
|
|
137
|
-
stderr: new Response("").body,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
if (cmd[0] === "git" && cmd[1] === "checkout") {
|
|
141
|
-
opts.onCheckout?.();
|
|
142
|
-
return {
|
|
143
|
-
exited: Promise.resolve(0),
|
|
144
|
-
stdout: new Response("").body,
|
|
145
|
-
stderr: new Response("").body,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
if (cmd[0] === "git" && cmd[1] === "diff") {
|
|
149
|
-
const files = opts.diffFiles[diffCount] || [];
|
|
150
|
-
diffCount++;
|
|
151
|
-
return {
|
|
152
|
-
exited: Promise.resolve(0),
|
|
153
|
-
stdout: new Response(files.join("\n") + "\n").body,
|
|
154
|
-
stderr: new Response("").body,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
return originalSpawn(cmd, spawnOpts);
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
test("fallback NO LONGER triggers when strategy='auto' and 0 test files (BUG-010 removed auto-fallback)", async () => {
|
|
162
|
-
let checkoutCalled = false;
|
|
163
|
-
|
|
164
|
-
// BUG-010: Zero-file scenarios now return greenfield-no-tests immediately
|
|
165
|
-
// No fallback to lite mode occurs
|
|
166
|
-
mockGitSpawnWithCheckout({
|
|
167
|
-
diffFiles: [
|
|
168
|
-
["requirements.md"], // s1 isolation (strict) — no source violations
|
|
169
|
-
["requirements.md"], // s1 getChangedFiles (strict) — 0 test files → return greenfield-no-tests
|
|
170
|
-
],
|
|
171
|
-
onCheckout: () => {
|
|
172
|
-
checkoutCalled = true;
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
const agent = createMockAgent([
|
|
177
|
-
{ success: true, estimatedCost: 0.01 }, // s1 strict test-writer
|
|
178
|
-
]);
|
|
179
|
-
|
|
180
|
-
const configWithAutoStrategy = {
|
|
181
|
-
...DEFAULT_CONFIG,
|
|
182
|
-
tdd: { ...DEFAULT_CONFIG.tdd, strategy: "auto" as const },
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
const result = await runThreeSessionTdd({
|
|
186
|
-
agent,
|
|
187
|
-
story,
|
|
188
|
-
config: configWithAutoStrategy,
|
|
189
|
-
workdir: "/tmp/test",
|
|
190
|
-
modelTier: "balanced",
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
expect(checkoutCalled).toBe(false); // git checkout NOT called (no fallback)
|
|
194
|
-
expect(result.lite).toBe(false); // not in lite mode
|
|
195
|
-
expect(result.success).toBe(false); // fails with greenfield-no-tests
|
|
196
|
-
expect(result.failureCategory).toBe("greenfield-no-tests");
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
test("zero-file scenario returns greenfield-no-tests (BUG-010 removed lite fallback)", async () => {
|
|
200
|
-
// BUG-010: No more auto-fallback to lite mode
|
|
201
|
-
mockGitSpawn({
|
|
202
|
-
diffFiles: [
|
|
203
|
-
["docs/plan.md"], // s1 isolation (strict)
|
|
204
|
-
["docs/plan.md"], // s1 getChangedFiles (strict) → 0 test files
|
|
205
|
-
],
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const agent = createMockAgent([{ success: true, estimatedCost: 0.01 }]);
|
|
209
|
-
|
|
210
|
-
const result = await runThreeSessionTdd({
|
|
211
|
-
agent,
|
|
212
|
-
story,
|
|
213
|
-
config: DEFAULT_CONFIG,
|
|
214
|
-
workdir: "/tmp/test",
|
|
215
|
-
modelTier: "balanced",
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
expect(result.lite).toBe(false);
|
|
219
|
-
expect(result.success).toBe(false);
|
|
220
|
-
expect(result.failureCategory).toBe("greenfield-no-tests");
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
test("fallback does NOT trigger when strategy='strict' (explicit strict mode)", async () => {
|
|
224
|
-
// In strategy='strict', no fallback — should return failure
|
|
225
|
-
mockGitSpawn({
|
|
226
|
-
diffFiles: [
|
|
227
|
-
["requirements.md"], // s1 isolation — no source violations
|
|
228
|
-
["requirements.md"], // s1 getChangedFiles — 0 test files
|
|
229
|
-
],
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
const agent = createMockAgent([{ success: true, estimatedCost: 0.01 }]);
|
|
233
|
-
|
|
234
|
-
const configWithStrictStrategy = {
|
|
235
|
-
...DEFAULT_CONFIG,
|
|
236
|
-
tdd: { ...DEFAULT_CONFIG.tdd, strategy: "strict" as const },
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
const result = await runThreeSessionTdd({
|
|
240
|
-
agent,
|
|
241
|
-
story,
|
|
242
|
-
config: configWithStrictStrategy,
|
|
243
|
-
workdir: "/tmp/test",
|
|
244
|
-
modelTier: "balanced",
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// Should fail (no fallback in strict mode)
|
|
248
|
-
expect(result.success).toBe(false);
|
|
249
|
-
expect(result.needsHumanReview).toBe(true);
|
|
250
|
-
expect(result.reviewReason).toBe("Test writer session created no test files (greenfield project)");
|
|
251
|
-
expect(result.lite).toBe(false); // Was called in strict mode, no fallback
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
test("fallback does NOT trigger when already in lite mode", async () => {
|
|
255
|
-
// Calling with lite=true — if 0 test files, should return failure (not recurse again)
|
|
256
|
-
mockGitSpawn({
|
|
257
|
-
diffFiles: [
|
|
258
|
-
["requirements.md"], // s1 getChangedFiles (lite, no isolation) — 0 test files
|
|
259
|
-
],
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
const agent = createMockAgent([{ success: true, estimatedCost: 0.01 }]);
|
|
263
|
-
|
|
264
|
-
const result = await runThreeSessionTdd({
|
|
265
|
-
agent,
|
|
266
|
-
story,
|
|
267
|
-
config: DEFAULT_CONFIG,
|
|
268
|
-
workdir: "/tmp/test",
|
|
269
|
-
modelTier: "balanced",
|
|
270
|
-
lite: true,
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
// Should fail — no further fallback from lite mode
|
|
274
|
-
expect(result.success).toBe(false);
|
|
275
|
-
expect(result.needsHumanReview).toBe(true);
|
|
276
|
-
expect(result.reviewReason).toBe("Test writer session created no test files (greenfield project)");
|
|
277
|
-
expect(result.lite).toBe(true);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
test("fallback does NOT trigger when strategy='lite' config", async () => {
|
|
281
|
-
// When strategy='lite', runThreeSessionTdd is called with lite=true (from execution stage)
|
|
282
|
-
// So !lite = false → no fallback
|
|
283
|
-
mockGitSpawn({
|
|
284
|
-
diffFiles: [
|
|
285
|
-
[], // s1 getChangedFiles (lite, no isolation) — 0 test files
|
|
286
|
-
],
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
const agent = createMockAgent([{ success: true, estimatedCost: 0.01 }]);
|
|
290
|
-
|
|
291
|
-
const configWithLiteStrategy = {
|
|
292
|
-
...DEFAULT_CONFIG,
|
|
293
|
-
tdd: { ...DEFAULT_CONFIG.tdd, strategy: "lite" as const },
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
const result = await runThreeSessionTdd({
|
|
297
|
-
agent,
|
|
298
|
-
story,
|
|
299
|
-
config: configWithLiteStrategy,
|
|
300
|
-
workdir: "/tmp/test",
|
|
301
|
-
modelTier: "balanced",
|
|
302
|
-
lite: true, // router sets this for lite strategy
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
expect(result.success).toBe(false);
|
|
306
|
-
expect(result.lite).toBe(true);
|
|
307
|
-
});
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
// ─── T4: failureCategory tests ────────────────────────────────────────────────
|
|
311
|
-
|