@nathapp/nax 0.50.2 → 0.51.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 +30 -0
- package/dist/nax.js +579 -373
- package/package.json +1 -3
- package/bin/nax.ts +0 -1195
- package/src/acceptance/fix-generator.ts +0 -322
- package/src/acceptance/generator.ts +0 -423
- package/src/acceptance/index.ts +0 -42
- package/src/acceptance/refinement.ts +0 -224
- package/src/acceptance/templates/cli.ts +0 -47
- package/src/acceptance/templates/component.ts +0 -78
- package/src/acceptance/templates/e2e.ts +0 -43
- package/src/acceptance/templates/index.ts +0 -21
- package/src/acceptance/templates/snapshot.ts +0 -50
- package/src/acceptance/templates/unit.ts +0 -48
- package/src/acceptance/types.ts +0 -135
- package/src/agents/acp/adapter.ts +0 -888
- package/src/agents/acp/cost.ts +0 -9
- package/src/agents/acp/index.ts +0 -7
- package/src/agents/acp/interaction-bridge.ts +0 -126
- package/src/agents/acp/parser.ts +0 -119
- package/src/agents/acp/spawn-client.ts +0 -373
- package/src/agents/acp/types.ts +0 -22
- package/src/agents/aider/adapter.ts +0 -135
- package/src/agents/claude/adapter.ts +0 -258
- package/src/agents/claude/complete.ts +0 -80
- package/src/agents/claude/cost.ts +0 -16
- package/src/agents/claude/execution.ts +0 -215
- package/src/agents/claude/index.ts +0 -3
- package/src/agents/claude/interactive.ts +0 -77
- package/src/agents/claude/plan.ts +0 -179
- package/src/agents/codex/adapter.ts +0 -153
- package/src/agents/cost/calculate.ts +0 -154
- package/src/agents/cost/index.ts +0 -10
- package/src/agents/cost/parse.ts +0 -97
- package/src/agents/cost/pricing.ts +0 -59
- package/src/agents/cost/types.ts +0 -45
- package/src/agents/gemini/adapter.ts +0 -177
- package/src/agents/index.ts +0 -18
- package/src/agents/opencode/adapter.ts +0 -106
- package/src/agents/registry.ts +0 -136
- package/src/agents/shared/decompose.ts +0 -154
- package/src/agents/shared/model-resolution.ts +0 -43
- package/src/agents/shared/types-extended.ts +0 -164
- package/src/agents/shared/validation.ts +0 -69
- package/src/agents/shared/version-detection.ts +0 -109
- package/src/agents/types.ts +0 -205
- package/src/analyze/classifier.ts +0 -282
- package/src/analyze/index.ts +0 -16
- package/src/analyze/scanner.ts +0 -171
- package/src/analyze/types.ts +0 -51
- package/src/cli/accept.ts +0 -108
- package/src/cli/agents.ts +0 -87
- package/src/cli/analyze-parser.ts +0 -291
- package/src/cli/analyze.ts +0 -352
- package/src/cli/config-descriptions.ts +0 -218
- package/src/cli/config-diff.ts +0 -103
- package/src/cli/config-display.ts +0 -285
- package/src/cli/config-get.ts +0 -55
- package/src/cli/config.ts +0 -14
- package/src/cli/constitution.ts +0 -17
- package/src/cli/diagnose-analysis.ts +0 -159
- package/src/cli/diagnose-formatter.ts +0 -87
- package/src/cli/diagnose.ts +0 -203
- package/src/cli/generate.ts +0 -250
- package/src/cli/index.ts +0 -42
- package/src/cli/init-context.ts +0 -405
- package/src/cli/init-detect.ts +0 -303
- package/src/cli/init.ts +0 -296
- package/src/cli/interact.ts +0 -295
- package/src/cli/plan.ts +0 -509
- package/src/cli/plugins.ts +0 -122
- package/src/cli/prompts-export.ts +0 -58
- package/src/cli/prompts-init.ts +0 -200
- package/src/cli/prompts-main.ts +0 -183
- package/src/cli/prompts-shared.ts +0 -70
- package/src/cli/prompts-tdd.ts +0 -88
- package/src/cli/prompts.ts +0 -17
- package/src/cli/runs.ts +0 -174
- package/src/cli/status-cost.ts +0 -151
- package/src/cli/status-features.ts +0 -405
- package/src/cli/status.ts +0 -13
- package/src/commands/common.ts +0 -171
- package/src/commands/diagnose.ts +0 -17
- package/src/commands/index.ts +0 -9
- package/src/commands/logs-formatter.ts +0 -201
- package/src/commands/logs-reader.ts +0 -171
- package/src/commands/logs.ts +0 -103
- package/src/commands/precheck.ts +0 -86
- package/src/commands/runs.ts +0 -220
- package/src/commands/unlock.ts +0 -96
- package/src/config/defaults.ts +0 -217
- package/src/config/index.ts +0 -22
- package/src/config/loader.ts +0 -143
- package/src/config/merge.ts +0 -106
- package/src/config/merger.ts +0 -147
- package/src/config/path-security.ts +0 -121
- package/src/config/paths.ts +0 -27
- package/src/config/permissions.ts +0 -63
- package/src/config/runtime-types.ts +0 -520
- package/src/config/schema-types.ts +0 -53
- package/src/config/schema.ts +0 -60
- package/src/config/schemas.ts +0 -425
- package/src/config/test-strategy.ts +0 -71
- package/src/config/types.ts +0 -57
- package/src/config/validate.ts +0 -103
- package/src/constitution/generator.ts +0 -158
- package/src/constitution/generators/aider.ts +0 -41
- package/src/constitution/generators/claude.ts +0 -35
- package/src/constitution/generators/cursor.ts +0 -36
- package/src/constitution/generators/opencode.ts +0 -38
- package/src/constitution/generators/types.ts +0 -33
- package/src/constitution/generators/windsurf.ts +0 -36
- package/src/constitution/index.ts +0 -11
- package/src/constitution/loader.ts +0 -121
- package/src/constitution/types.ts +0 -31
- package/src/context/auto-detect.ts +0 -228
- package/src/context/builder.ts +0 -299
- package/src/context/elements.ts +0 -122
- package/src/context/formatter.ts +0 -107
- package/src/context/generator.ts +0 -343
- package/src/context/generators/aider.ts +0 -34
- package/src/context/generators/claude.ts +0 -28
- package/src/context/generators/codex.ts +0 -28
- package/src/context/generators/cursor.ts +0 -28
- package/src/context/generators/gemini.ts +0 -28
- package/src/context/generators/opencode.ts +0 -30
- package/src/context/generators/windsurf.ts +0 -28
- package/src/context/greenfield.ts +0 -114
- package/src/context/index.ts +0 -34
- package/src/context/injector.ts +0 -279
- package/src/context/parent-context.ts +0 -39
- package/src/context/test-scanner.ts +0 -370
- package/src/context/types.ts +0 -98
- package/src/decompose/apply.ts +0 -50
- package/src/decompose/builder.ts +0 -181
- package/src/decompose/index.ts +0 -8
- package/src/decompose/sections/codebase.ts +0 -26
- package/src/decompose/sections/constraints.ts +0 -32
- package/src/decompose/sections/index.ts +0 -4
- package/src/decompose/sections/sibling-stories.ts +0 -25
- package/src/decompose/sections/target-story.ts +0 -31
- package/src/decompose/types.ts +0 -55
- package/src/decompose/validators/complexity.ts +0 -45
- package/src/decompose/validators/coverage.ts +0 -134
- package/src/decompose/validators/dependency.ts +0 -91
- package/src/decompose/validators/index.ts +0 -35
- package/src/decompose/validators/overlap.ts +0 -128
- package/src/errors.ts +0 -67
- package/src/execution/batching.ts +0 -157
- package/src/execution/crash-heartbeat.ts +0 -77
- package/src/execution/crash-recovery.ts +0 -79
- package/src/execution/crash-signals.ts +0 -165
- package/src/execution/crash-writer.ts +0 -154
- package/src/execution/deferred-review.ts +0 -105
- package/src/execution/dry-run.ts +0 -81
- package/src/execution/escalation/escalation.ts +0 -46
- package/src/execution/escalation/index.ts +0 -13
- package/src/execution/escalation/tier-escalation.ts +0 -346
- package/src/execution/escalation/tier-outcome.ts +0 -143
- package/src/execution/executor-types.ts +0 -73
- package/src/execution/helpers.ts +0 -38
- package/src/execution/index.ts +0 -27
- package/src/execution/iteration-runner.ts +0 -160
- package/src/execution/lifecycle/acceptance-loop.ts +0 -280
- package/src/execution/lifecycle/headless-formatter.ts +0 -83
- package/src/execution/lifecycle/index.ts +0 -11
- package/src/execution/lifecycle/parallel-lifecycle.ts +0 -101
- package/src/execution/lifecycle/precheck-runner.ts +0 -140
- package/src/execution/lifecycle/run-cleanup.ts +0 -81
- package/src/execution/lifecycle/run-completion.ts +0 -247
- package/src/execution/lifecycle/run-initialization.ts +0 -187
- package/src/execution/lifecycle/run-regression.ts +0 -305
- package/src/execution/lifecycle/run-setup.ts +0 -240
- package/src/execution/lifecycle/story-size-prompts.ts +0 -123
- package/src/execution/lock.ts +0 -129
- package/src/execution/parallel-coordinator.ts +0 -281
- package/src/execution/parallel-executor-rectification-pass.ts +0 -117
- package/src/execution/parallel-executor-rectify.ts +0 -136
- package/src/execution/parallel-executor.ts +0 -330
- package/src/execution/parallel-worker.ts +0 -149
- package/src/execution/parallel.ts +0 -13
- package/src/execution/pid-registry.ts +0 -275
- package/src/execution/pipeline-result-handler.ts +0 -221
- package/src/execution/progress.ts +0 -27
- package/src/execution/queue-handler.ts +0 -109
- package/src/execution/runner-completion.ts +0 -171
- package/src/execution/runner-execution.ts +0 -243
- package/src/execution/runner-setup.ts +0 -86
- package/src/execution/runner.ts +0 -265
- package/src/execution/sequential-executor.ts +0 -219
- package/src/execution/status-file.ts +0 -264
- package/src/execution/status-writer.ts +0 -181
- package/src/execution/story-context.ts +0 -266
- package/src/execution/story-selector.ts +0 -76
- package/src/execution/test-output-parser.ts +0 -14
- package/src/execution/timeout-handler.ts +0 -100
- package/src/hooks/index.ts +0 -2
- package/src/hooks/runner.ts +0 -280
- package/src/hooks/types.ts +0 -79
- package/src/interaction/chain.ts +0 -170
- package/src/interaction/index.ts +0 -61
- package/src/interaction/init.ts +0 -84
- package/src/interaction/plugins/auto.ts +0 -243
- package/src/interaction/plugins/cli.ts +0 -300
- package/src/interaction/plugins/telegram.ts +0 -384
- package/src/interaction/plugins/webhook.ts +0 -286
- package/src/interaction/state.ts +0 -171
- package/src/interaction/triggers.ts +0 -250
- package/src/interaction/types.ts +0 -170
- package/src/logger/formatters.ts +0 -84
- package/src/logger/index.ts +0 -16
- package/src/logger/logger.ts +0 -296
- package/src/logger/types.ts +0 -48
- package/src/logging/formatter.ts +0 -355
- package/src/logging/index.ts +0 -22
- package/src/logging/types.ts +0 -93
- package/src/metrics/aggregator.ts +0 -191
- package/src/metrics/index.ts +0 -14
- package/src/metrics/tracker.ts +0 -200
- package/src/metrics/types.ts +0 -115
- package/src/optimizer/index.ts +0 -63
- package/src/optimizer/noop.optimizer.ts +0 -24
- package/src/optimizer/rule-based.optimizer.ts +0 -248
- package/src/optimizer/types.ts +0 -53
- package/src/pipeline/event-bus.ts +0 -297
- package/src/pipeline/events.ts +0 -130
- package/src/pipeline/index.ts +0 -19
- package/src/pipeline/runner.ts +0 -149
- package/src/pipeline/stages/acceptance-setup.ts +0 -140
- package/src/pipeline/stages/acceptance.ts +0 -215
- package/src/pipeline/stages/autofix.ts +0 -262
- package/src/pipeline/stages/completion.ts +0 -110
- package/src/pipeline/stages/constitution.ts +0 -63
- package/src/pipeline/stages/context.ts +0 -122
- package/src/pipeline/stages/execution.ts +0 -359
- package/src/pipeline/stages/index.ts +0 -86
- package/src/pipeline/stages/optimizer.ts +0 -74
- package/src/pipeline/stages/prompt.ts +0 -79
- package/src/pipeline/stages/queue-check.ts +0 -103
- package/src/pipeline/stages/rectify.ts +0 -101
- package/src/pipeline/stages/regression.ts +0 -99
- package/src/pipeline/stages/review.ts +0 -94
- package/src/pipeline/stages/routing.ts +0 -276
- package/src/pipeline/stages/verify.ts +0 -286
- package/src/pipeline/subscribers/events-writer.ts +0 -135
- package/src/pipeline/subscribers/hooks.ts +0 -179
- package/src/pipeline/subscribers/interaction.ts +0 -103
- package/src/pipeline/subscribers/registry.ts +0 -73
- package/src/pipeline/subscribers/reporters.ts +0 -174
- package/src/pipeline/types.ts +0 -220
- package/src/plugins/extensions.ts +0 -225
- package/src/plugins/index.ts +0 -33
- package/src/plugins/loader.ts +0 -352
- package/src/plugins/plugin-logger.ts +0 -41
- package/src/plugins/registry.ts +0 -168
- package/src/plugins/types.ts +0 -206
- package/src/plugins/validator.ts +0 -352
- package/src/prd/index.ts +0 -220
- package/src/prd/schema.ts +0 -268
- package/src/prd/types.ts +0 -273
- package/src/prd/validate.ts +0 -41
- package/src/precheck/checks-agents.ts +0 -63
- package/src/precheck/checks-blockers.ts +0 -23
- package/src/precheck/checks-cli.ts +0 -68
- package/src/precheck/checks-config.ts +0 -102
- package/src/precheck/checks-git.ts +0 -117
- package/src/precheck/checks-system.ts +0 -101
- package/src/precheck/checks-warnings.ts +0 -221
- package/src/precheck/checks.ts +0 -36
- package/src/precheck/index.ts +0 -374
- package/src/precheck/story-size-gate.ts +0 -144
- package/src/precheck/types.ts +0 -31
- package/src/prompts/builder.ts +0 -166
- package/src/prompts/index.ts +0 -2
- package/src/prompts/loader.ts +0 -43
- package/src/prompts/sections/conventions.ts +0 -19
- package/src/prompts/sections/hermetic.ts +0 -41
- package/src/prompts/sections/index.ts +0 -12
- package/src/prompts/sections/isolation.ts +0 -70
- package/src/prompts/sections/role-task.ts +0 -182
- package/src/prompts/sections/story.ts +0 -55
- package/src/prompts/sections/verdict.ts +0 -70
- package/src/prompts/types.ts +0 -21
- package/src/queue/index.ts +0 -2
- package/src/queue/manager.ts +0 -254
- package/src/queue/types.ts +0 -54
- package/src/review/index.ts +0 -8
- package/src/review/orchestrator.ts +0 -154
- package/src/review/runner.ts +0 -303
- package/src/review/types.ts +0 -70
- package/src/routing/batch-route.ts +0 -35
- package/src/routing/builder.ts +0 -81
- package/src/routing/chain.ts +0 -75
- package/src/routing/content-hash.ts +0 -25
- package/src/routing/index.ts +0 -20
- package/src/routing/loader.ts +0 -62
- package/src/routing/router.ts +0 -305
- package/src/routing/strategies/adaptive.ts +0 -215
- package/src/routing/strategies/index.ts +0 -8
- package/src/routing/strategies/keyword.ts +0 -180
- package/src/routing/strategies/llm-prompts.ts +0 -224
- package/src/routing/strategies/llm.ts +0 -320
- package/src/routing/strategies/manual.ts +0 -50
- package/src/routing/strategy.ts +0 -102
- package/src/tdd/cleanup.ts +0 -120
- package/src/tdd/index.ts +0 -22
- package/src/tdd/isolation.ts +0 -117
- package/src/tdd/orchestrator.ts +0 -406
- package/src/tdd/prompts.ts +0 -40
- package/src/tdd/rectification-gate.ts +0 -274
- package/src/tdd/session-runner.ts +0 -263
- package/src/tdd/types.ts +0 -84
- package/src/tdd/verdict-reader.ts +0 -266
- package/src/tdd/verdict.ts +0 -152
- package/src/tui/App.tsx +0 -265
- package/src/tui/components/AgentPanel.tsx +0 -75
- package/src/tui/components/CostOverlay.tsx +0 -118
- package/src/tui/components/HelpOverlay.tsx +0 -107
- package/src/tui/components/StatusBar.tsx +0 -63
- package/src/tui/components/StoriesPanel.tsx +0 -177
- package/src/tui/hooks/useKeyboard.ts +0 -142
- package/src/tui/hooks/useLayout.ts +0 -137
- package/src/tui/hooks/usePipelineEvents.ts +0 -183
- package/src/tui/hooks/usePty.ts +0 -189
- package/src/tui/index.tsx +0 -38
- package/src/tui/types.ts +0 -76
- package/src/utils/errors.ts +0 -12
- package/src/utils/git.ts +0 -245
- package/src/utils/json-file.ts +0 -72
- package/src/utils/log-test-output.ts +0 -25
- package/src/utils/path-security.ts +0 -73
- package/src/utils/queue-writer.ts +0 -54
- package/src/verification/crash-detector.ts +0 -34
- package/src/verification/executor.ts +0 -250
- package/src/verification/index.ts +0 -12
- package/src/verification/orchestrator-types.ts +0 -154
- package/src/verification/orchestrator.ts +0 -76
- package/src/verification/parser.ts +0 -220
- package/src/verification/rectification-loop.ts +0 -172
- package/src/verification/rectification.ts +0 -108
- package/src/verification/runners.ts +0 -129
- package/src/verification/smart-runner.ts +0 -307
- package/src/verification/strategies/acceptance.ts +0 -136
- package/src/verification/strategies/regression.ts +0 -90
- package/src/verification/strategies/scoped.ts +0 -154
- package/src/verification/types.ts +0 -117
- package/src/version.ts +0 -40
- package/src/worktree/dispatcher.ts +0 -6
- package/src/worktree/index.ts +0 -2
- package/src/worktree/manager.ts +0 -193
- package/src/worktree/merge.ts +0 -302
- package/src/worktree/types.ts +0 -4
|
@@ -1,305 +0,0 @@
|
|
|
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 { AgentGetFn } from "../../pipeline/types";
|
|
14
|
-
import type { PRD, UserStory } from "../../prd";
|
|
15
|
-
import { countStories } from "../../prd";
|
|
16
|
-
import { hasCommitsForStory } from "../../utils/git";
|
|
17
|
-
import { parseBunTestOutput } from "../../verification";
|
|
18
|
-
import { runRectificationLoop } from "../../verification/rectification-loop";
|
|
19
|
-
import { fullSuite } from "../../verification/runners";
|
|
20
|
-
import { reverseMapTestToSource } from "../../verification/smart-runner";
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Injectable dependencies for testing (avoids mock.module() which leaks in Bun 1.x).
|
|
24
|
-
* @internal - test use only.
|
|
25
|
-
*/
|
|
26
|
-
export const _regressionDeps = {
|
|
27
|
-
runVerification: fullSuite,
|
|
28
|
-
runRectificationLoop,
|
|
29
|
-
parseBunTestOutput,
|
|
30
|
-
reverseMapTestToSource,
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export interface DeferredRegressionOptions {
|
|
34
|
-
config: NaxConfig;
|
|
35
|
-
prd: PRD;
|
|
36
|
-
workdir: string;
|
|
37
|
-
/** Protocol-aware agent resolver (ACP wiring). Falls back to static getAgent when absent. */
|
|
38
|
-
agentGetFn?: AgentGetFn;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface DeferredRegressionResult {
|
|
42
|
-
success: boolean;
|
|
43
|
-
failedTests: number;
|
|
44
|
-
passedTests: number;
|
|
45
|
-
rectificationAttempts: number;
|
|
46
|
-
affectedStories: string[];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Map a test file to the story responsible for it via git log.
|
|
51
|
-
*
|
|
52
|
-
* Searches recent commits for story IDs in the format US-NNN.
|
|
53
|
-
* Returns the first matching story ID, or undefined if not found.
|
|
54
|
-
*/
|
|
55
|
-
async function findResponsibleStory(
|
|
56
|
-
testFile: string,
|
|
57
|
-
workdir: string,
|
|
58
|
-
passedStories: UserStory[],
|
|
59
|
-
): Promise<UserStory | undefined> {
|
|
60
|
-
const logger = getSafeLogger();
|
|
61
|
-
|
|
62
|
-
// Try each passed story in reverse order (most recent first)
|
|
63
|
-
for (let i = passedStories.length - 1; i >= 0; i--) {
|
|
64
|
-
const story = passedStories[i];
|
|
65
|
-
const hasCommits = await hasCommitsForStory(workdir, story.id, 50);
|
|
66
|
-
if (hasCommits) {
|
|
67
|
-
logger?.info("regression", `Mapped test to story ${story.id}`, { testFile });
|
|
68
|
-
return story;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Run deferred regression gate after all stories complete.
|
|
77
|
-
*
|
|
78
|
-
* Steps:
|
|
79
|
-
* 1. Run full test suite
|
|
80
|
-
* 2. If failures, reverse-map test files to source files to stories
|
|
81
|
-
* 3. For each affected story, attempt targeted rectification
|
|
82
|
-
* 4. Re-run full suite to confirm fixes
|
|
83
|
-
* 5. Return results with affected story list
|
|
84
|
-
*/
|
|
85
|
-
export async function runDeferredRegression(options: DeferredRegressionOptions): Promise<DeferredRegressionResult> {
|
|
86
|
-
const logger = getSafeLogger();
|
|
87
|
-
const { config, prd, workdir, agentGetFn } = options;
|
|
88
|
-
|
|
89
|
-
// Check if regression gate is deferred
|
|
90
|
-
const regressionMode = config.execution.regressionGate?.mode ?? "deferred";
|
|
91
|
-
if (regressionMode === "disabled") {
|
|
92
|
-
logger?.info("regression", "Deferred regression gate disabled");
|
|
93
|
-
return {
|
|
94
|
-
success: true,
|
|
95
|
-
failedTests: 0,
|
|
96
|
-
passedTests: 0,
|
|
97
|
-
rectificationAttempts: 0,
|
|
98
|
-
affectedStories: [],
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (regressionMode !== "deferred") {
|
|
103
|
-
logger?.info("regression", "Regression gate mode is not deferred, skipping");
|
|
104
|
-
return {
|
|
105
|
-
success: true,
|
|
106
|
-
failedTests: 0,
|
|
107
|
-
passedTests: 0,
|
|
108
|
-
rectificationAttempts: 0,
|
|
109
|
-
affectedStories: [],
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const testCommand = config.quality.commands.test ?? "bun test";
|
|
114
|
-
const timeoutSeconds = config.execution.regressionGate?.timeoutSeconds ?? 120;
|
|
115
|
-
const maxRectificationAttempts = config.execution.regressionGate?.maxRectificationAttempts ?? 2;
|
|
116
|
-
|
|
117
|
-
// Only check stories that have been marked as passed
|
|
118
|
-
const counts = countStories(prd);
|
|
119
|
-
const passedStories = prd.userStories.filter((s) => s.status === "passed");
|
|
120
|
-
|
|
121
|
-
if (passedStories.length === 0) {
|
|
122
|
-
logger?.info("regression", "No passed stories to verify (partial completion)");
|
|
123
|
-
return {
|
|
124
|
-
success: true,
|
|
125
|
-
failedTests: 0,
|
|
126
|
-
passedTests: 0,
|
|
127
|
-
rectificationAttempts: 0,
|
|
128
|
-
affectedStories: [],
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
logger?.info("regression", "Running deferred full-suite regression gate", {
|
|
133
|
-
totalStories: counts.total,
|
|
134
|
-
passedStories: passedStories.length,
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
// Step 1: Run full test suite
|
|
138
|
-
const fullSuiteResult = await _regressionDeps.runVerification({
|
|
139
|
-
workdir: workdir,
|
|
140
|
-
command: testCommand,
|
|
141
|
-
timeoutSeconds,
|
|
142
|
-
forceExit: config.quality.forceExit,
|
|
143
|
-
detectOpenHandles: config.quality.detectOpenHandles,
|
|
144
|
-
detectOpenHandlesRetries: config.quality.detectOpenHandlesRetries,
|
|
145
|
-
timeoutRetryCount: 0,
|
|
146
|
-
gracePeriodMs: config.quality.gracePeriodMs,
|
|
147
|
-
drainTimeoutMs: config.quality.drainTimeoutMs,
|
|
148
|
-
shell: config.quality.shell,
|
|
149
|
-
stripEnvVars: config.quality.stripEnvVars,
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
if (fullSuiteResult.success) {
|
|
153
|
-
logger?.info("regression", "Full suite passed");
|
|
154
|
-
return {
|
|
155
|
-
success: true,
|
|
156
|
-
failedTests: 0,
|
|
157
|
-
passedTests: fullSuiteResult.passCount ?? 0,
|
|
158
|
-
rectificationAttempts: 0,
|
|
159
|
-
affectedStories: [],
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Handle timeout
|
|
164
|
-
const acceptOnTimeout = config.execution.regressionGate?.acceptOnTimeout ?? true;
|
|
165
|
-
if (fullSuiteResult.status === "TIMEOUT" && acceptOnTimeout) {
|
|
166
|
-
logger?.warn("regression", "Full-suite regression gate timed out (accepted as pass)");
|
|
167
|
-
return {
|
|
168
|
-
success: true,
|
|
169
|
-
failedTests: 0,
|
|
170
|
-
passedTests: 0,
|
|
171
|
-
rectificationAttempts: 0,
|
|
172
|
-
affectedStories: [],
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (!fullSuiteResult.output) {
|
|
177
|
-
logger?.error("regression", "Full suite failed with no output");
|
|
178
|
-
return {
|
|
179
|
-
success: false,
|
|
180
|
-
failedTests: fullSuiteResult.failCount ?? 0,
|
|
181
|
-
passedTests: fullSuiteResult.passCount ?? 0,
|
|
182
|
-
rectificationAttempts: 0,
|
|
183
|
-
affectedStories: [],
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Step 2: Parse failures and map to source files to stories
|
|
188
|
-
const testSummary = _regressionDeps.parseBunTestOutput(fullSuiteResult.output);
|
|
189
|
-
const affectedStories = new Set<string>();
|
|
190
|
-
const affectedStoriesObjs = new Map<string, UserStory>();
|
|
191
|
-
|
|
192
|
-
logger?.warn("regression", "Regression detected", {
|
|
193
|
-
failedTests: testSummary.failed,
|
|
194
|
-
passedTests: testSummary.passed,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// Extract test file paths from failures
|
|
198
|
-
const testFilesInFailures = new Set<string>();
|
|
199
|
-
for (const failure of testSummary.failures) {
|
|
200
|
-
if (failure.file) {
|
|
201
|
-
testFilesInFailures.add(failure.file);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (testFilesInFailures.size === 0) {
|
|
206
|
-
logger?.warn("regression", "No test files found in failures (unmapped)");
|
|
207
|
-
// Mark all passed stories for re-verification
|
|
208
|
-
for (const story of passedStories) {
|
|
209
|
-
affectedStories.add(story.id);
|
|
210
|
-
affectedStoriesObjs.set(story.id, story);
|
|
211
|
-
}
|
|
212
|
-
} else {
|
|
213
|
-
// Map test files to source files to stories
|
|
214
|
-
const testFilesArray = Array.from(testFilesInFailures);
|
|
215
|
-
const sourceFilesArray = _regressionDeps.reverseMapTestToSource(testFilesArray, workdir);
|
|
216
|
-
|
|
217
|
-
logger?.info("regression", "Mapped test files to source files", {
|
|
218
|
-
testFiles: testFilesArray.length,
|
|
219
|
-
sourceFiles: sourceFilesArray.length,
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
for (const testFile of testFilesArray) {
|
|
223
|
-
const responsibleStory = await findResponsibleStory(testFile, workdir, passedStories);
|
|
224
|
-
if (responsibleStory) {
|
|
225
|
-
affectedStories.add(responsibleStory.id);
|
|
226
|
-
affectedStoriesObjs.set(responsibleStory.id, responsibleStory);
|
|
227
|
-
} else {
|
|
228
|
-
logger?.warn("regression", "Could not map test file to story", { testFile });
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (affectedStories.size === 0) {
|
|
234
|
-
logger?.warn("regression", "No stories could be mapped to failures");
|
|
235
|
-
return {
|
|
236
|
-
success: false,
|
|
237
|
-
failedTests: testSummary.failed,
|
|
238
|
-
passedTests: testSummary.passed,
|
|
239
|
-
rectificationAttempts: 0,
|
|
240
|
-
affectedStories: Array.from(affectedStories),
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Step 3: Attempt rectification per story
|
|
245
|
-
let rectificationAttempts = 0;
|
|
246
|
-
const affectedStoriesList = Array.from(affectedStoriesObjs.values());
|
|
247
|
-
|
|
248
|
-
for (const story of affectedStoriesList) {
|
|
249
|
-
for (let attempt = 0; attempt < maxRectificationAttempts; attempt++) {
|
|
250
|
-
rectificationAttempts++;
|
|
251
|
-
|
|
252
|
-
logger?.info("regression", `Rectifying story ${story.id} (attempt ${attempt + 1}/${maxRectificationAttempts})`);
|
|
253
|
-
|
|
254
|
-
const fixed = await _regressionDeps.runRectificationLoop({
|
|
255
|
-
config,
|
|
256
|
-
workdir,
|
|
257
|
-
story,
|
|
258
|
-
testCommand,
|
|
259
|
-
timeoutSeconds,
|
|
260
|
-
testOutput: fullSuiteResult.output,
|
|
261
|
-
promptPrefix: `# DEFERRED REGRESSION: Full-Suite Failures\n\nYour story ${story.id} broke tests in the full suite. Fix these regressions.`,
|
|
262
|
-
agentGetFn,
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
if (fixed) {
|
|
266
|
-
logger?.info("regression", `Story ${story.id} rectified successfully`);
|
|
267
|
-
break; // Move to next story
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Step 4: Re-run full suite to confirm
|
|
273
|
-
logger?.info("regression", "Re-running full suite after rectification");
|
|
274
|
-
const retryResult = await _regressionDeps.runVerification({
|
|
275
|
-
workdir: workdir,
|
|
276
|
-
command: testCommand,
|
|
277
|
-
timeoutSeconds,
|
|
278
|
-
forceExit: config.quality.forceExit,
|
|
279
|
-
detectOpenHandles: config.quality.detectOpenHandles,
|
|
280
|
-
detectOpenHandlesRetries: config.quality.detectOpenHandlesRetries,
|
|
281
|
-
timeoutRetryCount: 0,
|
|
282
|
-
gracePeriodMs: config.quality.gracePeriodMs,
|
|
283
|
-
drainTimeoutMs: config.quality.drainTimeoutMs,
|
|
284
|
-
shell: config.quality.shell,
|
|
285
|
-
stripEnvVars: config.quality.stripEnvVars,
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
const success = retryResult.success || (retryResult.status === "TIMEOUT" && acceptOnTimeout);
|
|
289
|
-
|
|
290
|
-
if (success) {
|
|
291
|
-
logger?.info("regression", "Deferred regression gate passed after rectification");
|
|
292
|
-
} else {
|
|
293
|
-
logger?.warn("regression", "Deferred regression gate still failing after rectification", {
|
|
294
|
-
remainingFailures: retryResult.failCount,
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
return {
|
|
299
|
-
success,
|
|
300
|
-
failedTests: retryResult.failCount ?? 0,
|
|
301
|
-
passedTests: retryResult.passCount ?? 0,
|
|
302
|
-
rectificationAttempts,
|
|
303
|
-
affectedStories: Array.from(affectedStories),
|
|
304
|
-
};
|
|
305
|
-
}
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Run Setup — Initial Setup Logic
|
|
3
|
-
*
|
|
4
|
-
* Handles the initial setup phase before the main execution loop:
|
|
5
|
-
* - Status writer initialization
|
|
6
|
-
* - PID registry cleanup
|
|
7
|
-
* - Crash handler installation
|
|
8
|
-
* - Lock acquisition
|
|
9
|
-
* - Plugin loading
|
|
10
|
-
* - PRD loading
|
|
11
|
-
* - Precheck validation
|
|
12
|
-
* - Run initialization
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import * as os from "node:os";
|
|
16
|
-
import path from "node:path";
|
|
17
|
-
import type { NaxConfig } from "../../config";
|
|
18
|
-
import { LockAcquisitionError } from "../../errors";
|
|
19
|
-
import type { LoadedHooksConfig } from "../../hooks";
|
|
20
|
-
import { fireHook } from "../../hooks";
|
|
21
|
-
import type { InteractionChain } from "../../interaction";
|
|
22
|
-
import { initInteractionChain } from "../../interaction";
|
|
23
|
-
import { getSafeLogger } from "../../logger";
|
|
24
|
-
import { pipelineEventBus } from "../../pipeline/event-bus";
|
|
25
|
-
import type { AgentGetFn } from "../../pipeline/types";
|
|
26
|
-
import { loadPlugins } from "../../plugins/loader";
|
|
27
|
-
import type { PluginRegistry } from "../../plugins/registry";
|
|
28
|
-
import type { PRD } from "../../prd";
|
|
29
|
-
import { loadPRD } from "../../prd";
|
|
30
|
-
import { NAX_BUILD_INFO, NAX_COMMIT, NAX_VERSION } from "../../version";
|
|
31
|
-
import { installCrashHandlers } from "../crash-recovery";
|
|
32
|
-
import { acquireLock, hookCtx, releaseLock } from "../helpers";
|
|
33
|
-
import { PidRegistry } from "../pid-registry";
|
|
34
|
-
import { StatusWriter } from "../status-writer";
|
|
35
|
-
|
|
36
|
-
export interface RunSetupOptions {
|
|
37
|
-
prdPath: string;
|
|
38
|
-
workdir: string;
|
|
39
|
-
config: NaxConfig;
|
|
40
|
-
hooks: LoadedHooksConfig;
|
|
41
|
-
feature: string;
|
|
42
|
-
featureDir?: string;
|
|
43
|
-
dryRun: boolean;
|
|
44
|
-
statusFile: string;
|
|
45
|
-
logFilePath?: string;
|
|
46
|
-
runId: string;
|
|
47
|
-
startedAt: string;
|
|
48
|
-
startTime: number;
|
|
49
|
-
skipPrecheck: boolean;
|
|
50
|
-
headless: boolean;
|
|
51
|
-
formatterMode: "quiet" | "normal" | "verbose" | "json";
|
|
52
|
-
getTotalCost: () => number;
|
|
53
|
-
getIterations: () => number;
|
|
54
|
-
// BUG-017: Additional getters for run.complete event on SIGTERM
|
|
55
|
-
getStoriesCompleted: () => number;
|
|
56
|
-
getTotalStories: () => number;
|
|
57
|
-
/** Protocol-aware agent resolver — passed from runner.ts registry */
|
|
58
|
-
agentGetFn?: AgentGetFn;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface RunSetupResult {
|
|
62
|
-
statusWriter: StatusWriter;
|
|
63
|
-
pidRegistry: PidRegistry;
|
|
64
|
-
cleanupCrashHandlers: () => void;
|
|
65
|
-
pluginRegistry: PluginRegistry;
|
|
66
|
-
prd: PRD;
|
|
67
|
-
storyCounts: {
|
|
68
|
-
total: number;
|
|
69
|
-
passed: number;
|
|
70
|
-
pending: number;
|
|
71
|
-
failed: number;
|
|
72
|
-
};
|
|
73
|
-
interactionChain: InteractionChain | null;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Execute initial setup phase
|
|
78
|
-
*/
|
|
79
|
-
export async function setupRun(options: RunSetupOptions): Promise<RunSetupResult> {
|
|
80
|
-
const logger = getSafeLogger();
|
|
81
|
-
const {
|
|
82
|
-
prdPath,
|
|
83
|
-
workdir,
|
|
84
|
-
config,
|
|
85
|
-
hooks,
|
|
86
|
-
feature,
|
|
87
|
-
dryRun,
|
|
88
|
-
statusFile,
|
|
89
|
-
logFilePath,
|
|
90
|
-
runId,
|
|
91
|
-
startedAt,
|
|
92
|
-
startTime,
|
|
93
|
-
skipPrecheck,
|
|
94
|
-
headless,
|
|
95
|
-
formatterMode,
|
|
96
|
-
getTotalCost,
|
|
97
|
-
getIterations,
|
|
98
|
-
} = options;
|
|
99
|
-
|
|
100
|
-
// ── Status writer (encapsulates status file state and write logic) ───────
|
|
101
|
-
const statusWriter = new StatusWriter(statusFile, config, {
|
|
102
|
-
runId,
|
|
103
|
-
feature,
|
|
104
|
-
startedAt,
|
|
105
|
-
dryRun,
|
|
106
|
-
startTimeMs: startTime,
|
|
107
|
-
pid: process.pid,
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// ── PID registry for orphan process cleanup (BUG-002) ───────
|
|
111
|
-
const pidRegistry = new PidRegistry(workdir);
|
|
112
|
-
|
|
113
|
-
// Cleanup stale PIDs from previous crashed runs
|
|
114
|
-
await pidRegistry.cleanupStale();
|
|
115
|
-
|
|
116
|
-
// Install crash handlers for signal recovery (US-007, BUG-1+MEM-1 fix: pass getters, cleanup in finally)
|
|
117
|
-
const cleanupCrashHandlers = installCrashHandlers({
|
|
118
|
-
statusWriter,
|
|
119
|
-
getTotalCost,
|
|
120
|
-
getIterations,
|
|
121
|
-
jsonlFilePath: logFilePath,
|
|
122
|
-
pidRegistry,
|
|
123
|
-
// BUG-017: Pass context for run.complete event on SIGTERM
|
|
124
|
-
runId: options.runId,
|
|
125
|
-
feature: options.feature,
|
|
126
|
-
featureDir: options.featureDir,
|
|
127
|
-
getStartTime: () => options.startTime,
|
|
128
|
-
getTotalStories: options.getTotalStories,
|
|
129
|
-
getStoriesCompleted: options.getStoriesCompleted,
|
|
130
|
-
emitError: (reason: string) => {
|
|
131
|
-
pipelineEventBus.emit({ type: "run:errored", reason, feature: options.feature });
|
|
132
|
-
},
|
|
133
|
-
// Close open ACP sessions on SIGINT/SIGTERM so acpx processes don't stay alive
|
|
134
|
-
onShutdown: async () => {
|
|
135
|
-
const { sweepFeatureSessions } = await import("../../agents/acp/adapter");
|
|
136
|
-
await sweepFeatureSessions(workdir, feature).catch(() => {});
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
// Load PRD (before try block so it's accessible in finally for onRunEnd)
|
|
141
|
-
let prd = await loadPRD(prdPath);
|
|
142
|
-
|
|
143
|
-
// Initialize interaction chain (US-008) — do this BEFORE precheck so story size prompts can use it
|
|
144
|
-
const interactionChain = await initInteractionChain(config, headless);
|
|
145
|
-
|
|
146
|
-
// ── Prime StatusWriter with PRD so precheck-failed can be recorded ─────────
|
|
147
|
-
statusWriter.setPrd(prd);
|
|
148
|
-
|
|
149
|
-
// ── Run precheck validations (unless --skip-precheck) ──────────────────────
|
|
150
|
-
if (!skipPrecheck) {
|
|
151
|
-
const { runPrecheckValidation } = await import("./precheck-runner");
|
|
152
|
-
await runPrecheckValidation({
|
|
153
|
-
config,
|
|
154
|
-
prd,
|
|
155
|
-
workdir,
|
|
156
|
-
logFilePath,
|
|
157
|
-
statusWriter,
|
|
158
|
-
headless,
|
|
159
|
-
formatterMode,
|
|
160
|
-
interactionChain,
|
|
161
|
-
featureName: feature,
|
|
162
|
-
});
|
|
163
|
-
} else {
|
|
164
|
-
logger?.warn("precheck", "Precheck validations skipped (--skip-precheck)");
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Sweep stale ACP sessions from previous crashed runs (safety net)
|
|
168
|
-
const { sweepStaleFeatureSessions } = await import("../../agents/acp/adapter");
|
|
169
|
-
await sweepStaleFeatureSessions(workdir, feature).catch(() => {});
|
|
170
|
-
|
|
171
|
-
// Acquire lock to prevent concurrent execution
|
|
172
|
-
const lockAcquired = await acquireLock(workdir);
|
|
173
|
-
if (!lockAcquired) {
|
|
174
|
-
logger?.error("execution", "Another nax process is already running in this directory");
|
|
175
|
-
logger?.error("execution", "If you believe this is an error, remove nax.lock manually");
|
|
176
|
-
throw new LockAcquisitionError(workdir);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Everything after lock acquisition is wrapped in try-catch to ensure
|
|
180
|
-
// the lock is released if any setup step fails (FIX-H16)
|
|
181
|
-
try {
|
|
182
|
-
// Load plugins (before try block so it's accessible in finally)
|
|
183
|
-
const globalPluginsDir = path.join(os.homedir(), ".nax", "plugins");
|
|
184
|
-
const projectPluginsDir = path.join(workdir, "nax", "plugins");
|
|
185
|
-
const configPlugins = config.plugins || [];
|
|
186
|
-
const pluginRegistry = await loadPlugins(
|
|
187
|
-
globalPluginsDir,
|
|
188
|
-
projectPluginsDir,
|
|
189
|
-
configPlugins,
|
|
190
|
-
workdir,
|
|
191
|
-
config.disabledPlugins,
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
// Log plugins loaded
|
|
195
|
-
logger?.info("plugins", `Loaded ${pluginRegistry.plugins.length} plugins`, {
|
|
196
|
-
plugins: pluginRegistry.plugins.map((p) => ({ name: p.name, version: p.version, provides: p.provides })),
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
// Log run start
|
|
200
|
-
const routingMode = config.routing.llm?.mode ?? "hybrid";
|
|
201
|
-
logger?.info("run.start", `Starting feature: ${feature} [nax ${NAX_BUILD_INFO}]`, {
|
|
202
|
-
runId,
|
|
203
|
-
feature,
|
|
204
|
-
workdir,
|
|
205
|
-
dryRun,
|
|
206
|
-
routingMode,
|
|
207
|
-
naxVersion: NAX_VERSION,
|
|
208
|
-
naxCommit: NAX_COMMIT,
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
// Fire on-start hook
|
|
212
|
-
await fireHook(hooks, "on-start", hookCtx(feature), workdir);
|
|
213
|
-
|
|
214
|
-
// Initialize run: check agent, reconcile state, validate limits
|
|
215
|
-
const { initializeRun } = await import("./run-initialization");
|
|
216
|
-
const initResult = await initializeRun({
|
|
217
|
-
config,
|
|
218
|
-
prdPath,
|
|
219
|
-
workdir,
|
|
220
|
-
dryRun,
|
|
221
|
-
agentGetFn: options.agentGetFn,
|
|
222
|
-
});
|
|
223
|
-
prd = initResult.prd;
|
|
224
|
-
const counts = initResult.storyCounts;
|
|
225
|
-
|
|
226
|
-
return {
|
|
227
|
-
statusWriter,
|
|
228
|
-
pidRegistry,
|
|
229
|
-
cleanupCrashHandlers,
|
|
230
|
-
pluginRegistry,
|
|
231
|
-
prd,
|
|
232
|
-
storyCounts: counts,
|
|
233
|
-
interactionChain,
|
|
234
|
-
};
|
|
235
|
-
} catch (error) {
|
|
236
|
-
// Release lock before re-throwing so the directory isn't permanently locked
|
|
237
|
-
await releaseLock(workdir);
|
|
238
|
-
throw error;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Story Size Prompts (v0.16.0)
|
|
3
|
-
*
|
|
4
|
-
* Post-precheck interaction prompts for flagged stories.
|
|
5
|
-
* Asks user to confirm (Approve/Skip/Abort) for each large story.
|
|
6
|
-
* Integrates with interaction chain.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { InteractionChain } from "../../interaction/chain";
|
|
10
|
-
import type { InteractionResponse } from "../../interaction/types";
|
|
11
|
-
import { getSafeLogger } from "../../logger";
|
|
12
|
-
import type { PRD } from "../../prd/types";
|
|
13
|
-
import type { FlaggedStory } from "../../precheck/story-size-gate";
|
|
14
|
-
|
|
15
|
-
/** Prompt result for a single story */
|
|
16
|
-
export interface StoryPromptResult {
|
|
17
|
-
storyId: string;
|
|
18
|
-
action: "approve" | "skip" | "abort";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Summary of story size prompt results */
|
|
22
|
-
export interface StorySizePromptSummary {
|
|
23
|
-
approved: string[];
|
|
24
|
-
skipped: string[];
|
|
25
|
-
aborted: boolean;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Prompt user for each flagged story
|
|
30
|
-
*
|
|
31
|
-
* @param flaggedStories - Stories that exceeded size thresholds
|
|
32
|
-
* @param prd - PRD instance (mutated: skips stories if user selects Skip)
|
|
33
|
-
* @param chain - Interaction chain
|
|
34
|
-
* @param featureName - Feature name for context
|
|
35
|
-
* @returns Summary of user decisions
|
|
36
|
-
*/
|
|
37
|
-
export async function promptForFlaggedStories(
|
|
38
|
-
flaggedStories: FlaggedStory[],
|
|
39
|
-
prd: PRD,
|
|
40
|
-
chain: InteractionChain,
|
|
41
|
-
featureName: string,
|
|
42
|
-
): Promise<StorySizePromptSummary> {
|
|
43
|
-
const logger = getSafeLogger();
|
|
44
|
-
const summary: StorySizePromptSummary = {
|
|
45
|
-
approved: [],
|
|
46
|
-
skipped: [],
|
|
47
|
-
aborted: false,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
for (const flagged of flaggedStories) {
|
|
51
|
-
logger?.info("precheck", `Story size gate: prompting for ${flagged.storyId}`, {
|
|
52
|
-
recommendation: flagged.recommendation,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const story = prd.userStories.find((s) => s.id === flagged.storyId);
|
|
56
|
-
if (!story) {
|
|
57
|
-
logger?.warn("precheck", `Story ${flagged.storyId} not found in PRD, skipping prompt`);
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Build detail message with signal breakdown
|
|
62
|
-
const signalDetails = [
|
|
63
|
-
`- Acceptance Criteria: ${flagged.signals.acCount.value} (threshold: ${flagged.signals.acCount.threshold}) ${flagged.signals.acCount.flagged ? "⚠" : "✓"}`,
|
|
64
|
-
`- Description Length: ${flagged.signals.descriptionLength.value} chars (threshold: ${flagged.signals.descriptionLength.threshold}) ${flagged.signals.descriptionLength.flagged ? "⚠" : "✓"}`,
|
|
65
|
-
`- Bullet Points: ${flagged.signals.bulletPoints.value} (threshold: ${flagged.signals.bulletPoints.threshold}) ${flagged.signals.bulletPoints.flagged ? "⚠" : "✓"}`,
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
const detail = `${flagged.recommendation}\n\nSignal breakdown:\n${signalDetails.join("\n")}\n\nStory: ${story.title}`;
|
|
69
|
-
|
|
70
|
-
// Send interaction request
|
|
71
|
-
const requestId = `ix-${flagged.storyId}-size-gate`;
|
|
72
|
-
const response: InteractionResponse = await chain.prompt({
|
|
73
|
-
id: requestId,
|
|
74
|
-
type: "choose",
|
|
75
|
-
featureName,
|
|
76
|
-
storyId: flagged.storyId,
|
|
77
|
-
stage: "pre-flight",
|
|
78
|
-
summary: `Story ${flagged.storyId} exceeds size thresholds — proceed?`,
|
|
79
|
-
detail,
|
|
80
|
-
options: [
|
|
81
|
-
{ key: "approve", label: "Approve", description: "Continue with this story despite size warnings" },
|
|
82
|
-
{ key: "skip", label: "Skip", description: "Skip this story and continue with others" },
|
|
83
|
-
{ key: "abort", label: "Abort", description: "Abort the entire run" },
|
|
84
|
-
],
|
|
85
|
-
timeout: 600000, // 10 minutes
|
|
86
|
-
fallback: "escalate",
|
|
87
|
-
createdAt: Date.now(),
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// Process response
|
|
91
|
-
switch (response.action) {
|
|
92
|
-
case "approve": {
|
|
93
|
-
summary.approved.push(flagged.storyId);
|
|
94
|
-
logger?.info("precheck", `User approved ${flagged.storyId} despite size warnings`);
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
case "skip": {
|
|
98
|
-
summary.skipped.push(flagged.storyId);
|
|
99
|
-
story.status = "skipped";
|
|
100
|
-
logger?.info("precheck", `User skipped ${flagged.storyId} due to size warnings`);
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
103
|
-
case "abort": {
|
|
104
|
-
summary.aborted = true;
|
|
105
|
-
logger?.warn("precheck", `User aborted run due to ${flagged.storyId} size warnings`);
|
|
106
|
-
throw new Error(`Run aborted by user: story ${flagged.storyId} exceeds size thresholds`);
|
|
107
|
-
}
|
|
108
|
-
default: {
|
|
109
|
-
logger?.warn("precheck", `Unknown action ${response.action} for ${flagged.storyId}, aborting`);
|
|
110
|
-
summary.aborted = true;
|
|
111
|
-
throw new Error(`Run aborted: unknown action ${response.action}`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
logger?.info("precheck", "Story size gate prompts complete", {
|
|
117
|
-
approved: summary.approved.length,
|
|
118
|
-
skipped: summary.skipped.length,
|
|
119
|
-
aborted: summary.aborted,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
return summary;
|
|
123
|
-
}
|