@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,318 +0,0 @@
|
|
|
1
|
-
// RE-ARCH: keep
|
|
2
|
-
/**
|
|
3
|
-
* Tests for src/execution/helpers.ts
|
|
4
|
-
*
|
|
5
|
-
* Covers: hookCtx, getAllReadyStories, acquireLock, releaseLock, formatProgress
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
9
|
-
import * as fs from "node:fs";
|
|
10
|
-
import * as os from "node:os";
|
|
11
|
-
import * as path from "node:path";
|
|
12
|
-
import {
|
|
13
|
-
type StoryCounts,
|
|
14
|
-
acquireLock,
|
|
15
|
-
formatProgress,
|
|
16
|
-
getAllReadyStories,
|
|
17
|
-
hookCtx,
|
|
18
|
-
releaseLock,
|
|
19
|
-
} from "../../src/execution/helpers";
|
|
20
|
-
import type { PRD, UserStory } from "../../src/prd";
|
|
21
|
-
|
|
22
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
23
|
-
// Test fixtures
|
|
24
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
25
|
-
|
|
26
|
-
const mockStory = (
|
|
27
|
-
id: string,
|
|
28
|
-
passes: boolean,
|
|
29
|
-
status: "pending" | "skipped",
|
|
30
|
-
dependencies: string[] = [],
|
|
31
|
-
): UserStory => ({
|
|
32
|
-
id,
|
|
33
|
-
title: `Story ${id}`,
|
|
34
|
-
description: `Description for ${id}`,
|
|
35
|
-
acceptanceCriteria: [],
|
|
36
|
-
dependencies,
|
|
37
|
-
passes,
|
|
38
|
-
status,
|
|
39
|
-
estimatedComplexity: "medium",
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const createMockPRD = (stories: UserStory[]): PRD => ({
|
|
43
|
-
feature: "test-feature",
|
|
44
|
-
userStories: stories,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
48
|
-
// hookCtx
|
|
49
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
50
|
-
|
|
51
|
-
describe("hookCtx", () => {
|
|
52
|
-
it("creates hook context with minimal args", () => {
|
|
53
|
-
const ctx = hookCtx("my-feature");
|
|
54
|
-
expect(ctx.event).toBe("on-start");
|
|
55
|
-
expect(ctx.feature).toBe("my-feature");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("merges optional fields", () => {
|
|
59
|
-
const ctx = hookCtx("my-feature", {
|
|
60
|
-
storyId: "US-001",
|
|
61
|
-
cost: 0.42,
|
|
62
|
-
});
|
|
63
|
-
expect(ctx.event).toBe("on-start");
|
|
64
|
-
expect(ctx.feature).toBe("my-feature");
|
|
65
|
-
expect(ctx.storyId).toBe("US-001");
|
|
66
|
-
expect(ctx.cost).toBe(0.42);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("overrides defaults with opts", () => {
|
|
70
|
-
const ctx = hookCtx("my-feature", {
|
|
71
|
-
storyId: "US-999",
|
|
72
|
-
});
|
|
73
|
-
expect(ctx.storyId).toBe("US-999");
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
78
|
-
// getAllReadyStories
|
|
79
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
describe("getAllReadyStories", () => {
|
|
82
|
-
it("returns pending stories with no dependencies", () => {
|
|
83
|
-
const prd = createMockPRD([mockStory("US-001", false, "pending"), mockStory("US-002", false, "pending")]);
|
|
84
|
-
|
|
85
|
-
const ready = getAllReadyStories(prd);
|
|
86
|
-
expect(ready.length).toBe(2);
|
|
87
|
-
expect(ready.map((s) => s.id)).toEqual(["US-001", "US-002"]);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("excludes stories that already passed", () => {
|
|
91
|
-
const prd = createMockPRD([mockStory("US-001", true, "pending"), mockStory("US-002", false, "pending")]);
|
|
92
|
-
|
|
93
|
-
const ready = getAllReadyStories(prd);
|
|
94
|
-
expect(ready.length).toBe(1);
|
|
95
|
-
expect(ready[0].id).toBe("US-002");
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("excludes skipped stories", () => {
|
|
99
|
-
const prd = createMockPRD([mockStory("US-001", false, "skipped"), mockStory("US-002", false, "pending")]);
|
|
100
|
-
|
|
101
|
-
const ready = getAllReadyStories(prd);
|
|
102
|
-
expect(ready.length).toBe(1);
|
|
103
|
-
expect(ready[0].id).toBe("US-002");
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("includes stories whose dependencies are complete", () => {
|
|
107
|
-
const prd = createMockPRD([
|
|
108
|
-
mockStory("US-001", true, "pending"),
|
|
109
|
-
mockStory("US-002", false, "pending", ["US-001"]),
|
|
110
|
-
]);
|
|
111
|
-
|
|
112
|
-
const ready = getAllReadyStories(prd);
|
|
113
|
-
expect(ready.length).toBe(1);
|
|
114
|
-
expect(ready[0].id).toBe("US-002");
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it("excludes stories with unsatisfied dependencies", () => {
|
|
118
|
-
const prd = createMockPRD([
|
|
119
|
-
mockStory("US-001", false, "pending"),
|
|
120
|
-
mockStory("US-002", false, "pending", ["US-001"]),
|
|
121
|
-
]);
|
|
122
|
-
|
|
123
|
-
const ready = getAllReadyStories(prd);
|
|
124
|
-
expect(ready.length).toBe(1);
|
|
125
|
-
expect(ready[0].id).toBe("US-001");
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("handles complex dependency chains", () => {
|
|
129
|
-
const prd = createMockPRD([
|
|
130
|
-
mockStory("US-001", true, "pending"),
|
|
131
|
-
mockStory("US-002", true, "pending", ["US-001"]),
|
|
132
|
-
mockStory("US-003", false, "pending", ["US-001", "US-002"]),
|
|
133
|
-
mockStory("US-004", false, "pending", ["US-002"]),
|
|
134
|
-
]);
|
|
135
|
-
|
|
136
|
-
const ready = getAllReadyStories(prd);
|
|
137
|
-
expect(ready.length).toBe(2);
|
|
138
|
-
expect(ready.map((s) => s.id).sort()).toEqual(["US-003", "US-004"]);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it("returns empty array when all stories are complete", () => {
|
|
142
|
-
const prd = createMockPRD([mockStory("US-001", true, "pending"), mockStory("US-002", true, "pending")]);
|
|
143
|
-
|
|
144
|
-
const ready = getAllReadyStories(prd);
|
|
145
|
-
expect(ready.length).toBe(0);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("handles skipped dependencies correctly", () => {
|
|
149
|
-
const prd = createMockPRD([
|
|
150
|
-
mockStory("US-001", false, "skipped"),
|
|
151
|
-
mockStory("US-002", false, "pending", ["US-001"]),
|
|
152
|
-
]);
|
|
153
|
-
|
|
154
|
-
const ready = getAllReadyStories(prd);
|
|
155
|
-
// US-002 is ready because US-001 is skipped (treated as complete)
|
|
156
|
-
expect(ready.length).toBe(1);
|
|
157
|
-
expect(ready[0].id).toBe("US-002");
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
162
|
-
// acquireLock / releaseLock
|
|
163
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
164
|
-
|
|
165
|
-
describe("acquireLock / releaseLock", () => {
|
|
166
|
-
let testDir: string;
|
|
167
|
-
|
|
168
|
-
beforeEach(() => {
|
|
169
|
-
// Create a temporary directory for lock tests
|
|
170
|
-
testDir = path.join(os.tmpdir(), `nax-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
171
|
-
fs.mkdirSync(testDir, { recursive: true });
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
afterEach(async () => {
|
|
175
|
-
// Clean up lock file and test directory
|
|
176
|
-
await releaseLock(testDir);
|
|
177
|
-
try {
|
|
178
|
-
fs.rmSync(testDir, { recursive: true, force: true });
|
|
179
|
-
} catch {
|
|
180
|
-
// Ignore cleanup errors
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it("acquires lock when no lock exists", async () => {
|
|
185
|
-
const acquired = await acquireLock(testDir);
|
|
186
|
-
expect(acquired).toBe(true);
|
|
187
|
-
|
|
188
|
-
// Verify lock file was created
|
|
189
|
-
const lockPath = path.join(testDir, "nax.lock");
|
|
190
|
-
expect(fs.existsSync(lockPath)).toBe(true);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it("fails to acquire lock when already locked", async () => {
|
|
194
|
-
const first = await acquireLock(testDir);
|
|
195
|
-
expect(first).toBe(true);
|
|
196
|
-
|
|
197
|
-
const second = await acquireLock(testDir);
|
|
198
|
-
expect(second).toBe(false);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it("releases lock successfully", async () => {
|
|
202
|
-
await acquireLock(testDir);
|
|
203
|
-
await releaseLock(testDir);
|
|
204
|
-
|
|
205
|
-
// Verify lock file was removed
|
|
206
|
-
const lockPath = path.join(testDir, "nax.lock");
|
|
207
|
-
expect(fs.existsSync(lockPath)).toBe(false);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it("can reacquire lock after release", async () => {
|
|
211
|
-
const first = await acquireLock(testDir);
|
|
212
|
-
expect(first).toBe(true);
|
|
213
|
-
|
|
214
|
-
await releaseLock(testDir);
|
|
215
|
-
|
|
216
|
-
const second = await acquireLock(testDir);
|
|
217
|
-
expect(second).toBe(true);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it("removes stale lock from dead process", async () => {
|
|
221
|
-
// Create a lock with a non-existent PID
|
|
222
|
-
const lockPath = path.join(testDir, "nax.lock");
|
|
223
|
-
const staleLock = {
|
|
224
|
-
pid: 999999, // Non-existent process
|
|
225
|
-
timestamp: Date.now() - 1000000,
|
|
226
|
-
};
|
|
227
|
-
fs.writeFileSync(lockPath, JSON.stringify(staleLock));
|
|
228
|
-
|
|
229
|
-
// Should acquire lock by removing stale lock
|
|
230
|
-
const acquired = await acquireLock(testDir);
|
|
231
|
-
expect(acquired).toBe(true);
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it("handles non-existent directory gracefully during release", async () => {
|
|
235
|
-
const nonExistentDir = path.join(os.tmpdir(), "nax-nonexistent");
|
|
236
|
-
await expect(releaseLock(nonExistentDir)).resolves.toBeUndefined();
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
241
|
-
// formatProgress
|
|
242
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
243
|
-
|
|
244
|
-
describe("formatProgress", () => {
|
|
245
|
-
it("formats progress with all stories pending", () => {
|
|
246
|
-
const counts: StoryCounts = {
|
|
247
|
-
total: 12,
|
|
248
|
-
passed: 0,
|
|
249
|
-
failed: 0,
|
|
250
|
-
pending: 12,
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
const progress = formatProgress(counts, 0, 5.0, 0, 12);
|
|
254
|
-
expect(progress).toContain("0/12 stories");
|
|
255
|
-
expect(progress).toContain("0 passed");
|
|
256
|
-
expect(progress).toContain("0 failed");
|
|
257
|
-
expect(progress).toContain("$0.00/$5.00");
|
|
258
|
-
expect(progress).toContain("calculating...");
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it("formats progress with some completed stories", () => {
|
|
262
|
-
const counts: StoryCounts = {
|
|
263
|
-
total: 12,
|
|
264
|
-
passed: 5,
|
|
265
|
-
failed: 1,
|
|
266
|
-
pending: 6,
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
const progress = formatProgress(counts, 0.45, 5.0, 600000, 12);
|
|
270
|
-
expect(progress).toContain("6/12 stories");
|
|
271
|
-
expect(progress).toContain("5 passed");
|
|
272
|
-
expect(progress).toContain("1 failed");
|
|
273
|
-
expect(progress).toContain("$0.45/$5.00");
|
|
274
|
-
expect(progress).toContain("min remaining");
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
it("formats progress when all stories complete", () => {
|
|
278
|
-
const counts: StoryCounts = {
|
|
279
|
-
total: 12,
|
|
280
|
-
passed: 10,
|
|
281
|
-
failed: 2,
|
|
282
|
-
pending: 0,
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
const progress = formatProgress(counts, 4.5, 5.0, 3600000, 12);
|
|
286
|
-
expect(progress).toContain("12/12 stories");
|
|
287
|
-
expect(progress).toContain("10 passed");
|
|
288
|
-
expect(progress).toContain("2 failed");
|
|
289
|
-
expect(progress).toContain("$4.50/$5.00");
|
|
290
|
-
expect(progress).toContain("complete");
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
it("calculates ETA correctly", () => {
|
|
294
|
-
const counts: StoryCounts = {
|
|
295
|
-
total: 10,
|
|
296
|
-
passed: 5,
|
|
297
|
-
failed: 0,
|
|
298
|
-
pending: 5,
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
// 5 completed in 600000ms (10 minutes) = 120000ms per story
|
|
302
|
-
// 5 remaining = 600000ms (10 minutes)
|
|
303
|
-
const progress = formatProgress(counts, 1.0, 5.0, 600000, 10);
|
|
304
|
-
expect(progress).toContain("~10 min remaining");
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
it("handles zero elapsed time", () => {
|
|
308
|
-
const counts: StoryCounts = {
|
|
309
|
-
total: 10,
|
|
310
|
-
passed: 1,
|
|
311
|
-
failed: 0,
|
|
312
|
-
pending: 9,
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
const progress = formatProgress(counts, 0.1, 5.0, 0, 10);
|
|
316
|
-
expect(progress).toContain("~0 min remaining");
|
|
317
|
-
});
|
|
318
|
-
});
|
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
// RE-ARCH: keep
|
|
2
|
-
/**
|
|
3
|
-
* Tests for src/tdd/verdict.ts
|
|
4
|
-
*
|
|
5
|
-
* Covers: readVerdict, cleanupVerdict, categorizeVerdict
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
9
|
-
import { existsSync } from "node:fs";
|
|
10
|
-
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
11
|
-
import { tmpdir } from "node:os";
|
|
12
|
-
import path from "node:path";
|
|
13
|
-
import { categorizeVerdict, cleanupVerdict, readVerdict } from "../../src/tdd/verdict";
|
|
14
|
-
import type { VerifierVerdict } from "../../src/tdd/verdict";
|
|
15
|
-
|
|
16
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
-
// Test fixtures
|
|
18
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
-
|
|
20
|
-
/** Build a fully valid verdict with sensible defaults. */
|
|
21
|
-
function makeVerdict(overrides: Partial<VerifierVerdict> = {}): VerifierVerdict {
|
|
22
|
-
return {
|
|
23
|
-
version: 1,
|
|
24
|
-
approved: true,
|
|
25
|
-
tests: { allPassing: true, passCount: 10, failCount: 0 },
|
|
26
|
-
testModifications: { detected: false, files: [], legitimate: true, reasoning: "No modifications" },
|
|
27
|
-
acceptanceCriteria: {
|
|
28
|
-
allMet: true,
|
|
29
|
-
criteria: [{ criterion: "Feature works", met: true }],
|
|
30
|
-
},
|
|
31
|
-
quality: { rating: "good", issues: [] },
|
|
32
|
-
fixes: [],
|
|
33
|
-
reasoning: "All good.",
|
|
34
|
-
...overrides,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
39
|
-
// Setup / teardown
|
|
40
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
41
|
-
|
|
42
|
-
let workdir: string;
|
|
43
|
-
|
|
44
|
-
beforeEach(async () => {
|
|
45
|
-
workdir = path.join(tmpdir(), `verdict-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
46
|
-
await mkdir(workdir, { recursive: true });
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
afterEach(async () => {
|
|
50
|
-
await rm(workdir, { recursive: true, force: true });
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
54
|
-
// readVerdict
|
|
55
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
56
|
-
|
|
57
|
-
describe("readVerdict", () => {
|
|
58
|
-
it("returns parsed verdict when file exists and is valid", async () => {
|
|
59
|
-
const verdict = makeVerdict();
|
|
60
|
-
await writeFile(path.join(workdir, ".nax-verifier-verdict.json"), JSON.stringify(verdict), "utf-8");
|
|
61
|
-
|
|
62
|
-
const result = await readVerdict(workdir);
|
|
63
|
-
expect(result).not.toBeNull();
|
|
64
|
-
expect(result?.version).toBe(1);
|
|
65
|
-
expect(result?.approved).toBe(true);
|
|
66
|
-
expect(result?.tests.allPassing).toBe(true);
|
|
67
|
-
expect(result?.tests.passCount).toBe(10);
|
|
68
|
-
expect(result?.tests.failCount).toBe(0);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("returns null when file does not exist (no throw)", async () => {
|
|
72
|
-
const result = await readVerdict(workdir);
|
|
73
|
-
expect(result).toBeNull();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("returns null when JSON is malformed (no throw, logs warning)", async () => {
|
|
77
|
-
await writeFile(path.join(workdir, ".nax-verifier-verdict.json"), "{ this is not valid json !!!", "utf-8");
|
|
78
|
-
|
|
79
|
-
const result = await readVerdict(workdir);
|
|
80
|
-
expect(result).toBeNull();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("returns null when required field 'version' is missing", async () => {
|
|
84
|
-
const bad = { approved: true, tests: { allPassing: true, passCount: 5, failCount: 0 } };
|
|
85
|
-
await writeFile(path.join(workdir, ".nax-verifier-verdict.json"), JSON.stringify(bad), "utf-8");
|
|
86
|
-
|
|
87
|
-
const result = await readVerdict(workdir);
|
|
88
|
-
expect(result).toBeNull();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("returns null when required field 'approved' is missing", async () => {
|
|
92
|
-
const bad = { version: 1, tests: { allPassing: true, passCount: 5, failCount: 0 } };
|
|
93
|
-
await writeFile(path.join(workdir, ".nax-verifier-verdict.json"), JSON.stringify(bad), "utf-8");
|
|
94
|
-
|
|
95
|
-
const result = await readVerdict(workdir);
|
|
96
|
-
expect(result).toBeNull();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it("returns null when required field 'tests' is missing", async () => {
|
|
100
|
-
const bad = { version: 1, approved: true };
|
|
101
|
-
await writeFile(path.join(workdir, ".nax-verifier-verdict.json"), JSON.stringify(bad), "utf-8");
|
|
102
|
-
|
|
103
|
-
const result = await readVerdict(workdir);
|
|
104
|
-
expect(result).toBeNull();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("returns null when tests sub-fields are missing", async () => {
|
|
108
|
-
const bad = { version: 1, approved: true, tests: { allPassing: true } };
|
|
109
|
-
await writeFile(path.join(workdir, ".nax-verifier-verdict.json"), JSON.stringify(bad), "utf-8");
|
|
110
|
-
|
|
111
|
-
const result = await readVerdict(workdir);
|
|
112
|
-
expect(result).toBeNull();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("returns null when version is not 1", async () => {
|
|
116
|
-
const bad = { version: 2, approved: true, tests: { allPassing: true, passCount: 5, failCount: 0 } };
|
|
117
|
-
await writeFile(path.join(workdir, ".nax-verifier-verdict.json"), JSON.stringify(bad), "utf-8");
|
|
118
|
-
|
|
119
|
-
const result = await readVerdict(workdir);
|
|
120
|
-
expect(result).toBeNull();
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("returns null for empty JSON object", async () => {
|
|
124
|
-
await writeFile(path.join(workdir, ".nax-verifier-verdict.json"), JSON.stringify({}), "utf-8");
|
|
125
|
-
|
|
126
|
-
const result = await readVerdict(workdir);
|
|
127
|
-
expect(result).toBeNull();
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("returns null for JSON array (not an object)", async () => {
|
|
131
|
-
await writeFile(path.join(workdir, ".nax-verifier-verdict.json"), JSON.stringify([1, 2, 3]), "utf-8");
|
|
132
|
-
|
|
133
|
-
const result = await readVerdict(workdir);
|
|
134
|
-
expect(result).toBeNull();
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
139
|
-
// cleanupVerdict
|
|
140
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
141
|
-
|
|
142
|
-
describe("cleanupVerdict", () => {
|
|
143
|
-
it("deletes the verdict file when it exists", async () => {
|
|
144
|
-
const verdictPath = path.join(workdir, ".nax-verifier-verdict.json");
|
|
145
|
-
await writeFile(verdictPath, JSON.stringify(makeVerdict()), "utf-8");
|
|
146
|
-
|
|
147
|
-
expect(existsSync(verdictPath)).toBe(true);
|
|
148
|
-
|
|
149
|
-
await cleanupVerdict(workdir);
|
|
150
|
-
|
|
151
|
-
expect(existsSync(verdictPath)).toBe(false);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it("does not throw when verdict file does not exist", async () => {
|
|
155
|
-
// Should not throw
|
|
156
|
-
await expect(cleanupVerdict(workdir)).resolves.toBeUndefined();
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("does not throw when called twice (second call is a no-op)", async () => {
|
|
160
|
-
const verdictPath = path.join(workdir, ".nax-verifier-verdict.json");
|
|
161
|
-
await writeFile(verdictPath, JSON.stringify(makeVerdict()), "utf-8");
|
|
162
|
-
|
|
163
|
-
await cleanupVerdict(workdir);
|
|
164
|
-
// Second call — file already gone
|
|
165
|
-
await expect(cleanupVerdict(workdir)).resolves.toBeUndefined();
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
170
|
-
// categorizeVerdict — with valid verdict
|
|
171
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
172
|
-
|
|
173
|
-
describe("categorizeVerdict — with verdict", () => {
|
|
174
|
-
it("returns success when approved=true", () => {
|
|
175
|
-
const result = categorizeVerdict(makeVerdict({ approved: true }), false);
|
|
176
|
-
expect(result.success).toBe(true);
|
|
177
|
-
expect(result.failureCategory).toBeUndefined();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it("returns success when approved=true regardless of testsPass", () => {
|
|
181
|
-
const result = categorizeVerdict(makeVerdict({ approved: true }), false);
|
|
182
|
-
expect(result.success).toBe(true);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it("returns verifier-rejected when illegitimate test modifications detected", () => {
|
|
186
|
-
const verdict = makeVerdict({
|
|
187
|
-
approved: false,
|
|
188
|
-
testModifications: {
|
|
189
|
-
detected: true,
|
|
190
|
-
files: ["test/foo.test.ts"],
|
|
191
|
-
legitimate: false,
|
|
192
|
-
reasoning: "Assertions loosened to hide failures",
|
|
193
|
-
},
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
const result = categorizeVerdict(verdict, true);
|
|
197
|
-
expect(result.success).toBe(false);
|
|
198
|
-
expect(result.failureCategory).toBe("verifier-rejected");
|
|
199
|
-
expect(result.reviewReason).toContain("illegitimate test modifications");
|
|
200
|
-
expect(result.reviewReason).toContain("test/foo.test.ts");
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("returns tests-failing when tests are not all passing", () => {
|
|
204
|
-
const verdict = makeVerdict({
|
|
205
|
-
approved: false,
|
|
206
|
-
tests: { allPassing: false, passCount: 8, failCount: 3 },
|
|
207
|
-
testModifications: { detected: false, files: [], legitimate: true, reasoning: "" },
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
const result = categorizeVerdict(verdict, false);
|
|
211
|
-
expect(result.success).toBe(false);
|
|
212
|
-
expect(result.failureCategory).toBe("tests-failing");
|
|
213
|
-
expect(result.reviewReason).toContain("3 failure(s)");
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it("returns verifier-rejected when acceptance criteria not met", () => {
|
|
217
|
-
const verdict = makeVerdict({
|
|
218
|
-
approved: false,
|
|
219
|
-
tests: { allPassing: true, passCount: 10, failCount: 0 },
|
|
220
|
-
testModifications: { detected: false, files: [], legitimate: true, reasoning: "" },
|
|
221
|
-
acceptanceCriteria: {
|
|
222
|
-
allMet: false,
|
|
223
|
-
criteria: [
|
|
224
|
-
{ criterion: "Feature works", met: true },
|
|
225
|
-
{ criterion: "Error handling", met: false },
|
|
226
|
-
],
|
|
227
|
-
},
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
const result = categorizeVerdict(verdict, true);
|
|
231
|
-
expect(result.success).toBe(false);
|
|
232
|
-
expect(result.failureCategory).toBe("verifier-rejected");
|
|
233
|
-
expect(result.reviewReason).toContain("Acceptance criteria not met");
|
|
234
|
-
expect(result.reviewReason).toContain("Error handling");
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
it("returns verifier-rejected when quality rating is poor", () => {
|
|
238
|
-
const verdict = makeVerdict({
|
|
239
|
-
approved: false,
|
|
240
|
-
tests: { allPassing: true, passCount: 10, failCount: 0 },
|
|
241
|
-
testModifications: { detected: false, files: [], legitimate: true, reasoning: "" },
|
|
242
|
-
acceptanceCriteria: {
|
|
243
|
-
allMet: true,
|
|
244
|
-
criteria: [{ criterion: "Feature works", met: true }],
|
|
245
|
-
},
|
|
246
|
-
quality: { rating: "poor", issues: ["Security vulnerability", "Memory leak"] },
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
const result = categorizeVerdict(verdict, true);
|
|
250
|
-
expect(result.success).toBe(false);
|
|
251
|
-
expect(result.failureCategory).toBe("verifier-rejected");
|
|
252
|
-
expect(result.reviewReason).toContain("Poor code quality");
|
|
253
|
-
expect(result.reviewReason).toContain("Security vulnerability");
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
it("returns verifier-rejected for catch-all (approved=false, no matching reason)", () => {
|
|
257
|
-
const verdict = makeVerdict({
|
|
258
|
-
approved: false,
|
|
259
|
-
tests: { allPassing: true, passCount: 10, failCount: 0 },
|
|
260
|
-
testModifications: { detected: false, files: [], legitimate: true, reasoning: "" },
|
|
261
|
-
acceptanceCriteria: { allMet: true, criteria: [] },
|
|
262
|
-
quality: { rating: "acceptable", issues: [] },
|
|
263
|
-
reasoning: "Something else went wrong",
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
const result = categorizeVerdict(verdict, true);
|
|
267
|
-
expect(result.success).toBe(false);
|
|
268
|
-
expect(result.failureCategory).toBe("verifier-rejected");
|
|
269
|
-
expect(result.reviewReason).toContain("Something else went wrong");
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
it("legitimate test modifications do not trigger verifier-rejected", () => {
|
|
273
|
-
const verdict = makeVerdict({
|
|
274
|
-
approved: false,
|
|
275
|
-
tests: { allPassing: false, passCount: 5, failCount: 2 },
|
|
276
|
-
testModifications: {
|
|
277
|
-
detected: true,
|
|
278
|
-
files: ["test/bar.test.ts"],
|
|
279
|
-
legitimate: true,
|
|
280
|
-
reasoning: "Tests updated to fix incorrect expected values",
|
|
281
|
-
},
|
|
282
|
-
acceptanceCriteria: { allMet: true, criteria: [] },
|
|
283
|
-
quality: { rating: "good", issues: [] },
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
// Should fall through to tests-failing (not illegitimate mod path)
|
|
287
|
-
const result = categorizeVerdict(verdict, false);
|
|
288
|
-
expect(result.failureCategory).toBe("tests-failing");
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
it("not-detected test modifications do not trigger verifier-rejected for test mods", () => {
|
|
292
|
-
const verdict = makeVerdict({
|
|
293
|
-
approved: false,
|
|
294
|
-
tests: { allPassing: false, passCount: 3, failCount: 4 },
|
|
295
|
-
testModifications: {
|
|
296
|
-
detected: false,
|
|
297
|
-
files: [],
|
|
298
|
-
legitimate: true,
|
|
299
|
-
reasoning: "",
|
|
300
|
-
},
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
const result = categorizeVerdict(verdict, false);
|
|
304
|
-
expect(result.failureCategory).toBe("tests-failing");
|
|
305
|
-
});
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
309
|
-
// categorizeVerdict — null verdict (fallback)
|
|
310
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
311
|
-
|
|
312
|
-
describe("categorizeVerdict — null verdict (fallback)", () => {
|
|
313
|
-
it("returns success when null verdict + testsPass=true", () => {
|
|
314
|
-
const result = categorizeVerdict(null, true);
|
|
315
|
-
expect(result.success).toBe(true);
|
|
316
|
-
expect(result.failureCategory).toBeUndefined();
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
it("returns tests-failing when null verdict + testsPass=false", () => {
|
|
320
|
-
const result = categorizeVerdict(null, false);
|
|
321
|
-
expect(result.success).toBe(false);
|
|
322
|
-
expect(result.failureCategory).toBe("tests-failing");
|
|
323
|
-
expect(result.reviewReason).toContain("no verdict file");
|
|
324
|
-
});
|
|
325
|
-
});
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// RE-ARCH: keep
|
|
2
|
-
import { describe, expect, test } from "bun:test";
|
|
3
|
-
import {
|
|
4
|
-
makeSkippedResult,
|
|
5
|
-
makePassResult,
|
|
6
|
-
makeFailResult,
|
|
7
|
-
type VerifyResult,
|
|
8
|
-
} from "../../../src/verification/orchestrator-types";
|
|
9
|
-
|
|
10
|
-
describe("makeSkippedResult", () => {
|
|
11
|
-
test("returns SKIPPED success result", () => {
|
|
12
|
-
const r = makeSkippedResult("US-001", "scoped");
|
|
13
|
-
expect(r.success).toBe(true);
|
|
14
|
-
expect(r.status).toBe("SKIPPED");
|
|
15
|
-
expect(r.storyId).toBe("US-001");
|
|
16
|
-
expect(r.strategy).toBe("scoped");
|
|
17
|
-
expect(r.countsTowardEscalation).toBe(false);
|
|
18
|
-
expect(r.failures).toEqual([]);
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
describe("makePassResult", () => {
|
|
23
|
-
test("returns PASS success result", () => {
|
|
24
|
-
const r = makePassResult("US-002", "regression", { passCount: 42, durationMs: 1234 });
|
|
25
|
-
expect(r.success).toBe(true);
|
|
26
|
-
expect(r.status).toBe("PASS");
|
|
27
|
-
expect(r.passCount).toBe(42);
|
|
28
|
-
expect(r.failCount).toBe(0);
|
|
29
|
-
expect(r.totalCount).toBe(42);
|
|
30
|
-
expect(r.durationMs).toBe(1234);
|
|
31
|
-
expect(r.countsTowardEscalation).toBe(false);
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe("makeFailResult", () => {
|
|
36
|
-
test("returns failure result with correct totals", () => {
|
|
37
|
-
const r = makeFailResult("US-003", "scoped", "TEST_FAILURE", {
|
|
38
|
-
passCount: 10,
|
|
39
|
-
failCount: 3,
|
|
40
|
-
durationMs: 5000,
|
|
41
|
-
});
|
|
42
|
-
expect(r.success).toBe(false);
|
|
43
|
-
expect(r.status).toBe("TEST_FAILURE");
|
|
44
|
-
expect(r.passCount).toBe(10);
|
|
45
|
-
expect(r.failCount).toBe(3);
|
|
46
|
-
expect(r.totalCount).toBe(13);
|
|
47
|
-
expect(r.countsTowardEscalation).toBe(true);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("timeout does not count toward escalation by default when overridden", () => {
|
|
51
|
-
const r = makeFailResult("US-004", "scoped", "TIMEOUT", { countsTowardEscalation: false });
|
|
52
|
-
expect(r.countsTowardEscalation).toBe(false);
|
|
53
|
-
});
|
|
54
|
-
});
|