@nathapp/nax 0.20.0 → 0.22.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.
Files changed (233) hide show
  1. package/.claude/settings.json +15 -0
  2. package/.mcp.json +8 -0
  3. package/docs/20260304-review-nax.md +492 -0
  4. package/docs/ROADMAP.md +65 -18
  5. package/docs/adr/ADR-005-implementation-plan.md +655 -0
  6. package/docs/adr/ADR-005-pipeline-re-architecture.md +464 -0
  7. package/docs/specs/bug-039-orphan-processes.md +131 -0
  8. package/docs/specs/bug-040-review-rectification.md +82 -0
  9. package/docs/specs/bug-041-cross-story-test-isolation.md +88 -0
  10. package/docs/specs/bug-042-verifier-failure-capture.md +117 -0
  11. package/docs/specs/feat-010-smart-runner-git-history.md +96 -0
  12. package/docs/specs/feat-011-file-context-strategy.md +73 -0
  13. package/docs/specs/feat-012-tdd-writer-tier.md +79 -0
  14. package/docs/specs/feat-013-test-after-review.md +89 -0
  15. package/docs/specs/feat-014-heartbeat-observability.md +127 -0
  16. package/memory/topic/feat-010-baseref.md +28 -0
  17. package/memory/topic/feat-013-test-after-deprecation.md +22 -0
  18. package/nax/config.json +7 -4
  19. package/nax/features/bug-039-medium/prd.json +45 -0
  20. package/package.json +2 -2
  21. package/src/agents/claude.ts +109 -15
  22. package/src/config/types.ts +11 -0
  23. package/src/context/builder.ts +9 -1
  24. package/src/execution/dry-run.ts +81 -0
  25. package/src/execution/escalation/tier-outcome.ts +29 -44
  26. package/src/execution/executor-types.ts +65 -0
  27. package/src/execution/index.ts +0 -17
  28. package/src/execution/iteration-runner.ts +132 -0
  29. package/src/execution/lifecycle/index.ts +0 -1
  30. package/src/execution/lifecycle/run-regression.ts +5 -5
  31. package/src/execution/pipeline-result-handler.ts +51 -254
  32. package/src/execution/sequential-executor.ts +72 -315
  33. package/src/execution/story-selector.ts +75 -0
  34. package/src/pipeline/event-bus.ts +276 -0
  35. package/src/pipeline/runner.ts +51 -77
  36. package/src/pipeline/stages/autofix.ts +133 -0
  37. package/src/pipeline/stages/completion.ts +22 -30
  38. package/src/pipeline/stages/index.ts +30 -13
  39. package/src/pipeline/stages/rectify.ts +93 -0
  40. package/src/pipeline/stages/regression.ts +88 -0
  41. package/src/pipeline/stages/review.ts +19 -153
  42. package/src/pipeline/stages/verify.ts +19 -3
  43. package/src/pipeline/subscribers/hooks.ts +133 -0
  44. package/src/pipeline/subscribers/interaction.ts +68 -0
  45. package/src/pipeline/subscribers/reporters.ts +174 -0
  46. package/src/pipeline/types.ts +12 -1
  47. package/src/review/orchestrator.ts +105 -0
  48. package/src/review/runner.ts +39 -4
  49. package/src/routing/router.ts +3 -3
  50. package/src/routing/strategies/keyword.ts +5 -2
  51. package/src/routing/strategies/llm.ts +27 -1
  52. package/src/tdd/prompts.ts +1 -1
  53. package/src/utils/git.ts +49 -25
  54. package/src/verification/executor.ts +8 -2
  55. package/src/verification/index.ts +1 -1
  56. package/src/verification/orchestrator-types.ts +145 -0
  57. package/src/verification/orchestrator.ts +76 -0
  58. package/src/{execution/post-verify-rectification.ts → verification/rectification-loop.ts} +13 -20
  59. package/src/verification/{gate.ts → runners.ts} +17 -105
  60. package/src/verification/smart-runner.ts +6 -10
  61. package/src/verification/strategies/acceptance.ts +133 -0
  62. package/src/verification/strategies/regression.ts +90 -0
  63. package/src/verification/strategies/scoped.ts +123 -0
  64. package/test/COVERAGE-GAPS.md +333 -0
  65. package/test/{acceptance → e2e}/cm-003-default-view.test.ts +1 -0
  66. package/test/{integration/e2e.test.ts → e2e/plan-analyze-run.test.ts} +1 -0
  67. package/test/integration/{agent-validation.test.ts → cli/agent-validation.test.ts} +3 -3
  68. package/test/integration/{cli-config-default-edge-cases.test.ts → cli/cli-config-default-edge-cases.test.ts} +6 -5
  69. package/test/integration/{cli-config-default-view.test.ts → cli/cli-config-default-view.test.ts} +8 -7
  70. package/test/integration/{cli-config-diff.test.ts → cli/cli-config-diff.test.ts} +3 -2
  71. package/test/integration/{cli-config.test.ts → cli/cli-config.test.ts} +3 -2
  72. package/test/integration/{cli-diagnose.test.ts → cli/cli-diagnose.test.ts} +5 -4
  73. package/test/integration/{cli-logs.test.ts → cli/cli-logs.test.ts} +12 -3
  74. package/test/integration/{cli-plugins.test.ts → cli/cli-plugins.test.ts} +4 -3
  75. package/test/integration/{cli-precheck.test.ts → cli/cli-precheck.test.ts} +4 -3
  76. package/test/integration/{cli-run-headless.test.ts → cli/cli-run-headless.test.ts} +3 -2
  77. package/test/integration/{cli.test.ts → cli/cli.test.ts} +2 -1
  78. package/test/integration/{precheck-integration.test.ts → cli/precheck-integration.test.ts} +10 -9
  79. package/test/integration/{precheck-orchestrator.test.ts → cli/precheck-orchestrator.test.ts} +4 -3
  80. package/test/integration/{precheck.test.ts → cli/precheck.test.ts} +5 -4
  81. package/test/integration/{config-loader.test.ts → config/config-loader.test.ts} +2 -1
  82. package/test/integration/{config.test.ts → config/config.test.ts} +2 -2
  83. package/test/integration/config/merger.test.ts +1 -0
  84. package/test/integration/config/paths.test.ts +1 -0
  85. package/test/integration/{security-loader.test.ts → config/security-loader.test.ts} +2 -2
  86. package/test/integration/{context-integration.test.ts → context/context-integration.test.ts} +7 -6
  87. package/test/integration/{path-security.test.ts → context/context-path-security.test.ts} +2 -2
  88. package/test/integration/{context-provider-injection.test.ts → context/context-provider-injection.test.ts} +7 -6
  89. package/test/integration/{context-verification-integration.test.ts → context/context-verification-integration.test.ts} +5 -4
  90. package/test/integration/{s5-greenfield-fallback.test.ts → context/s5-greenfield-fallback.test.ts} +4 -3
  91. package/test/integration/{isolation.test.ts → execution/execution-isolation.test.ts} +1 -1
  92. package/test/integration/{execution.test.ts → execution/execution.test.ts} +8 -8
  93. package/test/integration/{parallel.test.ts → execution/parallel.test.ts} +2 -1
  94. package/test/integration/{prd-pause.test.ts → execution/prd-pause.test.ts} +2 -2
  95. package/test/integration/{prd-resolvers.test.ts → execution/prd-resolvers.test.ts} +3 -2
  96. package/test/integration/{progress.test.ts → execution/progress.test.ts} +1 -1
  97. package/test/integration/execution/runner-batching.test.ts +682 -0
  98. package/test/integration/{runner-config-plugins.test.ts → execution/runner-config-plugins.test.ts} +3 -2
  99. package/test/integration/execution/runner-escalation.test.ts +561 -0
  100. package/test/integration/{runner-fixes.test.ts → execution/runner-fixes.test.ts} +4 -3
  101. package/test/integration/{runner-plugin-integration.test.ts → execution/runner-plugin-integration.test.ts} +6 -5
  102. package/test/integration/execution/runner-queue-and-attempts.test.ts +476 -0
  103. package/test/integration/{status-file-integration.test.ts → execution/status-file-integration.test.ts} +9 -8
  104. package/test/integration/{status-file.test.ts → execution/status-file.test.ts} +3 -2
  105. package/test/integration/{status-writer.test.ts → execution/status-writer.test.ts} +5 -4
  106. package/test/integration/{story-id-in-events.test.ts → execution/story-id-in-events.test.ts} +9 -8
  107. package/test/integration/{interaction-chain-pipeline.test.ts → interaction/interaction-chain-pipeline.test.ts} +26 -14
  108. package/test/integration/{hooks.test.ts → pipeline/hooks.test.ts} +4 -2
  109. package/test/integration/{pipeline-acceptance.test.ts → pipeline/pipeline-acceptance.test.ts} +7 -6
  110. package/test/integration/{pipeline-events.test.ts → pipeline/pipeline-events.test.ts} +7 -6
  111. package/test/integration/{pipeline.test.ts → pipeline/pipeline.test.ts} +9 -7
  112. package/test/integration/{reporter-lifecycle.test.ts → pipeline/reporter-lifecycle.test.ts} +9 -7
  113. package/test/integration/{verify-stage.test.ts → pipeline/verify-stage.test.ts} +7 -5
  114. package/test/integration/{analyze-integration.test.ts → plan/analyze-integration.test.ts} +3 -2
  115. package/test/integration/{analyze-scanner.test.ts → plan/analyze-scanner.test.ts} +8 -7
  116. package/test/integration/{logger.test.ts → plan/logger.test.ts} +1 -1
  117. package/test/integration/{plan.test.ts → plan/plan.test.ts} +3 -3
  118. package/test/integration/plugins/config-integration.test.ts +1 -0
  119. package/test/integration/plugins/config-resolution.test.ts +1 -0
  120. package/test/integration/plugins/loader.test.ts +1 -0
  121. package/test/integration/plugins/{registry.test.ts → plugins-registry.test.ts} +1 -0
  122. package/test/integration/plugins/validator.test.ts +1 -0
  123. package/test/integration/{review-config-commands.test.ts → review/review-config-commands.test.ts} +4 -3
  124. package/test/integration/{review-config-schema.test.ts → review/review-config-schema.test.ts} +3 -2
  125. package/test/integration/{review-plugin-integration.test.ts → review/review-plugin-integration.test.ts} +5 -4
  126. package/test/integration/{review.test.ts → review/review.test.ts} +3 -2
  127. package/test/integration/routing/plugin-routing-advanced.test.ts +461 -0
  128. package/test/integration/{plugin-routing.test.ts → routing/plugin-routing-core.test.ts} +10 -404
  129. package/test/integration/{routing-stage-bug-021.test.ts → routing/routing-stage-bug-021.test.ts} +8 -7
  130. package/test/integration/{routing-stage-greenfield.test.ts → routing/routing-stage-greenfield.test.ts} +7 -6
  131. package/test/integration/{tdd-cleanup.test.ts → tdd/tdd-cleanup.test.ts} +1 -1
  132. package/test/integration/tdd/tdd-orchestrator-core.test.ts +565 -0
  133. package/test/integration/tdd/tdd-orchestrator-failureCategory.test.ts +355 -0
  134. package/test/integration/tdd/tdd-orchestrator-fallback.test.ts +311 -0
  135. package/test/integration/tdd/tdd-orchestrator-lite.test.ts +289 -0
  136. package/test/integration/tdd/tdd-orchestrator-prompts.test.ts +260 -0
  137. package/test/integration/tdd/tdd-orchestrator-verdict.test.ts +536 -0
  138. package/test/integration/tmp/headless-test/test.jsonl +30 -0
  139. package/test/integration/{test-scanner.test.ts → verification/test-scanner.test.ts} +1 -1
  140. package/test/integration/{verification-asset-check.test.ts → verification/verification-asset-check.test.ts} +3 -2
  141. package/test/unit/acceptance.test.ts +1 -0
  142. package/test/unit/agent-stderr-capture.test.ts +1 -0
  143. package/test/unit/agents/claude.test.ts +107 -0
  144. package/test/unit/analyze-classifier.test.ts +1 -0
  145. package/test/unit/auto-detect.test.ts +1 -0
  146. package/test/unit/cli-status.test.ts +1 -0
  147. package/test/unit/commands/common.test.ts +1 -0
  148. package/test/unit/commands/logs.test.ts +1 -0
  149. package/test/unit/commands/unlock.test.ts +1 -0
  150. package/test/unit/config/defaults.test.ts +1 -0
  151. package/test/unit/config/regression-gate-schema.test.ts +1 -0
  152. package/test/unit/config/smart-runner-flag.test.ts +1 -0
  153. package/test/unit/constitution-generators.test.ts +1 -0
  154. package/test/unit/constitution.test.ts +1 -0
  155. package/test/unit/context/context-autodetect.test.ts +297 -0
  156. package/test/unit/context/context-build.test.ts +575 -0
  157. package/test/unit/context/context-coverage.test.ts +236 -0
  158. package/test/unit/context/context-error.test.ts +93 -0
  159. package/test/unit/context/context-estimate-tokens.test.ts +201 -0
  160. package/test/unit/context/context-format.test.ts +302 -0
  161. package/test/unit/context/context-isolation.test.ts +267 -0
  162. package/test/unit/context/context-sort.test.ts +93 -0
  163. package/test/unit/context/context-story.test.ts +108 -0
  164. package/test/{context → unit/context}/prior-failures.test.ts +5 -4
  165. package/test/unit/context.test.ts +7 -3
  166. package/test/unit/crash-recovery.test.ts +1 -0
  167. package/test/unit/escalation.test.ts +1 -0
  168. package/test/unit/execution/lifecycle/run-completion.test.ts +1 -0
  169. package/test/unit/execution/lifecycle/run-regression.test.ts +2 -0
  170. package/test/{execution → unit/execution}/pid-registry.test.ts +2 -1
  171. package/test/{execution → unit/execution}/structured-failure.test.ts +3 -2
  172. package/test/unit/execution-logging-stderr.test.ts +1 -0
  173. package/test/unit/execution-stage.test.ts +1 -0
  174. package/test/unit/fix-generator.test.ts +1 -0
  175. package/test/unit/greenfield.test.ts +1 -0
  176. package/test/unit/interaction/human-review-trigger.test.ts +1 -0
  177. package/test/unit/interaction-network-failures.test.ts +1 -0
  178. package/test/unit/interaction-plugins.test.ts +1 -0
  179. package/test/unit/logging/formatter.test.ts +1 -0
  180. package/test/unit/merge.test.ts +1 -0
  181. package/test/unit/pipeline/event-bus.test.ts +105 -0
  182. package/test/unit/pipeline/routing-partial-override.test.ts +1 -0
  183. package/test/unit/pipeline/runner-retry.test.ts +89 -0
  184. package/test/unit/pipeline/stages/autofix.test.ts +97 -0
  185. package/test/unit/pipeline/stages/rectify.test.ts +101 -0
  186. package/test/unit/pipeline/stages/regression-stage.test.ts +69 -0
  187. package/test/unit/pipeline/stages/verify.test.ts +1 -0
  188. package/test/unit/pipeline/subscribers/hooks.test.ts +45 -0
  189. package/test/unit/pipeline/subscribers/interaction.test.ts +31 -0
  190. package/test/unit/pipeline/subscribers/reporters.test.ts +90 -0
  191. package/test/unit/pipeline/verify-smart-runner.test.ts +2 -1
  192. package/test/unit/prd-auto-default.test.ts +3 -2
  193. package/test/unit/prd-failure-category.test.ts +1 -0
  194. package/test/unit/prd-get-next-story.test.ts +1 -0
  195. package/test/unit/precheck-checks.test.ts +1 -0
  196. package/test/unit/precheck-story-size-gate.test.ts +1 -0
  197. package/test/unit/precheck-types.test.ts +1 -0
  198. package/test/unit/prompts.test.ts +1 -0
  199. package/test/unit/rectification.test.ts +2 -1
  200. package/test/unit/registry.test.ts +1 -0
  201. package/test/unit/routing/routing-stability.test.ts +2 -1
  202. package/test/unit/routing/strategies/llm.test.ts +251 -0
  203. package/test/unit/routing-advanced.test.ts +313 -0
  204. package/test/unit/routing-core.test.ts +341 -0
  205. package/test/unit/routing-strategies.test.ts +442 -0
  206. package/test/unit/storyid-events.test.ts +1 -0
  207. package/test/{ui → unit/ui}/tui-controls.test.ts +8 -7
  208. package/test/{ui → unit/ui}/tui-cost-and-pty.test.ts +4 -3
  209. package/test/{ui → unit/ui}/tui-layout.test.ts +5 -4
  210. package/test/{ui → unit/ui}/tui-stories.test.ts +5 -4
  211. package/test/unit/{isolation.test.ts → unit-isolation.test.ts} +1 -0
  212. package/test/unit/{helpers.test.ts → utils-helpers.test.ts} +1 -0
  213. package/test/unit/verdict.test.ts +1 -0
  214. package/test/unit/verification/orchestrator-types.test.ts +54 -0
  215. package/test/unit/verification/orchestrator.test.ts +66 -0
  216. package/test/unit/verification/smart-runner-config.test.ts +1 -0
  217. package/test/unit/verification/smart-runner-discovery.test.ts +8 -7
  218. package/test/unit/verification/strategies/acceptance.test.ts +33 -0
  219. package/test/unit/verification/strategies/regression.test.ts +87 -0
  220. package/test/unit/verification/strategies/scoped.test.ts +100 -0
  221. package/test/unit/worktree-manager.test.ts +1 -0
  222. package/src/execution/lifecycle/story-hooks.ts +0 -38
  223. package/src/execution/post-verify.ts +0 -193
  224. package/src/execution/rectification.ts +0 -13
  225. package/src/execution/verification.ts +0 -72
  226. package/test/integration/rectification-flow.test.ts +0 -512
  227. package/test/integration/runner.test.ts +0 -1679
  228. package/test/integration/tdd-orchestrator.test.ts +0 -1762
  229. package/test/unit/execution/post-verify-regression.test.ts +0 -362
  230. package/test/unit/execution/post-verify.test.ts +0 -236
  231. package/test/unit/routing.test.ts +0 -1039
  232. /package/test/{integration → helpers}/helpers.test.ts +0 -0
  233. /package/test/integration/worktree/{merge.test.ts → worktree-merge.test.ts} +0 -0
@@ -1,512 +0,0 @@
1
- /**
2
- * Integration tests for rectification flow (v0.11)
3
- *
4
- * Tests the E2E rectification flow:
5
- * - Scoped tests pass -> Full suite fails -> Rectification prompt sent -> Fix applied -> Full suite passes
6
- */
7
-
8
- import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
9
-
10
- // Skip rectification flow integration tests in CI: these tests spawn real agent
11
- // subprocesses via runPostAgentVerification which invokes bun test internally.
12
- // In CI the subprocess environment differs (no claude binary, different PATH,
13
- // container file system limits), causing hangs or unexpected failures unrelated
14
- // to the rectification logic itself. Run these locally or on a full-env runner.
15
- const skipInCI = process.env.CI ? test.skip : test;
16
- import { ALL_AGENTS } from "../../src/agents/registry";
17
- import { DEFAULT_CONFIG } from "../../src/config";
18
- import { runPostAgentVerification } from "../../src/execution/post-verify";
19
- import type { PostVerifyOptions } from "../../src/execution/post-verify";
20
- import { initLogger, resetLogger } from "../../src/logger";
21
- import type { StoryMetrics } from "../../src/metrics";
22
- import type { PRD, UserStory } from "../../src/prd";
23
-
24
- // Mock agent adapter for testing
25
- const createMockAgent = () => ({
26
- name: "mock-agent",
27
- displayName: "Mock Agent",
28
- binary: "mock",
29
- capabilities: {
30
- supportedTiers: ["fast", "balanced", "powerful"],
31
- maxContextTokens: 100000,
32
- features: new Set(["tdd", "review", "refactor"]),
33
- },
34
- isInstalled: async () => true,
35
- run: mock(async () => ({
36
- success: true,
37
- estimatedCost: 0.01,
38
- pid: undefined,
39
- })),
40
- buildCommand: () => ["mock", "command"],
41
- });
42
-
43
- // Create test PRD
44
- const createTestPRD = (story: Partial<UserStory>): PRD => ({
45
- project: "test-project",
46
- feature: "test-feature",
47
- branchName: "test-branch",
48
- createdAt: new Date().toISOString(),
49
- updatedAt: new Date().toISOString(),
50
- userStories: [
51
- {
52
- id: story.id || "US-001",
53
- title: story.title || "Test Story",
54
- description: story.description || "Test description",
55
- acceptanceCriteria: story.acceptanceCriteria || ["AC1"],
56
- dependencies: story.dependencies || [],
57
- tags: story.tags || [],
58
- status: story.status || "pending",
59
- passes: story.passes ?? false,
60
- escalations: story.escalations || [],
61
- attempts: story.attempts || 0,
62
- routing: story.routing,
63
- },
64
- ],
65
- });
66
-
67
- describe("rectification flow (integration)", () => {
68
- let tmpDir: string;
69
-
70
- beforeEach(async () => {
71
- initLogger({ level: "error", useChalk: false });
72
- tmpDir = `/tmp/nax-rectify-test-${Date.now()}`;
73
- await Bun.spawn(["mkdir", "-p", tmpDir], { stdout: "pipe" }).exited;
74
- });
75
-
76
- afterEach(async () => {
77
- resetLogger();
78
- await Bun.spawn(["rm", "-rf", tmpDir], { stdout: "pipe" }).exited;
79
- });
80
-
81
- test("should skip rectification when disabled in config", async () => {
82
- const story: UserStory = {
83
- id: "US-001",
84
- title: "Test Story",
85
- description: "Test",
86
- acceptanceCriteria: ["AC1"],
87
- dependencies: [],
88
- tags: [],
89
- status: "pending",
90
- passes: false,
91
- escalations: [],
92
- attempts: 0,
93
- };
94
-
95
- const prd = createTestPRD(story);
96
- const prdPath = `${tmpDir}/prd.json`;
97
- await Bun.write(prdPath, JSON.stringify(prd, null, 2));
98
-
99
- const config = {
100
- ...DEFAULT_CONFIG,
101
- execution: {
102
- ...DEFAULT_CONFIG.execution,
103
- rectification: {
104
- enabled: false, // Disabled
105
- maxRetries: 2,
106
- fullSuiteTimeoutSeconds: 120,
107
- maxFailureSummaryChars: 2000,
108
- abortOnIncreasingFailures: true,
109
- },
110
- },
111
- quality: {
112
- ...DEFAULT_CONFIG.quality,
113
- commands: {
114
- test: "exit 1", // Fail immediately
115
- },
116
- },
117
- };
118
-
119
- const opts: PostVerifyOptions = {
120
- config,
121
- prd,
122
- prdPath,
123
- workdir: tmpDir,
124
- story,
125
- storiesToExecute: [story],
126
- allStoryMetrics: [] as StoryMetrics[],
127
- timeoutRetryCountMap: new Map(),
128
- };
129
-
130
- // Register mock agent
131
- const mockAgent = createMockAgent();
132
- ALL_AGENTS.push(mockAgent);
133
- const cleanup = () => {
134
- const idx = ALL_AGENTS.findIndex((a) => a.name === "mock-agent");
135
- if (idx !== -1) ALL_AGENTS.splice(idx, 1);
136
- };
137
-
138
- try {
139
- const result = await runPostAgentVerification(opts);
140
-
141
- // Should fail without attempting rectification
142
- expect(result.passed).toBe(false);
143
- expect(mockAgent.run).not.toHaveBeenCalled();
144
- } finally {
145
- cleanup();
146
- }
147
- });
148
-
149
- test.skip("should attempt rectification when enabled and tests fail", async () => {
150
- const story: UserStory = {
151
- id: "US-001",
152
- title: "Test Story",
153
- description: "Test",
154
- acceptanceCriteria: ["AC1"],
155
- dependencies: [],
156
- tags: [],
157
- status: "pending",
158
- passes: false,
159
- escalations: [],
160
- attempts: 0,
161
- routing: {
162
- complexity: "simple",
163
- modelTier: "fast",
164
- testStrategy: "test-after",
165
- reasoning: "Test",
166
- },
167
- };
168
-
169
- const prd = createTestPRD(story);
170
- const prdPath = `${tmpDir}/prd.json`;
171
- await Bun.write(prdPath, JSON.stringify(prd, null, 2));
172
-
173
- // Create a fake test output with failures
174
- const failedTestOutput = `
175
- test/example.test.ts:
176
- ✗ failing test [1.2ms]
177
-
178
- (fail) should work correctly [1.2ms]
179
- Error: Expected 1 to equal 2
180
- at test/example.test.ts:10:15
181
- at Object.test (test/example.test.ts:8:3)
182
-
183
- 1 test passed
184
- 1 test failed
185
- `;
186
-
187
- // Create a script that returns failed output first, then success
188
- const callCount = 0;
189
- const testScript = `${tmpDir}/test.sh`;
190
- await Bun.write(
191
- testScript,
192
- `#!/bin/bash
193
- if [ -f ${tmpDir}/.rectify-attempt ]; then
194
- echo "✓ all tests passed"
195
- exit 0
196
- else
197
- cat <<'EOF'
198
- ${failedTestOutput}
199
- EOF
200
- exit 1
201
- fi
202
- `,
203
- );
204
- await Bun.spawn(["chmod", "+x", testScript], { stdout: "pipe" }).exited;
205
-
206
- const config = {
207
- ...DEFAULT_CONFIG,
208
- execution: {
209
- ...DEFAULT_CONFIG.execution,
210
- rectification: {
211
- enabled: true,
212
- maxRetries: 2,
213
- fullSuiteTimeoutSeconds: 120,
214
- maxFailureSummaryChars: 2000,
215
- abortOnIncreasingFailures: true,
216
- },
217
- },
218
- quality: {
219
- ...DEFAULT_CONFIG.quality,
220
- commands: {
221
- test: testScript,
222
- },
223
- },
224
- autoMode: {
225
- ...DEFAULT_CONFIG.autoMode,
226
- defaultAgent: "mock-agent",
227
- },
228
- };
229
-
230
- const opts: PostVerifyOptions = {
231
- config,
232
- prd,
233
- prdPath,
234
- workdir: tmpDir,
235
- story,
236
- storiesToExecute: [story],
237
- allStoryMetrics: [] as StoryMetrics[],
238
- timeoutRetryCountMap: new Map(),
239
- };
240
-
241
- // Register mock agent that creates the marker file when called
242
- const mockAgent = createMockAgent();
243
- mockAgent.run = mock(async () => {
244
- // Simulate agent fixing the issue
245
- await Bun.write(`${tmpDir}/.rectify-attempt`, "1");
246
- return {
247
- success: true,
248
- estimatedCost: 0.01,
249
- pid: undefined,
250
- };
251
- });
252
- ALL_AGENTS.push(mockAgent);
253
- const cleanup = () => {
254
- const idx = ALL_AGENTS.findIndex((a) => a.name === "mock-agent");
255
- if (idx !== -1) ALL_AGENTS.splice(idx, 1);
256
- };
257
-
258
- try {
259
- const result = await runPostAgentVerification(opts);
260
-
261
- // Should pass after rectification
262
- expect(result.passed).toBe(false) // Fixed: post-verify no longer rectifies;
263
- expect(mockAgent.run).toHaveBeenCalled();
264
- expect(mockAgent.run).toHaveBeenCalledTimes(1);
265
- } finally {
266
- cleanup();
267
- }
268
- });
269
-
270
- test.skip("should abort rectification if failures increase", async () => {
271
- const story: UserStory = {
272
- id: "US-001",
273
- title: "Test Story",
274
- description: "Test",
275
- acceptanceCriteria: ["AC1"],
276
- dependencies: [],
277
- tags: [],
278
- status: "pending",
279
- passes: false,
280
- escalations: [],
281
- attempts: 0,
282
- routing: {
283
- complexity: "simple",
284
- modelTier: "fast",
285
- testStrategy: "test-after",
286
- reasoning: "Test",
287
- },
288
- };
289
-
290
- const prd = createTestPRD(story);
291
- const prdPath = `${tmpDir}/prd.json`;
292
- await Bun.write(prdPath, JSON.stringify(prd, null, 2));
293
-
294
- // First failure: 2 tests fail
295
- const initialFailures = `
296
- test/example.test.ts:
297
- ✗ test 1 [1ms]
298
- ✗ test 2 [1ms]
299
-
300
- (fail) test 1 [1ms]
301
- Error: Failed
302
-
303
- (fail) test 2 [1ms]
304
- Error: Failed
305
-
306
- 2 tests failed
307
- `;
308
-
309
- // After rectification: 3 tests fail (regression!)
310
- const regressedFailures = `
311
- test/example.test.ts:
312
- ✗ test 1 [1ms]
313
- ✗ test 2 [1ms]
314
- ✗ test 3 [1ms]
315
-
316
- (fail) test 1 [1ms]
317
- Error: Failed
318
-
319
- (fail) test 2 [1ms]
320
- Error: Failed
321
-
322
- (fail) test 3 [1ms]
323
- Error: Failed
324
-
325
- 3 tests failed
326
- `;
327
-
328
- const callCount = 0;
329
- const testScript = `${tmpDir}/test.sh`;
330
- await Bun.write(
331
- testScript,
332
- `#!/bin/bash
333
- if [ -f ${tmpDir}/.rectify-attempt ]; then
334
- cat <<'EOF'
335
- ${regressedFailures}
336
- EOF
337
- exit 1
338
- else
339
- cat <<'EOF'
340
- ${initialFailures}
341
- EOF
342
- exit 1
343
- fi
344
- `,
345
- );
346
- await Bun.spawn(["chmod", "+x", testScript], { stdout: "pipe" }).exited;
347
-
348
- const config = {
349
- ...DEFAULT_CONFIG,
350
- execution: {
351
- ...DEFAULT_CONFIG.execution,
352
- rectification: {
353
- enabled: true,
354
- maxRetries: 2,
355
- fullSuiteTimeoutSeconds: 120,
356
- maxFailureSummaryChars: 2000,
357
- abortOnIncreasingFailures: true, // Abort on regression
358
- },
359
- },
360
- quality: {
361
- ...DEFAULT_CONFIG.quality,
362
- commands: {
363
- test: testScript,
364
- },
365
- },
366
- autoMode: {
367
- ...DEFAULT_CONFIG.autoMode,
368
- defaultAgent: "mock-agent",
369
- },
370
- };
371
-
372
- const opts: PostVerifyOptions = {
373
- config,
374
- prd,
375
- prdPath,
376
- workdir: tmpDir,
377
- story,
378
- storiesToExecute: [story],
379
- allStoryMetrics: [] as StoryMetrics[],
380
- timeoutRetryCountMap: new Map(),
381
- };
382
-
383
- // Register mock agent that creates marker file
384
- const mockAgent = createMockAgent();
385
- mockAgent.run = mock(async () => {
386
- await Bun.write(`${tmpDir}/.rectify-attempt`, "1");
387
- return {
388
- success: true,
389
- estimatedCost: 0.01,
390
- pid: undefined,
391
- };
392
- });
393
- ALL_AGENTS.push(mockAgent);
394
- const cleanup = () => {
395
- const idx = ALL_AGENTS.findIndex((a) => a.name === "mock-agent");
396
- if (idx !== -1) ALL_AGENTS.splice(idx, 1);
397
- };
398
-
399
- try {
400
- const result = await runPostAgentVerification(opts);
401
-
402
- // Should fail after aborting due to regression
403
- expect(result.passed).toBe(false);
404
- // Should only attempt once before detecting regression
405
- expect(mockAgent.run).toHaveBeenCalledTimes(1);
406
- } finally {
407
- cleanup();
408
- }
409
- });
410
-
411
- skipInCI("should respect maxRetries limit", async () => {
412
- const story: UserStory = {
413
- id: "US-001",
414
- title: "Test Story",
415
- description: "Test",
416
- acceptanceCriteria: ["AC1"],
417
- dependencies: [],
418
- tags: [],
419
- status: "pending",
420
- passes: false,
421
- escalations: [],
422
- attempts: 0,
423
- routing: {
424
- complexity: "simple",
425
- modelTier: "fast",
426
- testStrategy: "test-after",
427
- reasoning: "Test",
428
- },
429
- };
430
-
431
- const prd = createTestPRD(story);
432
- const prdPath = `${tmpDir}/prd.json`;
433
- await Bun.write(prdPath, JSON.stringify(prd, null, 2));
434
-
435
- const failedTestOutput = `
436
- test/example.test.ts:
437
- ✗ failing test [1ms]
438
-
439
- (fail) failing test [1ms]
440
- Error: Failed
441
-
442
- 1 test failed
443
- `;
444
-
445
- // Always fail
446
- const testScript = `${tmpDir}/test.sh`;
447
- await Bun.write(
448
- testScript,
449
- `#!/bin/bash
450
- cat <<'EOF'
451
- ${failedTestOutput}
452
- EOF
453
- exit 1
454
- `,
455
- );
456
- await Bun.spawn(["chmod", "+x", testScript], { stdout: "pipe" }).exited;
457
-
458
- const config = {
459
- ...DEFAULT_CONFIG,
460
- execution: {
461
- ...DEFAULT_CONFIG.execution,
462
- rectification: {
463
- enabled: true,
464
- maxRetries: 2, // Only 2 retries
465
- fullSuiteTimeoutSeconds: 120,
466
- maxFailureSummaryChars: 2000,
467
- abortOnIncreasingFailures: false, // Don't abort on regression
468
- },
469
- },
470
- quality: {
471
- ...DEFAULT_CONFIG.quality,
472
- commands: {
473
- test: testScript,
474
- },
475
- },
476
- autoMode: {
477
- ...DEFAULT_CONFIG.autoMode,
478
- defaultAgent: "mock-agent",
479
- },
480
- };
481
-
482
- const opts: PostVerifyOptions = {
483
- config,
484
- prd,
485
- prdPath,
486
- workdir: tmpDir,
487
- story,
488
- storiesToExecute: [story],
489
- allStoryMetrics: [] as StoryMetrics[],
490
- timeoutRetryCountMap: new Map(),
491
- };
492
-
493
- // Register mock agent
494
- const mockAgent = createMockAgent();
495
- ALL_AGENTS.push(mockAgent);
496
- const cleanup = () => {
497
- const idx = ALL_AGENTS.findIndex((a) => a.name === "mock-agent");
498
- if (idx !== -1) ALL_AGENTS.splice(idx, 1);
499
- };
500
-
501
- try {
502
- const result = await runPostAgentVerification(opts);
503
-
504
- // Should fail after exhausting retries
505
- expect(result.passed).toBe(false);
506
- // Should call agent exactly maxRetries times
507
- expect(mockAgent.run).toHaveBeenCalledTimes(2);
508
- } finally {
509
- cleanup();
510
- }
511
- });
512
- });