@nathapp/nax 0.19.0 → 0.20.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/docs/ROADMAP.md CHANGED
@@ -224,6 +224,8 @@
224
224
  - [x] **BUG-032:** Routing stage overrides escalated `modelTier` with complexity-derived tier. `src/pipeline/stages/routing.ts:43` always runs `complexityToModelTier(routing.complexity, config)` even when `story.routing.modelTier` was explicitly set by `handleTierEscalation()`. BUG-026 was escalated to `balanced` (logged in iteration header), but `Task classified` shows `modelTier=fast` because `complexityToModelTier("simple", config)` → `"fast"`. Related to BUG-013 (escalation routing not applied) which was marked fixed, but the fix in `applyCachedRouting()` in `pipeline-result-handler.ts:295-310` runs **after** the routing stage — too late. **Location:** `src/pipeline/stages/routing.ts:43`. **Fix:** When `story.routing.modelTier` is explicitly set (by escalation), skip `complexityToModelTier()` and use the cached tier directly. Only derive from complexity when `story.routing.modelTier` is absent.
225
225
  - [x] **BUG-033:** LLM routing has no retry on timeout — single attempt with hardcoded 15s default. All 5 LLM routing attempts in the v0.18.3 run timed out at 15s, forcing keyword fallback every time. `src/routing/strategies/llm.ts:63` reads `llmConfig?.timeoutMs ?? 15000` but there's no retry logic — one timeout = immediate fallback. **Location:** `src/routing/strategies/llm.ts:callLlm()`. **Fix:** Add `routing.llm.retries` config (default: 1) with backoff. Also surface `routing.llm.timeoutMs` in `nax config --explain` and consider raising default to 30s for batch routing which processes multiple stories.
226
226
 
227
+ - [ ] **BUG-037:** Test output summary (verify stage) captures precheck boilerplate instead of actual `bun test` failure. **Symptom:** Logs show successful prechecks (Head) instead of failed tests (Tail). **Fix:** Change `Test output preview` log to tail the last 20 lines of output instead of heading the first 10.
228
+ - [ ] **BUG-038:** `smart-runner` over-matching when global defaults change. **Symptom:** Changing `DEFAULT_CONFIG` matches broad integration tests that fail due to environment/precheck side effects, obscuring targeted results. **Fix:** Refine path mapping to prioritize direct unit tests and exclude known heavy integration tests from default smart-runner matches unless explicitly relevant.
227
229
  ### Features
228
230
  - [x] ~~`nax unlock` command~~
229
231
  - [x] ~~Constitution file support~~
package/nax/config.json CHANGED
@@ -59,7 +59,7 @@
59
59
  "maxIterations": 6,
60
60
  "iterationDelayMs": 2000,
61
61
  "costLimit": 8.0,
62
- "sessionTimeoutSeconds": 600,
62
+ "sessionTimeoutSeconds": 7200,
63
63
  "verificationTimeoutSeconds": 300,
64
64
  "maxStoriesPerFeature": 15,
65
65
  "rectification": {
@@ -147,4 +147,4 @@
147
147
  "scopeToStory": true
148
148
  }
149
149
  }
150
- }
150
+ }
@@ -0,0 +1,79 @@
1
+ {
2
+ "project": "nax",
3
+ "branchName": "feat/v0.20.0-verify-v2",
4
+ "feature": "verify-v2",
5
+ "userStories": [
6
+ {
7
+ "id": "US-001",
8
+ "title": "Remove test from review defaults",
9
+ "description": "Change review.checks default from ['typecheck', 'lint', 'test'] to ['typecheck', 'lint'] in src/config/defaults.ts. The test check in review duplicates the pipeline verify stage. Keep 'test' as a valid enum value in the schema for backwards compatibility but remove it from the default config. Update any tests that assert on the default review checks array.",
10
+ "complexity": "simple",
11
+ "status": "passed",
12
+ "attempts": 0,
13
+ "priorErrors": [
14
+ "Attempt 1 failed with model tier: balanced: Stage requested escalation to higher tier"
15
+ ],
16
+ "priorFailures": [
17
+ {
18
+ "attempt": 1,
19
+ "modelTier": "balanced",
20
+ "stage": "escalation",
21
+ "summary": "Failed with tier balanced, escalating to next tier",
22
+ "timestamp": "2026-03-05T11:14:42.773Z"
23
+ }
24
+ ],
25
+ "escalations": [],
26
+ "dependencies": [],
27
+ "tags": [],
28
+ "acceptanceCriteria": [],
29
+ "storyPoints": 1,
30
+ "routing": {
31
+ "complexity": "simple",
32
+ "modelTier": "powerful",
33
+ "testStrategy": "test-after",
34
+ "reasoning": "override: simple config default change, tests already exist"
35
+ },
36
+ "passes": true
37
+ },
38
+ {
39
+ "id": "US-002",
40
+ "title": "Remove post-verify scoped duplicate",
41
+ "description": "In src/execution/post-verify.ts, remove the scoped verification logic (getChangedTestFiles + runVerification for scoped tests) from runPostAgentVerification(). The pipeline verify stage already runs Smart Test Runner scoped tests. post-verify should ONLY run the regression gate (full suite) and handle failure revert with StructuredFailure. Remove getChangedTestFiles() and scopeTestCommand() helper functions. Remove the scoped rectification loop call. Update the function signature to no longer need storyGitRef. Update all tests for post-verify accordingly.",
42
+ "complexity": "medium",
43
+ "status": "passed",
44
+ "attempts": 0,
45
+ "priorErrors": [],
46
+ "priorFailures": [],
47
+ "escalations": [],
48
+ "dependencies": [],
49
+ "tags": [],
50
+ "acceptanceCriteria": [],
51
+ "storyPoints": 1,
52
+ "passes": true
53
+ },
54
+ {
55
+ "id": "US-003",
56
+ "title": "Deferred regression gate",
57
+ "description": "Create new src/execution/lifecycle/run-regression.ts that implements a deferred regression gate. Instead of running the full test suite after every story, run it once after all stories complete. Steps: (1) Add 'mode' field to RegressionGateConfigSchema with values 'deferred' | 'per-story' | 'disabled' (default: 'deferred'). (2) Add 'maxRectificationAttempts' field (default: 2). (3) In run-regression.ts: run full suite once, parse failures, use reverse Smart Test Runner mapping (test file -> source file -> responsible story via git log), attempt targeted rectification per responsible story, re-run full suite to confirm. (4) Call deferred regression from run-completion.ts before final metrics, only when mode is 'deferred'. (5) When mode is 'deferred', skip the per-story regression gate in post-verify.ts. (6) Add reverseMapTestToSource() to smart-runner.ts. (7) Handle edge cases: partial completion (only check passed stories), overlapping file changes (try last story first), unmapped tests (warn and mark all passed stories for re-verification).",
58
+ "complexity": "complex",
59
+ "status": "passed",
60
+ "attempts": 0,
61
+ "priorErrors": [],
62
+ "priorFailures": [],
63
+ "escalations": [],
64
+ "dependencies": [],
65
+ "tags": [],
66
+ "acceptanceCriteria": [],
67
+ "storyPoints": 1,
68
+ "routing": {
69
+ "complexity": "complex",
70
+ "modelTier": "balanced",
71
+ "testStrategy": "three-session-tdd-lite",
72
+ "reasoning": "override: complex new file + schema changes, needs powerful model"
73
+ },
74
+ "failureCategory": "session-failure",
75
+ "passes": true
76
+ }
77
+ ],
78
+ "updatedAt": "2026-03-05T11:58:46.858Z"
79
+ }
@@ -0,0 +1,3 @@
1
+ [2026-03-05T07:05:07.935Z] US-002 — PASSED — Remove post-verify scoped duplicate — Cost: $0.1170
2
+ [2026-03-05T08:08:59.656Z] US-003 — FAILED — Deferred regression gate — Execution failed
3
+ [2026-03-05T08:13:50.586Z] US-001 — FAILED — Remove test from review defaults — Execution failed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "AI Coding Agent Orchestrator \u2014 loops until done",
5
5
  "type": "module",
6
6
  "bin": {
@@ -67,6 +67,7 @@ export const DEFAULT_CONFIG: NaxConfig = {
67
67
  enabled: true,
68
68
  timeoutSeconds: 120,
69
69
  acceptOnTimeout: true,
70
+ maxRectificationAttempts: 2,
70
71
  },
71
72
  contextProviderTokenBudget: 2000,
72
73
  smartTestRunner: true,
@@ -113,7 +114,7 @@ export const DEFAULT_CONFIG: NaxConfig = {
113
114
  },
114
115
  review: {
115
116
  enabled: true,
116
- checks: ["typecheck", "lint", "test"],
117
+ checks: ["typecheck", "lint"],
117
118
  commands: {},
118
119
  },
119
120
  plan: {
@@ -63,6 +63,8 @@ const RegressionGateConfigSchema = z.object({
63
63
  enabled: z.boolean().default(true),
64
64
  timeoutSeconds: z.number().int().min(10).max(600).default(120),
65
65
  acceptOnTimeout: z.boolean().default(true),
66
+ mode: z.enum(["deferred", "per-story", "disabled"]).default("deferred"),
67
+ maxRectificationAttempts: z.number().int().min(1).default(2),
66
68
  });
67
69
 
68
70
  const SmartTestRunnerConfigSchema = z.object({
@@ -78,6 +78,10 @@ export interface RegressionGateConfig {
78
78
  timeoutSeconds: number;
79
79
  /** Accept timeout as pass instead of failing (BUG-026, default: true) */
80
80
  acceptOnTimeout?: boolean;
81
+ /** Mode of regression gate: 'deferred' (run once after all stories), 'per-story' (run after each story), 'disabled' (default: 'deferred') */
82
+ mode?: "deferred" | "per-story" | "disabled";
83
+ /** Max rectification attempts for deferred regression gate (default: 2) */
84
+ maxRectificationAttempts?: number;
81
85
  }
82
86
 
83
87
  /** Smart test runner configuration (STR-007) */
@@ -9,3 +9,4 @@ export { handleParallelCompletion, type ParallelCompletionOptions } from "./para
9
9
  export { handleRunCompletion, type RunCompletionOptions, type RunCompletionResult } from "./run-completion";
10
10
  export { cleanupRun, type RunCleanupOptions } from "./run-cleanup";
11
11
  export { setupRun, type RunSetupOptions, type RunSetupResult } from "./run-setup";
12
+ export { runDeferredRegression, type DeferredRegressionOptions, type DeferredRegressionResult } from "./run-regression";
@@ -2,17 +2,28 @@
2
2
  * Run Completion — Final Metrics and Status Updates
3
3
  *
4
4
  * Handles the final steps after sequential execution completes:
5
+ * - Run deferred regression gate (if configured)
5
6
  * - Save run metrics
6
7
  * - Log completion summary with per-story metrics
7
8
  * - Update final status
8
9
  */
9
10
 
11
+ import type { NaxConfig } from "../../config";
10
12
  import { getSafeLogger } from "../../logger";
11
13
  import type { StoryMetrics } from "../../metrics";
12
14
  import { saveRunMetrics } from "../../metrics";
13
15
  import { countStories, isComplete, isStalled } from "../../prd";
14
16
  import type { PRD } from "../../prd";
15
17
  import type { StatusWriter } from "../status-writer";
18
+ import { runDeferredRegression } from "./run-regression";
19
+
20
+ /**
21
+ * Injectable dependencies for testing (avoids mock.module() which leaks in Bun 1.x).
22
+ * @internal - test use only.
23
+ */
24
+ export const _runCompletionDeps = {
25
+ runDeferredRegression,
26
+ };
16
27
 
17
28
  export interface RunCompletionOptions {
18
29
  runId: string;
@@ -26,6 +37,7 @@ export interface RunCompletionOptions {
26
37
  startTime: number;
27
38
  workdir: string;
28
39
  statusWriter: StatusWriter;
40
+ config: NaxConfig;
29
41
  }
30
42
 
31
43
  export interface RunCompletionResult {
@@ -57,8 +69,25 @@ export async function handleRunCompletion(options: RunCompletionOptions): Promis
57
69
  startTime,
58
70
  workdir,
59
71
  statusWriter,
72
+ config,
60
73
  } = options;
61
74
 
75
+ // Run deferred regression gate before final metrics
76
+ const regressionMode = config.execution.regressionGate?.mode;
77
+ if (regressionMode === "deferred" && config.quality.commands.test) {
78
+ const regressionResult = await _runCompletionDeps.runDeferredRegression({
79
+ config,
80
+ prd,
81
+ workdir,
82
+ });
83
+
84
+ logger?.info("regression", "Deferred regression gate completed", {
85
+ success: regressionResult.success,
86
+ failedTests: regressionResult.failedTests,
87
+ affectedStories: regressionResult.affectedStories,
88
+ });
89
+ }
90
+
62
91
  const durationMs = Date.now() - startTime;
63
92
  const runCompletedAt = new Date().toISOString();
64
93
 
@@ -0,0 +1,301 @@
1
+ /**
2
+ * Deferred Regression Gate
3
+ *
4
+ * Runs full test suite once after all stories complete, then attempts
5
+ * targeted rectification per responsible story. Handles edge cases:
6
+ * - Partial completion: only check stories marked passed
7
+ * - Overlapping file changes: try last modified story first
8
+ * - Unmapped tests: warn and mark all passed stories for re-verification
9
+ */
10
+
11
+ import type { NaxConfig } from "../../config";
12
+ import { getSafeLogger } from "../../logger";
13
+ import type { PRD, UserStory } from "../../prd";
14
+ import { countStories } from "../../prd";
15
+ import { hasCommitsForStory } from "../../utils/git";
16
+ import { parseBunTestOutput } from "../../verification";
17
+ import { reverseMapTestToSource } from "../../verification/smart-runner";
18
+ import { runRectificationLoop } from "../post-verify-rectification";
19
+ import { runVerification } from "../verification";
20
+
21
+ /**
22
+ * Injectable dependencies for testing (avoids mock.module() which leaks in Bun 1.x).
23
+ * @internal - test use only.
24
+ */
25
+ export const _regressionDeps = {
26
+ runVerification,
27
+ runRectificationLoop,
28
+ parseBunTestOutput,
29
+ reverseMapTestToSource,
30
+ };
31
+
32
+ export interface DeferredRegressionOptions {
33
+ config: NaxConfig;
34
+ prd: PRD;
35
+ workdir: string;
36
+ }
37
+
38
+ export interface DeferredRegressionResult {
39
+ success: boolean;
40
+ failedTests: number;
41
+ passedTests: number;
42
+ rectificationAttempts: number;
43
+ affectedStories: string[];
44
+ }
45
+
46
+ /**
47
+ * Map a test file to the story responsible for it via git log.
48
+ *
49
+ * Searches recent commits for story IDs in the format US-NNN.
50
+ * Returns the first matching story ID, or undefined if not found.
51
+ */
52
+ async function findResponsibleStory(
53
+ testFile: string,
54
+ workdir: string,
55
+ passedStories: UserStory[],
56
+ ): Promise<UserStory | undefined> {
57
+ const logger = getSafeLogger();
58
+
59
+ // Try each passed story in reverse order (most recent first)
60
+ for (let i = passedStories.length - 1; i >= 0; i--) {
61
+ const story = passedStories[i];
62
+ const hasCommits = await hasCommitsForStory(workdir, story.id, 50);
63
+ if (hasCommits) {
64
+ logger?.info("regression", `Mapped test to story ${story.id}`, { testFile });
65
+ return story;
66
+ }
67
+ }
68
+
69
+ return undefined;
70
+ }
71
+
72
+ /**
73
+ * Run deferred regression gate after all stories complete.
74
+ *
75
+ * Steps:
76
+ * 1. Run full test suite
77
+ * 2. If failures, reverse-map test files to source files to stories
78
+ * 3. For each affected story, attempt targeted rectification
79
+ * 4. Re-run full suite to confirm fixes
80
+ * 5. Return results with affected story list
81
+ */
82
+ export async function runDeferredRegression(options: DeferredRegressionOptions): Promise<DeferredRegressionResult> {
83
+ const logger = getSafeLogger();
84
+ const { config, prd, workdir } = options;
85
+
86
+ // Check if regression gate is deferred
87
+ const regressionMode = config.execution.regressionGate?.mode ?? "deferred";
88
+ if (regressionMode === "disabled") {
89
+ logger?.info("regression", "Deferred regression gate disabled");
90
+ return {
91
+ success: true,
92
+ failedTests: 0,
93
+ passedTests: 0,
94
+ rectificationAttempts: 0,
95
+ affectedStories: [],
96
+ };
97
+ }
98
+
99
+ if (regressionMode !== "deferred") {
100
+ logger?.info("regression", "Regression gate mode is not deferred, skipping");
101
+ return {
102
+ success: true,
103
+ failedTests: 0,
104
+ passedTests: 0,
105
+ rectificationAttempts: 0,
106
+ affectedStories: [],
107
+ };
108
+ }
109
+
110
+ const testCommand = config.quality.commands.test ?? "bun test";
111
+ const timeoutSeconds = config.execution.regressionGate?.timeoutSeconds ?? 120;
112
+ const maxRectificationAttempts = config.execution.regressionGate?.maxRectificationAttempts ?? 2;
113
+
114
+ // Only check stories that have been marked as passed
115
+ const counts = countStories(prd);
116
+ const passedStories = prd.userStories.filter((s) => s.status === "passed");
117
+
118
+ if (passedStories.length === 0) {
119
+ logger?.info("regression", "No passed stories to verify (partial completion)");
120
+ return {
121
+ success: true,
122
+ failedTests: 0,
123
+ passedTests: 0,
124
+ rectificationAttempts: 0,
125
+ affectedStories: [],
126
+ };
127
+ }
128
+
129
+ logger?.info("regression", "Running deferred full-suite regression gate", {
130
+ totalStories: counts.total,
131
+ passedStories: passedStories.length,
132
+ });
133
+
134
+ // Step 1: Run full test suite
135
+ const fullSuiteResult = await _regressionDeps.runVerification({
136
+ workingDirectory: workdir,
137
+ command: testCommand,
138
+ timeoutSeconds,
139
+ forceExit: config.quality.forceExit,
140
+ detectOpenHandles: config.quality.detectOpenHandles,
141
+ detectOpenHandlesRetries: config.quality.detectOpenHandlesRetries,
142
+ timeoutRetryCount: 0,
143
+ gracePeriodMs: config.quality.gracePeriodMs,
144
+ drainTimeoutMs: config.quality.drainTimeoutMs,
145
+ shell: config.quality.shell,
146
+ stripEnvVars: config.quality.stripEnvVars,
147
+ });
148
+
149
+ if (fullSuiteResult.success) {
150
+ logger?.info("regression", "Full suite passed");
151
+ return {
152
+ success: true,
153
+ failedTests: 0,
154
+ passedTests: fullSuiteResult.passCount ?? 0,
155
+ rectificationAttempts: 0,
156
+ affectedStories: [],
157
+ };
158
+ }
159
+
160
+ // Handle timeout
161
+ const acceptOnTimeout = config.execution.regressionGate?.acceptOnTimeout ?? true;
162
+ if (fullSuiteResult.status === "TIMEOUT" && acceptOnTimeout) {
163
+ logger?.warn("regression", "Full-suite regression gate timed out (accepted as pass)");
164
+ return {
165
+ success: true,
166
+ failedTests: 0,
167
+ passedTests: 0,
168
+ rectificationAttempts: 0,
169
+ affectedStories: [],
170
+ };
171
+ }
172
+
173
+ if (!fullSuiteResult.output) {
174
+ logger?.error("regression", "Full suite failed with no output");
175
+ return {
176
+ success: false,
177
+ failedTests: fullSuiteResult.failCount ?? 0,
178
+ passedTests: fullSuiteResult.passCount ?? 0,
179
+ rectificationAttempts: 0,
180
+ affectedStories: [],
181
+ };
182
+ }
183
+
184
+ // Step 2: Parse failures and map to source files to stories
185
+ const testSummary = _regressionDeps.parseBunTestOutput(fullSuiteResult.output);
186
+ const affectedStories = new Set<string>();
187
+ const affectedStoriesObjs = new Map<string, UserStory>();
188
+
189
+ logger?.warn("regression", "Regression detected", {
190
+ failedTests: testSummary.failed,
191
+ passedTests: testSummary.passed,
192
+ });
193
+
194
+ // Extract test file paths from failures
195
+ const testFilesInFailures = new Set<string>();
196
+ for (const failure of testSummary.failures) {
197
+ if (failure.file) {
198
+ testFilesInFailures.add(failure.file);
199
+ }
200
+ }
201
+
202
+ if (testFilesInFailures.size === 0) {
203
+ logger?.warn("regression", "No test files found in failures (unmapped)");
204
+ // Mark all passed stories for re-verification
205
+ for (const story of passedStories) {
206
+ affectedStories.add(story.id);
207
+ affectedStoriesObjs.set(story.id, story);
208
+ }
209
+ } else {
210
+ // Map test files to source files to stories
211
+ const testFilesArray = Array.from(testFilesInFailures);
212
+ const sourceFilesArray = _regressionDeps.reverseMapTestToSource(testFilesArray, workdir);
213
+
214
+ logger?.info("regression", "Mapped test files to source files", {
215
+ testFiles: testFilesArray.length,
216
+ sourceFiles: sourceFilesArray.length,
217
+ });
218
+
219
+ for (const testFile of testFilesArray) {
220
+ const responsibleStory = await findResponsibleStory(testFile, workdir, passedStories);
221
+ if (responsibleStory) {
222
+ affectedStories.add(responsibleStory.id);
223
+ affectedStoriesObjs.set(responsibleStory.id, responsibleStory);
224
+ } else {
225
+ logger?.warn("regression", "Could not map test file to story", { testFile });
226
+ }
227
+ }
228
+ }
229
+
230
+ if (affectedStories.size === 0) {
231
+ logger?.warn("regression", "No stories could be mapped to failures");
232
+ return {
233
+ success: false,
234
+ failedTests: testSummary.failed,
235
+ passedTests: testSummary.passed,
236
+ rectificationAttempts: 0,
237
+ affectedStories: Array.from(affectedStories),
238
+ };
239
+ }
240
+
241
+ // Step 3: Attempt rectification per story
242
+ let rectificationAttempts = 0;
243
+ const affectedStoriesList = Array.from(affectedStoriesObjs.values());
244
+
245
+ for (const story of affectedStoriesList) {
246
+ for (let attempt = 0; attempt < maxRectificationAttempts; attempt++) {
247
+ rectificationAttempts++;
248
+
249
+ logger?.info("regression", `Rectifying story ${story.id} (attempt ${attempt + 1}/${maxRectificationAttempts})`);
250
+
251
+ const fixed = await _regressionDeps.runRectificationLoop({
252
+ config,
253
+ workdir,
254
+ story,
255
+ testCommand,
256
+ timeoutSeconds,
257
+ testOutput: fullSuiteResult.output,
258
+ promptPrefix: `# DEFERRED REGRESSION: Full-Suite Failures\n\nYour story ${story.id} broke tests in the full suite. Fix these regressions.`,
259
+ });
260
+
261
+ if (fixed) {
262
+ logger?.info("regression", `Story ${story.id} rectified successfully`);
263
+ break; // Move to next story
264
+ }
265
+ }
266
+ }
267
+
268
+ // Step 4: Re-run full suite to confirm
269
+ logger?.info("regression", "Re-running full suite after rectification");
270
+ const retryResult = await _regressionDeps.runVerification({
271
+ workingDirectory: workdir,
272
+ command: testCommand,
273
+ timeoutSeconds,
274
+ forceExit: config.quality.forceExit,
275
+ detectOpenHandles: config.quality.detectOpenHandles,
276
+ detectOpenHandlesRetries: config.quality.detectOpenHandlesRetries,
277
+ timeoutRetryCount: 0,
278
+ gracePeriodMs: config.quality.gracePeriodMs,
279
+ drainTimeoutMs: config.quality.drainTimeoutMs,
280
+ shell: config.quality.shell,
281
+ stripEnvVars: config.quality.stripEnvVars,
282
+ });
283
+
284
+ const success = retryResult.success || (retryResult.status === "TIMEOUT" && acceptOnTimeout);
285
+
286
+ if (success) {
287
+ logger?.info("regression", "Deferred regression gate passed after rectification");
288
+ } else {
289
+ logger?.warn("regression", "Deferred regression gate still failing after rectification", {
290
+ remainingFailures: retryResult.failCount,
291
+ });
292
+ }
293
+
294
+ return {
295
+ success,
296
+ failedTests: retryResult.failCount ?? 0,
297
+ passedTests: retryResult.passCount ?? 0,
298
+ rectificationAttempts,
299
+ affectedStories: Array.from(affectedStories),
300
+ };
301
+ }
@@ -83,7 +83,6 @@ export async function handlePipelineSuccess(
83
83
  storiesToExecute: ctx.storiesToExecute,
84
84
  allStoryMetrics: ctx.allStoryMetrics,
85
85
  timeoutRetryCountMap: ctx.timeoutRetryCountMap,
86
- storyGitRef: ctx.storyGitRef ?? undefined,
87
86
  });
88
87
  const verificationPassed = verifyResult.passed;
89
88
  prd = verifyResult.prd;