@nathapp/nax 0.50.3 → 0.51.2
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/README.md +177 -104
- package/dist/nax.js +417 -213
- 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 -415
- 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 -138
- 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 -219
- 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 -218
- 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 -522
- package/src/config/schema-types.ts +0 -53
- package/src/config/schema.ts +0 -60
- package/src/config/schemas.ts +0 -426
- 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 -309
- 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 -144
- 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,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Acceptance Setup Stage
|
|
3
|
-
*
|
|
4
|
-
* Pre-run pipeline stage that generates acceptance tests from PRD criteria
|
|
5
|
-
* and validates them with a RED gate before story execution begins.
|
|
6
|
-
*
|
|
7
|
-
* RED gate behavior:
|
|
8
|
-
* - exit != 0 (tests fail) → valid RED, continue
|
|
9
|
-
* - exit == 0 (all tests pass) → tests are not testing new behavior, warn and skip
|
|
10
|
-
*
|
|
11
|
-
* Stores results in ctx.acceptanceSetup = { totalCriteria, testableCount, redFailCount }.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import path from "node:path";
|
|
15
|
-
import type { RefinedCriterion } from "../../acceptance/types";
|
|
16
|
-
import { resolveModel } from "../../config";
|
|
17
|
-
import type { UserStory } from "../../prd/types";
|
|
18
|
-
import type { PipelineContext, PipelineStage, StageResult } from "../types";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Injectable dependencies for acceptance-setup stage.
|
|
22
|
-
* Allows tests to mock bun test execution, file I/O, and LLM calls.
|
|
23
|
-
* @internal
|
|
24
|
-
*/
|
|
25
|
-
export const _acceptanceSetupDeps = {
|
|
26
|
-
fileExists: async (_path: string): Promise<boolean> => {
|
|
27
|
-
const f = Bun.file(_path);
|
|
28
|
-
return f.exists();
|
|
29
|
-
},
|
|
30
|
-
writeFile: async (filePath: string, content: string): Promise<void> => {
|
|
31
|
-
await Bun.write(filePath, content);
|
|
32
|
-
},
|
|
33
|
-
runTest: async (_testPath: string, _workdir: string): Promise<{ exitCode: number; output: string }> => {
|
|
34
|
-
const proc = Bun.spawn(["bun", "test", _testPath], {
|
|
35
|
-
cwd: _workdir,
|
|
36
|
-
stdout: "pipe",
|
|
37
|
-
stderr: "pipe",
|
|
38
|
-
});
|
|
39
|
-
const [exitCode, stdout, stderr] = await Promise.all([
|
|
40
|
-
proc.exited,
|
|
41
|
-
new Response(proc.stdout).text(),
|
|
42
|
-
new Response(proc.stderr).text(),
|
|
43
|
-
]);
|
|
44
|
-
return { exitCode, output: `${stdout}\n${stderr}` };
|
|
45
|
-
},
|
|
46
|
-
refine: async (
|
|
47
|
-
_criteria: string[],
|
|
48
|
-
_context: import("../../acceptance/types").RefinementContext,
|
|
49
|
-
): Promise<RefinedCriterion[]> => {
|
|
50
|
-
const { refineAcceptanceCriteria } = await import("../../acceptance/refinement");
|
|
51
|
-
return refineAcceptanceCriteria(_criteria, _context);
|
|
52
|
-
},
|
|
53
|
-
generate: async (
|
|
54
|
-
_stories: UserStory[],
|
|
55
|
-
_refined: RefinedCriterion[],
|
|
56
|
-
_options: import("../../acceptance/types").GenerateFromPRDOptions,
|
|
57
|
-
): Promise<import("../../acceptance/types").AcceptanceTestResult> => {
|
|
58
|
-
const { generateFromPRD } = await import("../../acceptance/generator");
|
|
59
|
-
return generateFromPRD(_stories, _refined, _options);
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export const acceptanceSetupStage: PipelineStage = {
|
|
64
|
-
name: "acceptance-setup",
|
|
65
|
-
|
|
66
|
-
enabled(ctx: PipelineContext): boolean {
|
|
67
|
-
return ctx.config.acceptance.enabled && !!ctx.featureDir;
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
async execute(ctx: PipelineContext): Promise<StageResult> {
|
|
71
|
-
if (!ctx.featureDir) {
|
|
72
|
-
return { action: "fail", reason: "[acceptance-setup] featureDir is not set" };
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const testPath = path.join(ctx.featureDir, "acceptance.test.ts");
|
|
76
|
-
const fileExists = await _acceptanceSetupDeps.fileExists(testPath);
|
|
77
|
-
|
|
78
|
-
let totalCriteria = 0;
|
|
79
|
-
let testableCount = 0;
|
|
80
|
-
|
|
81
|
-
if (!fileExists) {
|
|
82
|
-
const allCriteria: string[] = ctx.prd.userStories.flatMap((s) => s.acceptanceCriteria);
|
|
83
|
-
totalCriteria = allCriteria.length;
|
|
84
|
-
|
|
85
|
-
const { getAgent } = await import("../../agents");
|
|
86
|
-
const agent = (ctx.agentGetFn ?? getAgent)(ctx.config.autoMode.defaultAgent);
|
|
87
|
-
|
|
88
|
-
let refinedCriteria: RefinedCriterion[];
|
|
89
|
-
|
|
90
|
-
if (ctx.config.acceptance.refinement) {
|
|
91
|
-
refinedCriteria = await _acceptanceSetupDeps.refine(allCriteria, {
|
|
92
|
-
storyId: ctx.prd.userStories[0]?.id ?? "US-001",
|
|
93
|
-
codebaseContext: "",
|
|
94
|
-
config: ctx.config,
|
|
95
|
-
testStrategy: ctx.config.acceptance.testStrategy,
|
|
96
|
-
testFramework: ctx.config.acceptance.testFramework,
|
|
97
|
-
});
|
|
98
|
-
} else {
|
|
99
|
-
refinedCriteria = allCriteria.map((c) => ({
|
|
100
|
-
original: c,
|
|
101
|
-
refined: c,
|
|
102
|
-
testable: true,
|
|
103
|
-
storyId: ctx.prd.userStories[0]?.id ?? "US-001",
|
|
104
|
-
}));
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
testableCount = refinedCriteria.filter((r) => r.testable).length;
|
|
108
|
-
|
|
109
|
-
const result = await _acceptanceSetupDeps.generate(ctx.prd.userStories, refinedCriteria, {
|
|
110
|
-
featureName: ctx.prd.feature,
|
|
111
|
-
workdir: ctx.workdir,
|
|
112
|
-
featureDir: ctx.featureDir,
|
|
113
|
-
codebaseContext: "",
|
|
114
|
-
modelTier: ctx.config.acceptance.model ?? "fast",
|
|
115
|
-
modelDef: resolveModel(ctx.config.models[ctx.config.acceptance.model ?? "fast"]),
|
|
116
|
-
config: ctx.config,
|
|
117
|
-
testStrategy: ctx.config.acceptance.testStrategy,
|
|
118
|
-
testFramework: ctx.config.acceptance.testFramework,
|
|
119
|
-
adapter: agent ?? undefined,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
await _acceptanceSetupDeps.writeFile(testPath, result.testCode);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (ctx.config.acceptance.redGate === false) {
|
|
126
|
-
ctx.acceptanceSetup = { totalCriteria, testableCount, redFailCount: 0 };
|
|
127
|
-
return { action: "continue" };
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const { exitCode } = await _acceptanceSetupDeps.runTest(testPath, ctx.workdir);
|
|
131
|
-
|
|
132
|
-
if (exitCode === 0) {
|
|
133
|
-
ctx.acceptanceSetup = { totalCriteria, testableCount, redFailCount: 0 };
|
|
134
|
-
return {
|
|
135
|
-
action: "skip",
|
|
136
|
-
reason:
|
|
137
|
-
"[acceptance-setup] Acceptance tests already pass — they are not testing new behavior. Skipping acceptance gate.",
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
ctx.acceptanceSetup = { totalCriteria, testableCount, redFailCount: 1 };
|
|
142
|
-
return { action: "continue" };
|
|
143
|
-
},
|
|
144
|
-
};
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Acceptance Stage
|
|
3
|
-
*
|
|
4
|
-
* Runs acceptance tests when all stories are complete.
|
|
5
|
-
* Validates the feature against acceptance criteria from spec.md.
|
|
6
|
-
*
|
|
7
|
-
* Only executes when:
|
|
8
|
-
* - All stories in the PRD are complete (status: passed/failed/skipped, not pending/in-progress)
|
|
9
|
-
* - Acceptance validation is enabled in config
|
|
10
|
-
*
|
|
11
|
-
* @returns
|
|
12
|
-
* - `continue`: All acceptance tests pass
|
|
13
|
-
* - `fail`: One or more acceptance tests failed
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```ts
|
|
17
|
-
* // All stories complete, acceptance tests pass
|
|
18
|
-
* await acceptanceStage.execute(ctx);
|
|
19
|
-
* // Returns: { action: "continue" }
|
|
20
|
-
*
|
|
21
|
-
* // All stories complete, acceptance tests fail
|
|
22
|
-
* await acceptanceStage.execute(ctx);
|
|
23
|
-
* // Returns: { action: "fail", reason: "Acceptance tests failed: AC-2, AC-5" }
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import path from "node:path";
|
|
28
|
-
import { getLogger } from "../../logger";
|
|
29
|
-
import { countStories } from "../../prd";
|
|
30
|
-
import { logTestOutput } from "../../utils/log-test-output";
|
|
31
|
-
import type { PipelineContext, PipelineStage, StageResult } from "../types";
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Parse bun test output to extract failed test names.
|
|
35
|
-
*
|
|
36
|
-
* Looks for lines containing "AC-N:" to identify which acceptance criteria failed.
|
|
37
|
-
*
|
|
38
|
-
* @param output - stdout/stderr from bun test
|
|
39
|
-
* @returns Array of failed AC IDs (e.g., ["AC-2", "AC-5"])
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```ts
|
|
43
|
-
* const output = `
|
|
44
|
-
* ✓ AC-1: TTL expiry
|
|
45
|
-
* ✗ AC-2: handles empty input
|
|
46
|
-
* ✓ AC-3: validates format
|
|
47
|
-
* `;
|
|
48
|
-
* const failed = parseTestFailures(output);
|
|
49
|
-
* // Returns: ["AC-2"]
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
function parseTestFailures(output: string): string[] {
|
|
53
|
-
const failedACs: string[] = [];
|
|
54
|
-
const lines = output.split("\n");
|
|
55
|
-
|
|
56
|
-
for (const line of lines) {
|
|
57
|
-
// Look for Bun's (fail) marker followed by AC-N pattern
|
|
58
|
-
// Pattern: (fail) ... > AC-N: description
|
|
59
|
-
if (line.includes("(fail)")) {
|
|
60
|
-
const acMatch = line.match(/(AC-\d+):/i);
|
|
61
|
-
if (acMatch) {
|
|
62
|
-
const acId = acMatch[1].toUpperCase();
|
|
63
|
-
if (!failedACs.includes(acId)) {
|
|
64
|
-
failedACs.push(acId);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return failedACs;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Check if all stories in the PRD are complete.
|
|
75
|
-
*
|
|
76
|
-
* Stories are complete if their status is passed, failed, or skipped.
|
|
77
|
-
* Pending or in-progress stories are not complete.
|
|
78
|
-
*
|
|
79
|
-
* @param ctx - Pipeline context
|
|
80
|
-
* @returns true if all stories complete, false otherwise
|
|
81
|
-
*/
|
|
82
|
-
function areAllStoriesComplete(ctx: PipelineContext): boolean {
|
|
83
|
-
const counts = countStories(ctx.prd);
|
|
84
|
-
const totalComplete = counts.passed + counts.failed + counts.skipped;
|
|
85
|
-
return totalComplete === counts.total;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export const acceptanceStage: PipelineStage = {
|
|
89
|
-
name: "acceptance",
|
|
90
|
-
|
|
91
|
-
enabled(ctx: PipelineContext): boolean {
|
|
92
|
-
// Only run when:
|
|
93
|
-
// 1. Acceptance validation is enabled
|
|
94
|
-
// 2. All stories are complete
|
|
95
|
-
const effectiveConfig = ctx.effectiveConfig ?? ctx.config;
|
|
96
|
-
if (!effectiveConfig.acceptance.enabled) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (!areAllStoriesComplete(ctx)) {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return true;
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
async execute(ctx: PipelineContext): Promise<StageResult> {
|
|
108
|
-
const logger = getLogger();
|
|
109
|
-
|
|
110
|
-
// PKG-004: use centrally resolved effective config
|
|
111
|
-
const effectiveConfig = ctx.effectiveConfig ?? ctx.config;
|
|
112
|
-
|
|
113
|
-
logger.info("acceptance", "Running acceptance tests");
|
|
114
|
-
|
|
115
|
-
// Build path to acceptance test file
|
|
116
|
-
if (!ctx.featureDir) {
|
|
117
|
-
logger.warn("acceptance", "No feature directory — skipping acceptance tests");
|
|
118
|
-
return { action: "continue" };
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const testPath = path.join(ctx.featureDir, effectiveConfig.acceptance.testPath);
|
|
122
|
-
|
|
123
|
-
// Check if test file exists
|
|
124
|
-
const testFile = Bun.file(testPath);
|
|
125
|
-
const exists = await testFile.exists();
|
|
126
|
-
|
|
127
|
-
if (!exists) {
|
|
128
|
-
logger.warn("acceptance", "Acceptance test file not found — skipping", {
|
|
129
|
-
testPath,
|
|
130
|
-
});
|
|
131
|
-
return { action: "continue" };
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Run bun test on the acceptance test file
|
|
135
|
-
const proc = Bun.spawn(["bun", "test", testPath], {
|
|
136
|
-
cwd: ctx.workdir,
|
|
137
|
-
stdout: "pipe",
|
|
138
|
-
stderr: "pipe",
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
const [exitCode, stdout, stderr] = await Promise.all([
|
|
142
|
-
proc.exited,
|
|
143
|
-
new Response(proc.stdout).text(),
|
|
144
|
-
new Response(proc.stderr).text(),
|
|
145
|
-
]);
|
|
146
|
-
|
|
147
|
-
// Combine stdout and stderr for parsing
|
|
148
|
-
const output = `${stdout}\n${stderr}`;
|
|
149
|
-
|
|
150
|
-
// Parse test results
|
|
151
|
-
const failedACs = parseTestFailures(output);
|
|
152
|
-
|
|
153
|
-
// Check for overridden ACs (skip those)
|
|
154
|
-
const overrides = ctx.prd.acceptanceOverrides || {};
|
|
155
|
-
const actualFailures = failedACs.filter((acId) => !overrides[acId]);
|
|
156
|
-
|
|
157
|
-
// If all tests passed cleanly
|
|
158
|
-
if (actualFailures.length === 0 && exitCode === 0) {
|
|
159
|
-
logger.info("acceptance", "All acceptance tests passed");
|
|
160
|
-
return { action: "continue" };
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// All parsed AC failures are overridden — treat as success even with non-zero exit
|
|
164
|
-
if (failedACs.length > 0 && actualFailures.length === 0) {
|
|
165
|
-
logger.info("acceptance", "All failed ACs are overridden — treating as pass");
|
|
166
|
-
return { action: "continue" };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Non-zero exit but no AC failures parsed at all — test crashed (syntax error, import failure, etc.)
|
|
170
|
-
if (failedACs.length === 0 && exitCode !== 0) {
|
|
171
|
-
logger.error("acceptance", "Tests errored with no AC failures parsed", { exitCode });
|
|
172
|
-
logTestOutput(logger, "acceptance", output);
|
|
173
|
-
|
|
174
|
-
ctx.acceptanceFailures = {
|
|
175
|
-
failedACs: ["AC-ERROR"],
|
|
176
|
-
testOutput: output,
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
return {
|
|
180
|
-
action: "fail",
|
|
181
|
-
reason: `Acceptance tests errored (exit code ${exitCode}): syntax error, import failure, or unhandled exception`,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// If we have actual failures, report them
|
|
186
|
-
if (actualFailures.length > 0) {
|
|
187
|
-
// Log overridden failures (if any)
|
|
188
|
-
const overriddenFailures = failedACs.filter((acId) => overrides[acId]);
|
|
189
|
-
if (overriddenFailures.length > 0) {
|
|
190
|
-
logger.warn("acceptance", "Skipped failures (overridden)", {
|
|
191
|
-
overriddenFailures,
|
|
192
|
-
overrides: overriddenFailures.map((acId) => ({ acId, reason: overrides[acId] })),
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
logger.error("acceptance", "Acceptance tests failed", { failedACs: actualFailures });
|
|
197
|
-
logTestOutput(logger, "acceptance", output);
|
|
198
|
-
|
|
199
|
-
// Store failed ACs and test output in context for fix generation
|
|
200
|
-
ctx.acceptanceFailures = {
|
|
201
|
-
failedACs: actualFailures,
|
|
202
|
-
testOutput: output,
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
return {
|
|
206
|
-
action: "fail",
|
|
207
|
-
reason: `Acceptance tests failed: ${actualFailures.join(", ")}`,
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// All tests passed
|
|
212
|
-
logger.info("acceptance", "All acceptance tests passed");
|
|
213
|
-
return { action: "continue" };
|
|
214
|
-
},
|
|
215
|
-
};
|
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
// RE-ARCH: keep
|
|
2
|
-
/**
|
|
3
|
-
* Autofix Stage (ADR-005, Phase 2)
|
|
4
|
-
*
|
|
5
|
-
* Runs after a failed review stage. Attempts to fix quality issues
|
|
6
|
-
* automatically before escalating:
|
|
7
|
-
*
|
|
8
|
-
* Phase 1 — Mechanical fix: runs lintFix / formatFix commands (if configured)
|
|
9
|
-
* Phase 2 — Agent rectification: spawns an agent session with the review error
|
|
10
|
-
* output as context (reuses the pattern from rectification-loop.ts)
|
|
11
|
-
*
|
|
12
|
-
* Language-agnostic: uses quality.commands.lintFix / formatFix from config.
|
|
13
|
-
* No hardcoded tool names.
|
|
14
|
-
*
|
|
15
|
-
* Enabled only when ctx.reviewResult?.passed === false AND autofix is enabled.
|
|
16
|
-
*
|
|
17
|
-
* Returns:
|
|
18
|
-
* - `retry` fromStage:"review" — autofix resolved the failures
|
|
19
|
-
* - `escalate` — max attempts exhausted or agent unavailable
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { join } from "node:path";
|
|
23
|
-
import { getAgent } from "../../agents";
|
|
24
|
-
import { resolveModel } from "../../config";
|
|
25
|
-
import { loadConfigForWorkdir } from "../../config/loader";
|
|
26
|
-
import { resolvePermissions } from "../../config/permissions";
|
|
27
|
-
import { getLogger } from "../../logger";
|
|
28
|
-
import type { UserStory } from "../../prd";
|
|
29
|
-
import type { ReviewCheckResult } from "../../review/types";
|
|
30
|
-
import { pipelineEventBus } from "../event-bus";
|
|
31
|
-
import type { PipelineContext, PipelineStage, StageResult } from "../types";
|
|
32
|
-
|
|
33
|
-
export const autofixStage: PipelineStage = {
|
|
34
|
-
name: "autofix",
|
|
35
|
-
|
|
36
|
-
enabled(ctx: PipelineContext): boolean {
|
|
37
|
-
if (!ctx.reviewResult) return false;
|
|
38
|
-
if (ctx.reviewResult.success) return false;
|
|
39
|
-
const autofixEnabled = (ctx.effectiveConfig ?? ctx.config).quality.autofix?.enabled ?? true;
|
|
40
|
-
return autofixEnabled;
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
skipReason(ctx: PipelineContext): string {
|
|
44
|
-
if (!ctx.reviewResult || ctx.reviewResult.success) return "not needed (review passed)";
|
|
45
|
-
return "disabled (autofix not enabled in config)";
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
async execute(ctx: PipelineContext): Promise<StageResult> {
|
|
49
|
-
const logger = getLogger();
|
|
50
|
-
const { reviewResult } = ctx;
|
|
51
|
-
|
|
52
|
-
if (!reviewResult || reviewResult.success) {
|
|
53
|
-
return { action: "continue" };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// PKG-004: use centrally resolved effective config (ctx.effectiveConfig set once per story)
|
|
57
|
-
const effectiveConfig = ctx.effectiveConfig ?? ctx.config;
|
|
58
|
-
const lintFixCmd = effectiveConfig.quality.commands.lintFix;
|
|
59
|
-
const formatFixCmd = effectiveConfig.quality.commands.formatFix;
|
|
60
|
-
|
|
61
|
-
// Effective workdir for running commands (scoped to package if monorepo)
|
|
62
|
-
const effectiveWorkdir = ctx.story.workdir ? join(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
63
|
-
|
|
64
|
-
// Identify which checks failed
|
|
65
|
-
const failedCheckNames = new Set((reviewResult.checks ?? []).filter((c) => !c.success).map((c) => c.check));
|
|
66
|
-
const hasLintFailure = failedCheckNames.has("lint");
|
|
67
|
-
|
|
68
|
-
logger.info("autofix", "Starting autofix", {
|
|
69
|
-
storyId: ctx.story.id,
|
|
70
|
-
failedChecks: [...failedCheckNames],
|
|
71
|
-
workdir: effectiveWorkdir,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// Phase 1: Mechanical fix — only for lint failures (lintFix/formatFix cannot fix typecheck errors)
|
|
75
|
-
if (hasLintFailure && (lintFixCmd || formatFixCmd)) {
|
|
76
|
-
if (lintFixCmd) {
|
|
77
|
-
pipelineEventBus.emit({ type: "autofix:started", storyId: ctx.story.id, command: lintFixCmd });
|
|
78
|
-
const lintResult = await _autofixDeps.runCommand(lintFixCmd, effectiveWorkdir);
|
|
79
|
-
logger.debug("autofix", `lintFix exit=${lintResult.exitCode}`, { storyId: ctx.story.id, command: lintFixCmd });
|
|
80
|
-
if (lintResult.exitCode !== 0) {
|
|
81
|
-
logger.warn("autofix", "lintFix command failed — may not have fixed all issues", {
|
|
82
|
-
storyId: ctx.story.id,
|
|
83
|
-
exitCode: lintResult.exitCode,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (formatFixCmd) {
|
|
89
|
-
pipelineEventBus.emit({ type: "autofix:started", storyId: ctx.story.id, command: formatFixCmd });
|
|
90
|
-
const fmtResult = await _autofixDeps.runCommand(formatFixCmd, effectiveWorkdir);
|
|
91
|
-
logger.debug("autofix", `formatFix exit=${fmtResult.exitCode}`, {
|
|
92
|
-
storyId: ctx.story.id,
|
|
93
|
-
command: formatFixCmd,
|
|
94
|
-
});
|
|
95
|
-
if (fmtResult.exitCode !== 0) {
|
|
96
|
-
logger.warn("autofix", "formatFix command failed — may not have fixed all issues", {
|
|
97
|
-
storyId: ctx.story.id,
|
|
98
|
-
exitCode: fmtResult.exitCode,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const recheckPassed = await _autofixDeps.recheckReview(ctx);
|
|
104
|
-
pipelineEventBus.emit({ type: "autofix:completed", storyId: ctx.story.id, fixed: recheckPassed });
|
|
105
|
-
|
|
106
|
-
if (recheckPassed) {
|
|
107
|
-
logger.info("autofix", "Mechanical autofix succeeded — retrying review", { storyId: ctx.story.id });
|
|
108
|
-
return { action: "retry", fromStage: "review" };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
logger.info("autofix", "Mechanical autofix did not resolve all failures — proceeding to agent rectification", {
|
|
112
|
-
storyId: ctx.story.id,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Phase 2: Agent rectification — spawn agent with review error context
|
|
117
|
-
const agentFixed = await _autofixDeps.runAgentRectification(ctx);
|
|
118
|
-
if (agentFixed) {
|
|
119
|
-
if (ctx.reviewResult) ctx.reviewResult = { ...ctx.reviewResult, success: true };
|
|
120
|
-
logger.info("autofix", "Agent rectification succeeded — retrying review", { storyId: ctx.story.id });
|
|
121
|
-
return { action: "retry", fromStage: "review" };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
logger.warn("autofix", "Autofix exhausted — escalating", { storyId: ctx.story.id });
|
|
125
|
-
return { action: "escalate", reason: "Autofix exhausted: review still failing after fix attempts" };
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
// ---------------------------------------------------------------------------
|
|
130
|
-
// Helpers
|
|
131
|
-
// ---------------------------------------------------------------------------
|
|
132
|
-
|
|
133
|
-
interface CommandResult {
|
|
134
|
-
exitCode: number;
|
|
135
|
-
output: string;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
async function runCommand(cmd: string, cwd: string): Promise<CommandResult> {
|
|
139
|
-
const parts = cmd.split(/\s+/);
|
|
140
|
-
const proc = Bun.spawn(parts, { cwd, stdout: "pipe", stderr: "pipe" });
|
|
141
|
-
const [exitCode, stdout, stderr] = await Promise.all([
|
|
142
|
-
proc.exited,
|
|
143
|
-
new Response(proc.stdout).text(),
|
|
144
|
-
new Response(proc.stderr).text(),
|
|
145
|
-
]);
|
|
146
|
-
return { exitCode, output: `${stdout}\n${stderr}` };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async function recheckReview(ctx: PipelineContext): Promise<boolean> {
|
|
150
|
-
// Import reviewStage lazily to avoid circular deps
|
|
151
|
-
const { reviewStage } = await import("./review");
|
|
152
|
-
if (!reviewStage.enabled(ctx)) return true;
|
|
153
|
-
// reviewStage.execute updates ctx.reviewResult in place.
|
|
154
|
-
// We cannot use result.action here because review returns "continue" for BOTH
|
|
155
|
-
// pass and built-in-check-failure (to hand off to autofix). Check success directly.
|
|
156
|
-
await reviewStage.execute(ctx);
|
|
157
|
-
return ctx.reviewResult?.success === true;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function collectFailedChecks(ctx: PipelineContext): ReviewCheckResult[] {
|
|
161
|
-
return (ctx.reviewResult?.checks ?? []).filter((c) => !c.success);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export function buildReviewRectificationPrompt(failedChecks: ReviewCheckResult[], story: UserStory): string {
|
|
165
|
-
const errors = failedChecks
|
|
166
|
-
.map((c) => `## ${c.check} errors (exit code ${c.exitCode})\n\`\`\`\n${c.output}\n\`\`\``)
|
|
167
|
-
.join("\n\n");
|
|
168
|
-
|
|
169
|
-
// ENH-008: Scope constraint for monorepo stories — prevent out-of-package changes
|
|
170
|
-
const scopeConstraint = story.workdir
|
|
171
|
-
? `\n\nIMPORTANT: Only modify files within \`${story.workdir}/\`. Do NOT touch files outside this directory.`
|
|
172
|
-
: "";
|
|
173
|
-
|
|
174
|
-
return `You are fixing lint/typecheck errors from a code review.
|
|
175
|
-
|
|
176
|
-
Story: ${story.title} (${story.id})
|
|
177
|
-
|
|
178
|
-
The following quality checks failed after implementation:
|
|
179
|
-
|
|
180
|
-
${errors}
|
|
181
|
-
|
|
182
|
-
Fix ALL errors listed above. Do NOT change test files or test behavior.
|
|
183
|
-
Do NOT add new features — only fix the quality check errors.
|
|
184
|
-
Commit your fixes when done.${scopeConstraint}`;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
async function runAgentRectification(ctx: PipelineContext): Promise<boolean> {
|
|
188
|
-
const logger = getLogger();
|
|
189
|
-
const effectiveConfig = ctx.effectiveConfig ?? ctx.config;
|
|
190
|
-
const maxAttempts = effectiveConfig.quality.autofix?.maxAttempts ?? 2;
|
|
191
|
-
const failedChecks = collectFailedChecks(ctx);
|
|
192
|
-
|
|
193
|
-
if (failedChecks.length === 0) {
|
|
194
|
-
logger.debug("autofix", "No failed checks found — skipping agent rectification", { storyId: ctx.story.id });
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
logger.info("autofix", "Starting agent rectification for review failures", {
|
|
199
|
-
storyId: ctx.story.id,
|
|
200
|
-
failedChecks: failedChecks.map((c) => c.check),
|
|
201
|
-
maxAttempts,
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
const agentGetFn = ctx.agentGetFn ?? getAgent;
|
|
205
|
-
|
|
206
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
207
|
-
logger.info("autofix", `Agent rectification attempt ${attempt}/${maxAttempts}`, { storyId: ctx.story.id });
|
|
208
|
-
|
|
209
|
-
const agent = agentGetFn(ctx.config.autoMode.defaultAgent);
|
|
210
|
-
if (!agent) {
|
|
211
|
-
logger.error("autofix", "Agent not found — cannot run agent rectification", { storyId: ctx.story.id });
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const prompt = buildReviewRectificationPrompt(failedChecks, ctx.story);
|
|
216
|
-
const modelTier = ctx.story.routing?.modelTier ?? ctx.config.autoMode.escalation.tierOrder[0]?.tier ?? "balanced";
|
|
217
|
-
const modelDef = resolveModel(ctx.config.models[modelTier]);
|
|
218
|
-
|
|
219
|
-
// ENH-008: Scope agent to story.workdir for monorepo — prevents out-of-package changes
|
|
220
|
-
const rectificationWorkdir = ctx.story.workdir ? join(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
221
|
-
|
|
222
|
-
await agent.run({
|
|
223
|
-
prompt,
|
|
224
|
-
workdir: rectificationWorkdir,
|
|
225
|
-
modelTier,
|
|
226
|
-
modelDef,
|
|
227
|
-
timeoutSeconds: ctx.config.execution.sessionTimeoutSeconds,
|
|
228
|
-
dangerouslySkipPermissions: resolvePermissions(ctx.config, "rectification").skipPermissions,
|
|
229
|
-
pipelineStage: "rectification",
|
|
230
|
-
config: ctx.config,
|
|
231
|
-
maxInteractionTurns: ctx.config.agent?.maxInteractionTurns,
|
|
232
|
-
storyId: ctx.story.id,
|
|
233
|
-
sessionRole: "implementer",
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
const passed = await _autofixDeps.recheckReview(ctx);
|
|
237
|
-
if (passed) {
|
|
238
|
-
logger.info("autofix", `[OK] Agent rectification succeeded on attempt ${attempt}`, {
|
|
239
|
-
storyId: ctx.story.id,
|
|
240
|
-
});
|
|
241
|
-
return true;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Refresh failed checks for next attempt
|
|
245
|
-
const updatedFailed = collectFailedChecks(ctx);
|
|
246
|
-
if (updatedFailed.length > 0) {
|
|
247
|
-
failedChecks.splice(0, failedChecks.length, ...updatedFailed);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
logger.warn("autofix", `Agent rectification still failing after attempt ${attempt}`, {
|
|
251
|
-
storyId: ctx.story.id,
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
logger.warn("autofix", "Agent rectification exhausted", { storyId: ctx.story.id });
|
|
256
|
-
return false;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Injectable deps for testing.
|
|
261
|
-
*/
|
|
262
|
-
export const _autofixDeps = { runCommand, recheckReview, runAgentRectification, loadConfigForWorkdir };
|