@nathapp/nax 0.21.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 (210) hide show
  1. package/.mcp.json +8 -0
  2. package/docs/ROADMAP.md +20 -5
  3. package/docs/adr/ADR-005-implementation-plan.md +655 -0
  4. package/docs/adr/ADR-005-pipeline-re-architecture.md +464 -0
  5. package/package.json +1 -1
  6. package/src/agents/claude.ts +44 -9
  7. package/src/config/types.ts +11 -0
  8. package/src/execution/dry-run.ts +81 -0
  9. package/src/execution/escalation/tier-outcome.ts +29 -44
  10. package/src/execution/executor-types.ts +65 -0
  11. package/src/execution/index.ts +0 -17
  12. package/src/execution/iteration-runner.ts +132 -0
  13. package/src/execution/lifecycle/index.ts +0 -1
  14. package/src/execution/lifecycle/run-regression.ts +5 -5
  15. package/src/execution/pipeline-result-handler.ts +51 -254
  16. package/src/execution/sequential-executor.ts +72 -316
  17. package/src/execution/story-selector.ts +75 -0
  18. package/src/pipeline/event-bus.ts +276 -0
  19. package/src/pipeline/runner.ts +51 -77
  20. package/src/pipeline/stages/autofix.ts +133 -0
  21. package/src/pipeline/stages/completion.ts +22 -30
  22. package/src/pipeline/stages/index.ts +30 -13
  23. package/src/pipeline/stages/rectify.ts +93 -0
  24. package/src/pipeline/stages/regression.ts +88 -0
  25. package/src/pipeline/stages/review.ts +19 -153
  26. package/src/pipeline/stages/verify.ts +18 -2
  27. package/src/pipeline/subscribers/hooks.ts +133 -0
  28. package/src/pipeline/subscribers/interaction.ts +68 -0
  29. package/src/pipeline/subscribers/reporters.ts +174 -0
  30. package/src/pipeline/types.ts +10 -1
  31. package/src/review/orchestrator.ts +105 -0
  32. package/src/tdd/prompts.ts +1 -1
  33. package/src/verification/index.ts +1 -1
  34. package/src/verification/orchestrator-types.ts +145 -0
  35. package/src/verification/orchestrator.ts +76 -0
  36. package/src/{execution/post-verify-rectification.ts → verification/rectification-loop.ts} +13 -20
  37. package/src/verification/{gate.ts → runners.ts} +17 -105
  38. package/src/verification/strategies/acceptance.ts +133 -0
  39. package/src/verification/strategies/regression.ts +90 -0
  40. package/src/verification/strategies/scoped.ts +123 -0
  41. package/test/COVERAGE-GAPS.md +333 -0
  42. package/test/{acceptance → e2e}/cm-003-default-view.test.ts +1 -0
  43. package/test/{integration/e2e.test.ts → e2e/plan-analyze-run.test.ts} +1 -0
  44. package/test/integration/{agent-validation.test.ts → cli/agent-validation.test.ts} +3 -3
  45. package/test/integration/{cli-config-default-edge-cases.test.ts → cli/cli-config-default-edge-cases.test.ts} +6 -5
  46. package/test/integration/{cli-config-default-view.test.ts → cli/cli-config-default-view.test.ts} +8 -7
  47. package/test/integration/{cli-config-diff.test.ts → cli/cli-config-diff.test.ts} +3 -2
  48. package/test/integration/{cli-config.test.ts → cli/cli-config.test.ts} +3 -2
  49. package/test/integration/{cli-diagnose.test.ts → cli/cli-diagnose.test.ts} +5 -4
  50. package/test/integration/{cli-logs.test.ts → cli/cli-logs.test.ts} +12 -3
  51. package/test/integration/{cli-plugins.test.ts → cli/cli-plugins.test.ts} +4 -3
  52. package/test/integration/{cli-precheck.test.ts → cli/cli-precheck.test.ts} +4 -3
  53. package/test/integration/{cli-run-headless.test.ts → cli/cli-run-headless.test.ts} +3 -2
  54. package/test/integration/{cli.test.ts → cli/cli.test.ts} +2 -1
  55. package/test/integration/{precheck-integration.test.ts → cli/precheck-integration.test.ts} +10 -9
  56. package/test/integration/{precheck-orchestrator.test.ts → cli/precheck-orchestrator.test.ts} +4 -3
  57. package/test/integration/{precheck.test.ts → cli/precheck.test.ts} +5 -4
  58. package/test/integration/{config-loader.test.ts → config/config-loader.test.ts} +2 -1
  59. package/test/integration/{config.test.ts → config/config.test.ts} +2 -2
  60. package/test/integration/config/merger.test.ts +1 -0
  61. package/test/integration/config/paths.test.ts +1 -0
  62. package/test/integration/{security-loader.test.ts → config/security-loader.test.ts} +2 -2
  63. package/test/integration/{context-integration.test.ts → context/context-integration.test.ts} +7 -6
  64. package/test/integration/{path-security.test.ts → context/context-path-security.test.ts} +2 -2
  65. package/test/integration/{context-provider-injection.test.ts → context/context-provider-injection.test.ts} +7 -6
  66. package/test/integration/{context-verification-integration.test.ts → context/context-verification-integration.test.ts} +5 -4
  67. package/test/integration/{s5-greenfield-fallback.test.ts → context/s5-greenfield-fallback.test.ts} +4 -3
  68. package/test/integration/{isolation.test.ts → execution/execution-isolation.test.ts} +1 -1
  69. package/test/integration/{execution.test.ts → execution/execution.test.ts} +8 -8
  70. package/test/integration/{parallel.test.ts → execution/parallel.test.ts} +2 -1
  71. package/test/integration/{prd-pause.test.ts → execution/prd-pause.test.ts} +2 -2
  72. package/test/integration/{prd-resolvers.test.ts → execution/prd-resolvers.test.ts} +3 -2
  73. package/test/integration/{progress.test.ts → execution/progress.test.ts} +1 -1
  74. package/test/integration/execution/runner-batching.test.ts +682 -0
  75. package/test/integration/{runner-config-plugins.test.ts → execution/runner-config-plugins.test.ts} +3 -2
  76. package/test/integration/execution/runner-escalation.test.ts +561 -0
  77. package/test/integration/{runner-fixes.test.ts → execution/runner-fixes.test.ts} +4 -3
  78. package/test/integration/{runner-plugin-integration.test.ts → execution/runner-plugin-integration.test.ts} +6 -5
  79. package/test/integration/execution/runner-queue-and-attempts.test.ts +476 -0
  80. package/test/integration/{status-file-integration.test.ts → execution/status-file-integration.test.ts} +9 -8
  81. package/test/integration/{status-file.test.ts → execution/status-file.test.ts} +3 -2
  82. package/test/integration/{status-writer.test.ts → execution/status-writer.test.ts} +5 -4
  83. package/test/integration/{story-id-in-events.test.ts → execution/story-id-in-events.test.ts} +9 -8
  84. package/test/integration/{interaction-chain-pipeline.test.ts → interaction/interaction-chain-pipeline.test.ts} +26 -14
  85. package/test/integration/{hooks.test.ts → pipeline/hooks.test.ts} +4 -2
  86. package/test/integration/{pipeline-acceptance.test.ts → pipeline/pipeline-acceptance.test.ts} +7 -6
  87. package/test/integration/{pipeline-events.test.ts → pipeline/pipeline-events.test.ts} +7 -6
  88. package/test/integration/{pipeline.test.ts → pipeline/pipeline.test.ts} +9 -7
  89. package/test/integration/{reporter-lifecycle.test.ts → pipeline/reporter-lifecycle.test.ts} +9 -7
  90. package/test/integration/{verify-stage.test.ts → pipeline/verify-stage.test.ts} +7 -5
  91. package/test/integration/{analyze-integration.test.ts → plan/analyze-integration.test.ts} +3 -2
  92. package/test/integration/{analyze-scanner.test.ts → plan/analyze-scanner.test.ts} +8 -7
  93. package/test/integration/{logger.test.ts → plan/logger.test.ts} +1 -1
  94. package/test/integration/{plan.test.ts → plan/plan.test.ts} +3 -3
  95. package/test/integration/plugins/config-integration.test.ts +1 -0
  96. package/test/integration/plugins/config-resolution.test.ts +1 -0
  97. package/test/integration/plugins/loader.test.ts +1 -0
  98. package/test/integration/plugins/{registry.test.ts → plugins-registry.test.ts} +1 -0
  99. package/test/integration/plugins/validator.test.ts +1 -0
  100. package/test/integration/{review-config-commands.test.ts → review/review-config-commands.test.ts} +4 -3
  101. package/test/integration/{review-config-schema.test.ts → review/review-config-schema.test.ts} +3 -2
  102. package/test/integration/{review-plugin-integration.test.ts → review/review-plugin-integration.test.ts} +5 -4
  103. package/test/integration/{review.test.ts → review/review.test.ts} +3 -2
  104. package/test/integration/routing/plugin-routing-advanced.test.ts +461 -0
  105. package/test/integration/{plugin-routing.test.ts → routing/plugin-routing-core.test.ts} +9 -403
  106. package/test/integration/{routing-stage-bug-021.test.ts → routing/routing-stage-bug-021.test.ts} +8 -7
  107. package/test/integration/{routing-stage-greenfield.test.ts → routing/routing-stage-greenfield.test.ts} +7 -6
  108. package/test/integration/{tdd-cleanup.test.ts → tdd/tdd-cleanup.test.ts} +1 -1
  109. package/test/integration/tdd/tdd-orchestrator-core.test.ts +565 -0
  110. package/test/integration/tdd/tdd-orchestrator-failureCategory.test.ts +355 -0
  111. package/test/integration/tdd/tdd-orchestrator-fallback.test.ts +311 -0
  112. package/test/integration/tdd/tdd-orchestrator-lite.test.ts +289 -0
  113. package/test/integration/tdd/tdd-orchestrator-prompts.test.ts +260 -0
  114. package/test/integration/tdd/tdd-orchestrator-verdict.test.ts +536 -0
  115. package/test/integration/tmp/headless-test/test.jsonl +30 -0
  116. package/test/integration/{test-scanner.test.ts → verification/test-scanner.test.ts} +1 -1
  117. package/test/integration/{verification-asset-check.test.ts → verification/verification-asset-check.test.ts} +3 -2
  118. package/test/unit/acceptance.test.ts +1 -0
  119. package/test/unit/agent-stderr-capture.test.ts +1 -0
  120. package/test/unit/agents/claude.test.ts +1 -0
  121. package/test/unit/analyze-classifier.test.ts +1 -0
  122. package/test/unit/auto-detect.test.ts +1 -0
  123. package/test/unit/cli-status.test.ts +1 -0
  124. package/test/unit/commands/common.test.ts +1 -0
  125. package/test/unit/commands/logs.test.ts +1 -0
  126. package/test/unit/commands/unlock.test.ts +1 -0
  127. package/test/unit/config/defaults.test.ts +1 -0
  128. package/test/unit/config/regression-gate-schema.test.ts +1 -0
  129. package/test/unit/config/smart-runner-flag.test.ts +1 -0
  130. package/test/unit/constitution-generators.test.ts +1 -0
  131. package/test/unit/constitution.test.ts +1 -0
  132. package/test/unit/context/context-autodetect.test.ts +297 -0
  133. package/test/unit/context/context-build.test.ts +575 -0
  134. package/test/unit/context/context-coverage.test.ts +236 -0
  135. package/test/unit/context/context-error.test.ts +93 -0
  136. package/test/unit/context/context-estimate-tokens.test.ts +201 -0
  137. package/test/unit/context/context-format.test.ts +302 -0
  138. package/test/unit/context/context-isolation.test.ts +267 -0
  139. package/test/unit/context/context-sort.test.ts +93 -0
  140. package/test/unit/context/context-story.test.ts +108 -0
  141. package/test/{context → unit/context}/prior-failures.test.ts +5 -4
  142. package/test/unit/context.test.ts +1 -0
  143. package/test/unit/crash-recovery.test.ts +1 -0
  144. package/test/unit/escalation.test.ts +1 -0
  145. package/test/unit/execution/lifecycle/run-completion.test.ts +1 -0
  146. package/test/unit/execution/lifecycle/run-regression.test.ts +2 -0
  147. package/test/{execution → unit/execution}/pid-registry.test.ts +2 -1
  148. package/test/{execution → unit/execution}/structured-failure.test.ts +3 -2
  149. package/test/unit/execution-logging-stderr.test.ts +1 -0
  150. package/test/unit/execution-stage.test.ts +1 -0
  151. package/test/unit/fix-generator.test.ts +1 -0
  152. package/test/unit/greenfield.test.ts +1 -0
  153. package/test/unit/interaction/human-review-trigger.test.ts +1 -0
  154. package/test/unit/interaction-network-failures.test.ts +1 -0
  155. package/test/unit/interaction-plugins.test.ts +1 -0
  156. package/test/unit/logging/formatter.test.ts +1 -0
  157. package/test/unit/merge.test.ts +1 -0
  158. package/test/unit/pipeline/event-bus.test.ts +105 -0
  159. package/test/unit/pipeline/routing-partial-override.test.ts +1 -0
  160. package/test/unit/pipeline/runner-retry.test.ts +89 -0
  161. package/test/unit/pipeline/stages/autofix.test.ts +97 -0
  162. package/test/unit/pipeline/stages/rectify.test.ts +101 -0
  163. package/test/unit/pipeline/stages/regression-stage.test.ts +69 -0
  164. package/test/unit/pipeline/stages/verify.test.ts +1 -0
  165. package/test/unit/pipeline/subscribers/hooks.test.ts +45 -0
  166. package/test/unit/pipeline/subscribers/interaction.test.ts +31 -0
  167. package/test/unit/pipeline/subscribers/reporters.test.ts +90 -0
  168. package/test/unit/pipeline/verify-smart-runner.test.ts +1 -0
  169. package/test/unit/prd-auto-default.test.ts +1 -0
  170. package/test/unit/prd-failure-category.test.ts +1 -0
  171. package/test/unit/prd-get-next-story.test.ts +1 -0
  172. package/test/unit/precheck-checks.test.ts +1 -0
  173. package/test/unit/precheck-story-size-gate.test.ts +1 -0
  174. package/test/unit/precheck-types.test.ts +1 -0
  175. package/test/unit/prompts.test.ts +1 -0
  176. package/test/unit/rectification.test.ts +2 -1
  177. package/test/unit/registry.test.ts +1 -0
  178. package/test/unit/routing/routing-stability.test.ts +1 -0
  179. package/test/unit/routing/strategies/llm.test.ts +1 -0
  180. package/test/unit/routing-advanced.test.ts +313 -0
  181. package/test/unit/routing-core.test.ts +341 -0
  182. package/test/unit/routing-strategies.test.ts +442 -0
  183. package/test/unit/storyid-events.test.ts +1 -0
  184. package/test/{ui → unit/ui}/tui-controls.test.ts +8 -7
  185. package/test/{ui → unit/ui}/tui-cost-and-pty.test.ts +4 -3
  186. package/test/{ui → unit/ui}/tui-layout.test.ts +5 -4
  187. package/test/{ui → unit/ui}/tui-stories.test.ts +5 -4
  188. package/test/unit/{isolation.test.ts → unit-isolation.test.ts} +1 -0
  189. package/test/unit/{helpers.test.ts → utils-helpers.test.ts} +1 -0
  190. package/test/unit/verdict.test.ts +1 -0
  191. package/test/unit/verification/orchestrator-types.test.ts +54 -0
  192. package/test/unit/verification/orchestrator.test.ts +66 -0
  193. package/test/unit/verification/smart-runner-config.test.ts +1 -0
  194. package/test/unit/verification/smart-runner-discovery.test.ts +8 -7
  195. package/test/unit/verification/strategies/acceptance.test.ts +33 -0
  196. package/test/unit/verification/strategies/regression.test.ts +87 -0
  197. package/test/unit/verification/strategies/scoped.test.ts +100 -0
  198. package/test/unit/worktree-manager.test.ts +1 -0
  199. package/src/execution/lifecycle/story-hooks.ts +0 -38
  200. package/src/execution/post-verify.ts +0 -193
  201. package/src/execution/rectification.ts +0 -13
  202. package/src/execution/verification.ts +0 -72
  203. package/test/integration/rectification-flow.test.ts +0 -512
  204. package/test/integration/runner.test.ts +0 -1679
  205. package/test/integration/tdd-orchestrator.test.ts +0 -1762
  206. package/test/unit/execution/post-verify-regression.test.ts +0 -362
  207. package/test/unit/execution/post-verify.test.ts +0 -236
  208. package/test/unit/routing.test.ts +0 -1039
  209. /package/test/{integration → helpers}/helpers.test.ts +0 -0
  210. /package/test/integration/worktree/{merge.test.ts → worktree-merge.test.ts} +0 -0
@@ -3,13 +3,14 @@
3
3
  *
4
4
  * Extracted from tier-escalation.ts: handles outcomes when escalation
5
5
  * is not possible (no tier available or max attempts reached).
6
+ *
7
+ * Phase 3 (ADR-005): Replaced direct fireHook() calls with event bus emissions.
6
8
  */
7
9
 
8
- import { fireHook } from "../../hooks";
9
10
  import { getSafeLogger } from "../../logger";
11
+ import { pipelineEventBus } from "../../pipeline/event-bus";
10
12
  import { markStoryFailed, markStoryPaused, savePRD } from "../../prd";
11
13
  import type { FailureCategory } from "../../tdd/types";
12
- import { hookCtx } from "../helpers";
13
14
  import { appendProgress } from "../progress";
14
15
  import type { EscalationHandlerContext, EscalationHandlerResult } from "./tier-escalation";
15
16
  import { resolveMaxAttemptsOutcome } from "./tier-escalation";
@@ -43,16 +44,12 @@ export async function handleNoTierAvailable(
43
44
  );
44
45
  }
45
46
 
46
- await fireHook(
47
- ctx.hooks,
48
- "on-pause",
49
- hookCtx(ctx.feature, {
50
- storyId: ctx.story.id,
51
- reason: `Execution stopped (${failureCategory ?? "unknown"} requires human review)`,
52
- cost: ctx.totalCost,
53
- }),
54
- ctx.workdir,
55
- );
47
+ pipelineEventBus.emit({
48
+ type: "story:paused",
49
+ storyId: ctx.story.id,
50
+ reason: `Execution stopped (${failureCategory ?? "unknown"} requires human review)`,
51
+ cost: ctx.totalCost,
52
+ });
56
53
 
57
54
  return { outcome: "paused", prdDirty: true, prd: pausedPrd };
58
55
  }
@@ -70,17 +67,13 @@ export async function handleNoTierAvailable(
70
67
  await appendProgress(ctx.featureDir, ctx.story.id, "failed", `${ctx.story.title} — Execution failed`);
71
68
  }
72
69
 
73
- await fireHook(
74
- ctx.hooks,
75
- "on-story-fail",
76
- hookCtx(ctx.feature, {
77
- storyId: ctx.story.id,
78
- status: "failed",
79
- reason: "Execution failed",
80
- cost: ctx.totalCost,
81
- }),
82
- ctx.workdir,
83
- );
70
+ pipelineEventBus.emit({
71
+ type: "story:failed",
72
+ storyId: ctx.story.id,
73
+ story: ctx.story,
74
+ reason: "Execution failed",
75
+ countsTowardEscalation: true,
76
+ });
84
77
 
85
78
  return { outcome: "failed", prdDirty: true, prd: failedPrd };
86
79
  }
@@ -114,16 +107,12 @@ export async function handleMaxAttemptsReached(
114
107
  );
115
108
  }
116
109
 
117
- await fireHook(
118
- ctx.hooks,
119
- "on-pause",
120
- hookCtx(ctx.feature, {
121
- storyId: ctx.story.id,
122
- reason: `Max attempts reached (${failureCategory ?? "unknown"} requires human review)`,
123
- cost: ctx.totalCost,
124
- }),
125
- ctx.workdir,
126
- );
110
+ pipelineEventBus.emit({
111
+ type: "story:paused",
112
+ storyId: ctx.story.id,
113
+ reason: `Max attempts reached (${failureCategory ?? "unknown"} requires human review)`,
114
+ cost: ctx.totalCost,
115
+ });
127
116
 
128
117
  return { outcome: "paused", prdDirty: true, prd: pausedPrd };
129
118
  }
@@ -142,17 +131,13 @@ export async function handleMaxAttemptsReached(
142
131
  await appendProgress(ctx.featureDir, ctx.story.id, "failed", `${ctx.story.title} — Max attempts reached`);
143
132
  }
144
133
 
145
- await fireHook(
146
- ctx.hooks,
147
- "on-story-fail",
148
- hookCtx(ctx.feature, {
149
- storyId: ctx.story.id,
150
- status: "failed",
151
- reason: "Max attempts reached",
152
- cost: ctx.totalCost,
153
- }),
154
- ctx.workdir,
155
- );
134
+ pipelineEventBus.emit({
135
+ type: "story:failed",
136
+ storyId: ctx.story.id,
137
+ story: ctx.story,
138
+ reason: "Max attempts reached",
139
+ countsTowardEscalation: true,
140
+ });
156
141
 
157
142
  return { outcome: "failed", prdDirty: true, prd: failedPrd };
158
143
  }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Sequential Executor Types (ADR-005, Phase 4)
3
+ *
4
+ * Extracted from sequential-executor.ts to slim it below 200 lines.
5
+ */
6
+
7
+ import type { NaxConfig } from "../config";
8
+ import type { LoadedHooksConfig } from "../hooks";
9
+ import type { InteractionChain } from "../interaction/chain";
10
+ import type { StoryMetrics } from "../metrics";
11
+ import type { PipelineEventEmitter } from "../pipeline/events";
12
+ import type { RoutingResult } from "../pipeline/types";
13
+ import type { PluginRegistry } from "../plugins";
14
+ import type { PRD, UserStory } from "../prd/types";
15
+ import type { StoryBatch } from "./batching";
16
+ import type { StatusWriter } from "./status-writer";
17
+
18
+ export interface SequentialExecutionContext {
19
+ prdPath: string;
20
+ workdir: string;
21
+ config: NaxConfig;
22
+ hooks: LoadedHooksConfig;
23
+ feature: string;
24
+ featureDir?: string;
25
+ dryRun: boolean;
26
+ useBatch: boolean;
27
+ pluginRegistry: PluginRegistry;
28
+ eventEmitter?: PipelineEventEmitter;
29
+ statusWriter: StatusWriter;
30
+ logFilePath?: string;
31
+ runId: string;
32
+ startTime: number;
33
+ batchPlan: StoryBatch[];
34
+ interactionChain?: InteractionChain | null;
35
+ }
36
+
37
+ export interface SequentialExecutionResult {
38
+ prd: PRD;
39
+ iterations: number;
40
+ storiesCompleted: number;
41
+ totalCost: number;
42
+ allStoryMetrics: StoryMetrics[];
43
+ exitReason: "completed" | "cost-limit" | "max-iterations" | "stalled" | "no-stories";
44
+ }
45
+
46
+ /**
47
+ * Build a preview routing from cached story.routing or config defaults.
48
+ * The pipeline routing stage performs full classification and overwrites ctx.routing.
49
+ * This preview is used only for logging, status display, and event emission.
50
+ */
51
+ export function buildPreviewRouting(story: UserStory, config: NaxConfig): RoutingResult {
52
+ const cached = story.routing;
53
+ const defaultComplexity = "medium" as const;
54
+ const defaultTier = "balanced" as const;
55
+ const defaultStrategy = "test-after" as const;
56
+ return {
57
+ complexity: (cached?.complexity as RoutingResult["complexity"]) ?? defaultComplexity,
58
+ modelTier:
59
+ (cached?.modelTier as RoutingResult["modelTier"]) ??
60
+ (config.autoMode.complexityRouting?.[defaultComplexity] as RoutingResult["modelTier"]) ??
61
+ defaultTier,
62
+ testStrategy: (cached?.testStrategy as RoutingResult["testStrategy"]) ?? defaultStrategy,
63
+ reasoning: cached ? "cached from story.routing" : "preview (pending pipeline routing stage)",
64
+ };
65
+ }
@@ -5,23 +5,6 @@ export { appendProgress } from "./progress";
5
5
  export { buildSingleSessionPrompt, buildBatchPrompt } from "./prompts";
6
6
  export { groupStoriesIntoBatches, type StoryBatch } from "./batching";
7
7
  export { escalateTier, getTierConfig, calculateMaxIterations } from "./escalation";
8
- export {
9
- verifyAssets,
10
- executeWithTimeout,
11
- parseTestOutput,
12
- getEnvironmentalEscalationThreshold,
13
- normalizeEnvironment,
14
- buildTestCommand,
15
- appendOpenHandlesFlag,
16
- appendForceExitFlag,
17
- runVerification,
18
- type VerificationResult,
19
- type VerificationStatus,
20
- type TestOutputAnalysis,
21
- type AssetVerificationResult,
22
- type TimeoutExecutionResult,
23
- } from "./verification";
24
- export { runPostAgentVerification, type PostVerifyOptions, type PostVerifyResult } from "./post-verify";
25
8
  export { readQueueFile, clearQueueFile } from "./queue-handler";
26
9
  export {
27
10
  hookCtx,
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Iteration Runner (ADR-005, Phase 4)
3
+ *
4
+ * Runs a single story through the pipeline.
5
+ * Extracted from sequential-executor.ts to slim it below 120 lines.
6
+ */
7
+
8
+ import { getSafeLogger } from "../logger";
9
+ import type { StoryMetrics } from "../metrics";
10
+ import { runPipeline } from "../pipeline/runner";
11
+ import type { PipelineRunResult } from "../pipeline/runner";
12
+ import { defaultPipeline } from "../pipeline/stages";
13
+ import type { PipelineContext } from "../pipeline/types";
14
+ import type { PRD } from "../prd/types";
15
+ import { captureGitRef } from "../utils/git";
16
+ import { handleDryRun } from "./dry-run";
17
+ import type { SequentialExecutionContext } from "./executor-types";
18
+ import { handlePipelineFailure, handlePipelineSuccess } from "./pipeline-result-handler";
19
+ import type { StorySelection } from "./story-selector";
20
+
21
+ export interface IterationResult {
22
+ prd: PRD;
23
+ storiesCompletedDelta: number;
24
+ costDelta: number;
25
+ prdDirty: boolean;
26
+ finalAction?: string;
27
+ reason?: string;
28
+ }
29
+
30
+ export async function runIteration(
31
+ ctx: SequentialExecutionContext,
32
+ prd: PRD,
33
+ selection: StorySelection,
34
+ iterations: number,
35
+ totalCost: number,
36
+ allStoryMetrics: StoryMetrics[],
37
+ ): Promise<IterationResult> {
38
+ const logger = getSafeLogger();
39
+ const { story, storiesToExecute, routing, isBatchExecution } = selection;
40
+
41
+ if (ctx.dryRun) {
42
+ const dryRunResult = await handleDryRun({
43
+ prd,
44
+ prdPath: ctx.prdPath,
45
+ storiesToExecute,
46
+ routing,
47
+ statusWriter: ctx.statusWriter,
48
+ pluginRegistry: ctx.pluginRegistry,
49
+ runId: ctx.runId,
50
+ totalCost,
51
+ iterations,
52
+ });
53
+ return {
54
+ prd,
55
+ storiesCompletedDelta: dryRunResult.storiesCompletedDelta,
56
+ costDelta: 0,
57
+ prdDirty: dryRunResult.prdDirty,
58
+ };
59
+ }
60
+
61
+ const storyGitRef = await captureGitRef(ctx.workdir);
62
+ const pipelineContext: PipelineContext = {
63
+ config: ctx.config,
64
+ prd,
65
+ story,
66
+ stories: storiesToExecute,
67
+ routing,
68
+ workdir: ctx.workdir,
69
+ featureDir: ctx.featureDir,
70
+ hooks: ctx.hooks,
71
+ plugins: ctx.pluginRegistry,
72
+ storyStartTime: new Date().toISOString(),
73
+ storyGitRef: storyGitRef ?? undefined,
74
+ interaction: ctx.interactionChain ?? undefined,
75
+ };
76
+
77
+ ctx.statusWriter.setPrd(prd);
78
+ ctx.statusWriter.setCurrentStory({
79
+ storyId: story.id,
80
+ title: story.title,
81
+ complexity: routing.complexity,
82
+ tddStrategy: routing.testStrategy,
83
+ model: routing.modelTier,
84
+ attempt: (story.attempts ?? 0) + 1,
85
+ phase: "routing",
86
+ });
87
+ await ctx.statusWriter.update(totalCost, iterations);
88
+
89
+ const pipelineResult = await runPipeline(defaultPipeline, pipelineContext, ctx.eventEmitter);
90
+ const currentPrd = pipelineResult.context.prd;
91
+
92
+ const handlerCtx = {
93
+ config: ctx.config,
94
+ prd: currentPrd,
95
+ prdPath: ctx.prdPath,
96
+ workdir: ctx.workdir,
97
+ featureDir: ctx.featureDir,
98
+ hooks: ctx.hooks,
99
+ feature: ctx.feature,
100
+ totalCost,
101
+ startTime: ctx.startTime,
102
+ runId: ctx.runId,
103
+ pluginRegistry: ctx.pluginRegistry,
104
+ story,
105
+ storiesToExecute,
106
+ routing: pipelineResult.context.routing ?? routing,
107
+ isBatchExecution,
108
+ allStoryMetrics,
109
+ storyGitRef,
110
+ interactionChain: ctx.interactionChain,
111
+ };
112
+
113
+ if (pipelineResult.success) {
114
+ const r = await handlePipelineSuccess(handlerCtx, pipelineResult);
115
+ return {
116
+ prd: r.prd,
117
+ storiesCompletedDelta: r.storiesCompletedDelta,
118
+ costDelta: r.costDelta,
119
+ prdDirty: r.prdDirty,
120
+ finalAction: pipelineResult.finalAction,
121
+ };
122
+ }
123
+ const r = await handlePipelineFailure(handlerCtx, pipelineResult);
124
+ return {
125
+ prd: r.prd,
126
+ storiesCompletedDelta: 0,
127
+ costDelta: 0,
128
+ prdDirty: r.prdDirty,
129
+ finalAction: pipelineResult.finalAction,
130
+ reason: pipelineResult.reason,
131
+ };
132
+ }
@@ -3,7 +3,6 @@
3
3
  */
4
4
 
5
5
  export { runAcceptanceLoop, type AcceptanceLoopContext, type AcceptanceLoopResult } from "./acceptance-loop";
6
- export { emitStoryComplete, type StoryCompleteEvent } from "./story-hooks";
7
6
  export { outputRunHeader, outputRunFooter, type RunHeaderOptions, type RunFooterOptions } from "./headless-formatter";
8
7
  export { handleParallelCompletion, type ParallelCompletionOptions } from "./parallel-lifecycle";
9
8
  export { handleRunCompletion, type RunCompletionOptions, type RunCompletionResult } from "./run-completion";
@@ -14,16 +14,16 @@ import type { PRD, UserStory } from "../../prd";
14
14
  import { countStories } from "../../prd";
15
15
  import { hasCommitsForStory } from "../../utils/git";
16
16
  import { parseBunTestOutput } from "../../verification";
17
+ import { runRectificationLoop } from "../../verification/rectification-loop";
18
+ import { fullSuite } from "../../verification/runners";
17
19
  import { reverseMapTestToSource } from "../../verification/smart-runner";
18
- import { runRectificationLoop } from "../post-verify-rectification";
19
- import { runVerification } from "../verification";
20
20
 
21
21
  /**
22
22
  * Injectable dependencies for testing (avoids mock.module() which leaks in Bun 1.x).
23
23
  * @internal - test use only.
24
24
  */
25
25
  export const _regressionDeps = {
26
- runVerification,
26
+ runVerification: fullSuite,
27
27
  runRectificationLoop,
28
28
  parseBunTestOutput,
29
29
  reverseMapTestToSource,
@@ -133,7 +133,7 @@ export async function runDeferredRegression(options: DeferredRegressionOptions):
133
133
 
134
134
  // Step 1: Run full test suite
135
135
  const fullSuiteResult = await _regressionDeps.runVerification({
136
- workingDirectory: workdir,
136
+ workdir: workdir,
137
137
  command: testCommand,
138
138
  timeoutSeconds,
139
139
  forceExit: config.quality.forceExit,
@@ -268,7 +268,7 @@ export async function runDeferredRegression(options: DeferredRegressionOptions):
268
268
  // Step 4: Re-run full suite to confirm
269
269
  logger?.info("regression", "Re-running full suite after rectification");
270
270
  const retryResult = await _regressionDeps.runVerification({
271
- workingDirectory: workdir,
271
+ workdir: workdir,
272
272
  command: testCommand,
273
273
  timeoutSeconds,
274
274
  forceExit: config.quality.forceExit,