@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,110 +0,0 @@
|
|
|
1
|
-
// RE-ARCH: keep
|
|
2
|
-
/**
|
|
3
|
-
* Completion Stage
|
|
4
|
-
*
|
|
5
|
-
* Marks stories as passed, logs progress, emits lifecycle events.
|
|
6
|
-
* This is the final stage in the pipeline for successful executions.
|
|
7
|
-
*
|
|
8
|
-
* Phase 3 (ADR-005): Replaced direct fireHook() calls with event bus emissions.
|
|
9
|
-
* The hooks/reporters subscriber wires those events to actual hook/reporter calls.
|
|
10
|
-
*
|
|
11
|
-
* @returns
|
|
12
|
-
* - `continue`: Stories marked complete, events emitted
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { appendProgress } from "../../execution/progress";
|
|
16
|
-
import { checkReviewGate, isTriggerEnabled } from "../../interaction/triggers";
|
|
17
|
-
import { getLogger } from "../../logger";
|
|
18
|
-
import { collectBatchMetrics, collectStoryMetrics } from "../../metrics";
|
|
19
|
-
import { countStories, markStoryPassed, savePRD } from "../../prd";
|
|
20
|
-
import { pipelineEventBus } from "../event-bus";
|
|
21
|
-
import type { PipelineContext, PipelineStage, StageResult } from "../types";
|
|
22
|
-
|
|
23
|
-
export const completionStage: PipelineStage = {
|
|
24
|
-
name: "completion",
|
|
25
|
-
enabled: () => true,
|
|
26
|
-
|
|
27
|
-
async execute(ctx: PipelineContext): Promise<StageResult> {
|
|
28
|
-
const logger = getLogger();
|
|
29
|
-
const isBatch = ctx.stories.length > 1;
|
|
30
|
-
const sessionCost = ctx.agentResult?.estimatedCost || 0;
|
|
31
|
-
|
|
32
|
-
// Calculate PRD path
|
|
33
|
-
const prdPath = ctx.featureDir ? `${ctx.featureDir}/prd.json` : `${ctx.workdir}/nax/features/unknown/prd.json`;
|
|
34
|
-
|
|
35
|
-
// Collect story metrics
|
|
36
|
-
const storyStartTime = ctx.storyStartTime || new Date().toISOString();
|
|
37
|
-
if (isBatch) {
|
|
38
|
-
ctx.storyMetrics = collectBatchMetrics(ctx, storyStartTime);
|
|
39
|
-
} else {
|
|
40
|
-
ctx.storyMetrics = [collectStoryMetrics(ctx, storyStartTime)];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Mark all stories in batch as passed
|
|
44
|
-
for (const completedStory of ctx.stories) {
|
|
45
|
-
markStoryPassed(ctx.prd, completedStory.id);
|
|
46
|
-
|
|
47
|
-
const costPerStory = sessionCost / ctx.stories.length;
|
|
48
|
-
logger.info("completion", "Story passed", {
|
|
49
|
-
storyId: completedStory.id,
|
|
50
|
-
cost: costPerStory,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// Log progress
|
|
54
|
-
if (ctx.featureDir) {
|
|
55
|
-
await appendProgress(
|
|
56
|
-
ctx.featureDir,
|
|
57
|
-
completedStory.id,
|
|
58
|
-
"passed",
|
|
59
|
-
`${completedStory.title} — Cost: $${costPerStory.toFixed(4)}${isBatch ? " (batched)" : ""}`,
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Emit story:completed event — hooks + reporter subscribers handle the rest
|
|
64
|
-
const storyMetric = ctx.storyMetrics?.find((m) => m.storyId === completedStory.id) ?? ctx.storyMetrics?.[0];
|
|
65
|
-
pipelineEventBus.emit({
|
|
66
|
-
type: "story:completed",
|
|
67
|
-
storyId: completedStory.id,
|
|
68
|
-
story: completedStory,
|
|
69
|
-
passed: true,
|
|
70
|
-
runElapsedMs: storyMetric?.durationMs ?? 0,
|
|
71
|
-
cost: costPerStory,
|
|
72
|
-
modelTier: ctx.routing?.modelTier,
|
|
73
|
-
testStrategy: ctx.routing?.testStrategy,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// review-gate trigger: check if story needs re-review after passing
|
|
77
|
-
if (ctx.interaction && isTriggerEnabled("review-gate", ctx.config)) {
|
|
78
|
-
const shouldContinue = await _completionDeps.checkReviewGate(
|
|
79
|
-
{ featureName: ctx.prd.feature, storyId: completedStory.id },
|
|
80
|
-
ctx.config,
|
|
81
|
-
ctx.interaction,
|
|
82
|
-
);
|
|
83
|
-
if (!shouldContinue) {
|
|
84
|
-
logger.warn("completion", "Story marked for re-review", { storyId: completedStory.id });
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Save PRD
|
|
90
|
-
await savePRD(ctx.prd, prdPath);
|
|
91
|
-
|
|
92
|
-
// Display progress
|
|
93
|
-
const updatedCounts = countStories(ctx.prd);
|
|
94
|
-
logger.info("completion", "Progress update", {
|
|
95
|
-
completed: updatedCounts.passed + updatedCounts.failed,
|
|
96
|
-
total: updatedCounts.total,
|
|
97
|
-
passed: updatedCounts.passed,
|
|
98
|
-
failed: updatedCounts.failed,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
return { action: "continue" };
|
|
102
|
-
},
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Swappable dependencies for testing (avoids mock.module() which leaks in Bun 1.x).
|
|
107
|
-
*/
|
|
108
|
-
export const _completionDeps = {
|
|
109
|
-
checkReviewGate,
|
|
110
|
-
};
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Constitution Stage
|
|
3
|
-
*
|
|
4
|
-
* Loads the project constitution (if enabled) and stores it in context.
|
|
5
|
-
* Constitution defines coding standards, architectural rules, and forbidden patterns.
|
|
6
|
-
*
|
|
7
|
-
* @returns
|
|
8
|
-
* - `continue`: Always continues (soft failure if constitution missing)
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* // Constitution enabled and found
|
|
13
|
-
* await constitutionStage.execute(ctx);
|
|
14
|
-
* // ctx.constitution: { content: "...", tokens: 500, truncated: false }
|
|
15
|
-
*
|
|
16
|
-
* // Constitution enabled but not found
|
|
17
|
-
* await constitutionStage.execute(ctx);
|
|
18
|
-
* // ctx.constitution: undefined (stage logs warning and continues)
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { dirname } from "node:path";
|
|
23
|
-
import { loadConstitution } from "../../constitution";
|
|
24
|
-
import { getLogger } from "../../logger";
|
|
25
|
-
import type { PipelineContext, PipelineStage, StageResult } from "../types";
|
|
26
|
-
|
|
27
|
-
export const constitutionStage: PipelineStage = {
|
|
28
|
-
name: "constitution",
|
|
29
|
-
enabled: (ctx) => ctx.config.constitution.enabled,
|
|
30
|
-
|
|
31
|
-
async execute(ctx: PipelineContext): Promise<StageResult> {
|
|
32
|
-
const logger = getLogger();
|
|
33
|
-
|
|
34
|
-
// Constitution file is in nax/constitution.md
|
|
35
|
-
// featureDir is nax/features/<name>/, so we need to go up two levels
|
|
36
|
-
const ngentDir = ctx.featureDir ? dirname(dirname(ctx.featureDir)) : `${ctx.workdir}/nax`;
|
|
37
|
-
|
|
38
|
-
const result = await loadConstitution(ngentDir, ctx.config.constitution);
|
|
39
|
-
|
|
40
|
-
if (result) {
|
|
41
|
-
ctx.constitution = result;
|
|
42
|
-
|
|
43
|
-
logger.debug("constitution", "Constitution loaded", {
|
|
44
|
-
tokens: result.tokens,
|
|
45
|
-
truncated: result.truncated,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
if (result.truncated) {
|
|
49
|
-
logger.warn("constitution", "Constitution truncated", {
|
|
50
|
-
originalTokens: result.originalTokens,
|
|
51
|
-
tokens: result.tokens,
|
|
52
|
-
maxTokens: ctx.config.constitution.maxTokens,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
// SOFT FAILURE: Constitution missing or failed to load — continue without it
|
|
57
|
-
// This is acceptable because constitution is optional project governance
|
|
58
|
-
logger.debug("constitution", "Constitution not found or failed to load");
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return { action: "continue" };
|
|
62
|
-
},
|
|
63
|
-
};
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Context Stage
|
|
3
|
-
*
|
|
4
|
-
* Builds contextual information for the agent from the PRD and related stories.
|
|
5
|
-
* After building core context, calls plugin context providers to inject external data.
|
|
6
|
-
* Formats as markdown for inclusion in the prompt.
|
|
7
|
-
*
|
|
8
|
-
* @returns
|
|
9
|
-
* - `continue`: Always continues (soft failure if context empty)
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* // PRD has related stories with context
|
|
14
|
-
* await contextStage.execute(ctx);
|
|
15
|
-
* // ctx.contextMarkdown: "## Related Stories\n- US-001: ..."
|
|
16
|
-
*
|
|
17
|
-
* // No related context found
|
|
18
|
-
* await contextStage.execute(ctx);
|
|
19
|
-
* // ctx.contextMarkdown: "" (empty but continues)
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import { join } from "node:path";
|
|
24
|
-
import type { ContextElement } from "../../context/types";
|
|
25
|
-
import { buildStoryContextFull } from "../../execution/helpers";
|
|
26
|
-
import { getLogger } from "../../logger";
|
|
27
|
-
import { errorMessage } from "../../utils/errors";
|
|
28
|
-
import type { PipelineContext, PipelineStage, StageResult } from "../types";
|
|
29
|
-
|
|
30
|
-
export const contextStage: PipelineStage = {
|
|
31
|
-
name: "context",
|
|
32
|
-
enabled: () => true,
|
|
33
|
-
|
|
34
|
-
async execute(ctx: PipelineContext): Promise<StageResult> {
|
|
35
|
-
const logger = getLogger();
|
|
36
|
-
|
|
37
|
-
// MW-003: resolve package workdir for per-package context.md loading
|
|
38
|
-
const packageWorkdir = ctx.story.workdir ? join(ctx.workdir, ctx.story.workdir) : undefined;
|
|
39
|
-
|
|
40
|
-
// Build context from PRD with element-level tracking
|
|
41
|
-
const result = await buildStoryContextFull(ctx.prd, ctx.story, ctx.config, packageWorkdir);
|
|
42
|
-
|
|
43
|
-
// SOFT FAILURE: Empty context is acceptable — agent can work without PRD context
|
|
44
|
-
// This happens when no relevant stories/context is found, which is normal
|
|
45
|
-
if (result) {
|
|
46
|
-
ctx.contextMarkdown = result.markdown;
|
|
47
|
-
ctx.builtContext = result.builtContext;
|
|
48
|
-
} else {
|
|
49
|
-
// Initialize contextMarkdown to empty string if no PRD context was built
|
|
50
|
-
ctx.contextMarkdown = ctx.contextMarkdown || "";
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Add plugin context if any providers are registered
|
|
54
|
-
if (ctx.plugins) {
|
|
55
|
-
const providers = ctx.plugins.getContextProviders();
|
|
56
|
-
if (providers.length > 0) {
|
|
57
|
-
logger.info("context", `Running ${providers.length} plugin context provider(s)`);
|
|
58
|
-
|
|
59
|
-
const pluginElements: ContextElement[] = [];
|
|
60
|
-
let pluginTokensUsed = 0;
|
|
61
|
-
const tokenBudget = ctx.config.execution.contextProviderTokenBudget;
|
|
62
|
-
|
|
63
|
-
for (const provider of providers) {
|
|
64
|
-
// Check if we have budget remaining
|
|
65
|
-
if (pluginTokensUsed >= tokenBudget) {
|
|
66
|
-
logger.info("context", "Plugin context budget exhausted, skipping remaining providers");
|
|
67
|
-
break;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
logger.info("context", `Fetching context from plugin: ${provider.name}`);
|
|
72
|
-
const providerResult = await provider.getContext(ctx.story);
|
|
73
|
-
|
|
74
|
-
// Check if adding this provider's content would exceed budget
|
|
75
|
-
if (pluginTokensUsed + providerResult.estimatedTokens > tokenBudget) {
|
|
76
|
-
logger.info("context", `Skipping plugin ${provider.name}: would exceed budget`);
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Add plugin context as a new element
|
|
81
|
-
pluginElements.push({
|
|
82
|
-
type: "file", // Reuse file type for external context
|
|
83
|
-
content: `## ${providerResult.label}\n\n${providerResult.content}`,
|
|
84
|
-
priority: 50, // Medium priority (between dependencies and errors)
|
|
85
|
-
tokens: providerResult.estimatedTokens,
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
pluginTokensUsed += providerResult.estimatedTokens;
|
|
89
|
-
logger.info(
|
|
90
|
-
"context",
|
|
91
|
-
`Added context from plugin ${provider.name} (${providerResult.estimatedTokens} tokens)`,
|
|
92
|
-
);
|
|
93
|
-
} catch (error) {
|
|
94
|
-
logger.error("context", `Plugin context provider error: ${provider.name}`, {
|
|
95
|
-
error: errorMessage(error),
|
|
96
|
-
});
|
|
97
|
-
// Continue with other providers on error (soft failure)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Append plugin context to existing markdown
|
|
102
|
-
if (pluginElements.length > 0) {
|
|
103
|
-
const pluginMarkdown = pluginElements.map((el) => el.content).join("\n\n");
|
|
104
|
-
ctx.contextMarkdown = ctx.contextMarkdown ? `${ctx.contextMarkdown}\n\n${pluginMarkdown}` : pluginMarkdown;
|
|
105
|
-
|
|
106
|
-
// Update built context with plugin elements
|
|
107
|
-
if (ctx.builtContext) {
|
|
108
|
-
ctx.builtContext.elements.push(...pluginElements);
|
|
109
|
-
ctx.builtContext.totalTokens += pluginTokensUsed;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
logger.info(
|
|
113
|
-
"context",
|
|
114
|
-
`Added ${pluginElements.length} plugin context element(s) (${pluginTokensUsed} tokens total)`,
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return { action: "continue" };
|
|
121
|
-
},
|
|
122
|
-
};
|
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Execution Stage
|
|
3
|
-
*
|
|
4
|
-
* Spawns the agent session(s) to execute the story/stories.
|
|
5
|
-
* Handles both single-session (test-after) and three-session TDD.
|
|
6
|
-
*
|
|
7
|
-
* @returns
|
|
8
|
-
* - `continue`: Agent session succeeded
|
|
9
|
-
* - `fail`: Agent not found or prompt missing
|
|
10
|
-
* - `escalate`: Agent session failed (will retry with higher tier)
|
|
11
|
-
* - `pause`: Three-session TDD fallback (backward compatible, no failureCategory)
|
|
12
|
-
*
|
|
13
|
-
* TDD failure routing by failureCategory:
|
|
14
|
-
* - `isolation-violation` (strict mode) → escalate + ctx.retryAsLite=true
|
|
15
|
-
* - `isolation-violation` (lite mode) → escalate
|
|
16
|
-
* - `session-failure` → escalate
|
|
17
|
-
* - `tests-failing` → escalate
|
|
18
|
-
* - `verifier-rejected` → escalate
|
|
19
|
-
* - `greenfield-no-tests` → escalate (tier-escalation switches to test-after)
|
|
20
|
-
* - no category / unknown → pause (backward compatible)
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```ts
|
|
24
|
-
* // Single session (test-after)
|
|
25
|
-
* await executionStage.execute(ctx);
|
|
26
|
-
* // ctx.agentResult: { success: true, estimatedCost: 0.05, ... }
|
|
27
|
-
*
|
|
28
|
-
* // Three-session TDD
|
|
29
|
-
* await executionStage.execute(ctx);
|
|
30
|
-
* // ctx.agentResult: { success: true, estimatedCost: 0.15, ... }
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
import { existsSync } from "node:fs";
|
|
35
|
-
import { join } from "node:path";
|
|
36
|
-
import { getAgent, validateAgentForTier } from "../../agents";
|
|
37
|
-
import { resolveModel } from "../../config";
|
|
38
|
-
import { resolvePermissions } from "../../config/permissions";
|
|
39
|
-
import { checkMergeConflict, checkStoryAmbiguity, isTriggerEnabled } from "../../interaction/triggers";
|
|
40
|
-
import { getLogger } from "../../logger";
|
|
41
|
-
import type { FailureCategory } from "../../tdd";
|
|
42
|
-
import { runThreeSessionTdd } from "../../tdd";
|
|
43
|
-
import { autoCommitIfDirty, detectMergeConflict } from "../../utils/git";
|
|
44
|
-
import type { PipelineContext, PipelineStage, StageResult } from "../types";
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Resolve the effective working directory for a story.
|
|
48
|
-
* When story.workdir is set, returns join(repoRoot, story.workdir).
|
|
49
|
-
* Otherwise returns the repo root unchanged.
|
|
50
|
-
*
|
|
51
|
-
* MW-001 runtime check: throws if the resolved workdir does not exist on disk.
|
|
52
|
-
*/
|
|
53
|
-
export function resolveStoryWorkdir(repoRoot: string, storyWorkdir?: string): string {
|
|
54
|
-
if (!storyWorkdir) return repoRoot;
|
|
55
|
-
const resolved = join(repoRoot, storyWorkdir);
|
|
56
|
-
if (!existsSync(resolved)) {
|
|
57
|
-
throw new Error(`[execution] story.workdir "${storyWorkdir}" does not exist at "${resolved}"`);
|
|
58
|
-
}
|
|
59
|
-
return resolved;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Detect if agent output contains ambiguity signals
|
|
64
|
-
* Checks for keywords that indicate the agent is unsure about the implementation
|
|
65
|
-
*/
|
|
66
|
-
export function isAmbiguousOutput(output: string): boolean {
|
|
67
|
-
if (!output) return false;
|
|
68
|
-
|
|
69
|
-
const ambiguityKeywords = [
|
|
70
|
-
"unclear",
|
|
71
|
-
"ambiguous",
|
|
72
|
-
"need clarification",
|
|
73
|
-
"please clarify",
|
|
74
|
-
"which one",
|
|
75
|
-
"not sure which",
|
|
76
|
-
];
|
|
77
|
-
|
|
78
|
-
const lowerOutput = output.toLowerCase();
|
|
79
|
-
return ambiguityKeywords.some((keyword) => lowerOutput.includes(keyword));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Determine the pipeline action for a failed TDD result, based on its failureCategory.
|
|
84
|
-
*
|
|
85
|
-
* This is a pure routing function — it mutates only `ctx.retryAsLite` when needed.
|
|
86
|
-
* Exported for unit testing.
|
|
87
|
-
*
|
|
88
|
-
* @param failureCategory - Category set by the TDD orchestrator (or undefined)
|
|
89
|
-
* @param isLiteMode - Whether the story was running in tdd-lite mode
|
|
90
|
-
* @param ctx - Pipeline context (mutated: ctx.retryAsLite may be set)
|
|
91
|
-
* @param reviewReason - Human-readable reason string from the TDD result
|
|
92
|
-
*/
|
|
93
|
-
export function routeTddFailure(
|
|
94
|
-
failureCategory: FailureCategory | undefined,
|
|
95
|
-
isLiteMode: boolean,
|
|
96
|
-
ctx: Pick<PipelineContext, "retryAsLite">,
|
|
97
|
-
reviewReason?: string,
|
|
98
|
-
): StageResult {
|
|
99
|
-
if (failureCategory === "isolation-violation") {
|
|
100
|
-
// Strict mode: request a lite-mode retry on next attempt
|
|
101
|
-
if (!isLiteMode) {
|
|
102
|
-
ctx.retryAsLite = true;
|
|
103
|
-
}
|
|
104
|
-
return { action: "escalate" };
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
failureCategory === "session-failure" ||
|
|
109
|
-
failureCategory === "tests-failing" ||
|
|
110
|
-
failureCategory === "verifier-rejected"
|
|
111
|
-
) {
|
|
112
|
-
return { action: "escalate" };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// S5: greenfield-no-tests → escalate so tier-escalation can switch to test-after
|
|
116
|
-
if (failureCategory === "greenfield-no-tests") {
|
|
117
|
-
return { action: "escalate" };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Default: no category or unknown — backward-compatible pause for human review
|
|
121
|
-
return {
|
|
122
|
-
action: "pause",
|
|
123
|
-
reason: reviewReason || "Three-session TDD requires review",
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export const executionStage: PipelineStage = {
|
|
128
|
-
name: "execution",
|
|
129
|
-
enabled: () => true,
|
|
130
|
-
|
|
131
|
-
async execute(ctx: PipelineContext): Promise<StageResult> {
|
|
132
|
-
const logger = getLogger();
|
|
133
|
-
|
|
134
|
-
// HARD FAILURE: No agent available — cannot proceed without an agent
|
|
135
|
-
const agent = (ctx.agentGetFn ?? _executionDeps.getAgent)(ctx.config.autoMode.defaultAgent);
|
|
136
|
-
if (!agent) {
|
|
137
|
-
return {
|
|
138
|
-
action: "fail",
|
|
139
|
-
reason: `Agent "${ctx.config.autoMode.defaultAgent}" not found`,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Three-session TDD path (respect tdd.enabled config)
|
|
144
|
-
const isTddStrategy =
|
|
145
|
-
ctx.routing.testStrategy === "three-session-tdd" || ctx.routing.testStrategy === "three-session-tdd-lite";
|
|
146
|
-
const isLiteMode = ctx.routing.testStrategy === "three-session-tdd-lite";
|
|
147
|
-
|
|
148
|
-
// TYPE-2 fix: TddConfig has no enabled field, removed dead code
|
|
149
|
-
if (isTddStrategy) {
|
|
150
|
-
logger.info("execution", `Starting three-session TDD${isLiteMode ? " (lite)" : ""}`, {
|
|
151
|
-
storyId: ctx.story.id,
|
|
152
|
-
lite: isLiteMode,
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
const effectiveWorkdir = _executionDeps.resolveStoryWorkdir(ctx.workdir, ctx.story.workdir);
|
|
156
|
-
|
|
157
|
-
const tddResult = await runThreeSessionTdd({
|
|
158
|
-
agent,
|
|
159
|
-
story: ctx.story,
|
|
160
|
-
config: ctx.config,
|
|
161
|
-
workdir: effectiveWorkdir,
|
|
162
|
-
modelTier: ctx.routing.modelTier,
|
|
163
|
-
featureName: ctx.prd.feature,
|
|
164
|
-
contextMarkdown: ctx.contextMarkdown,
|
|
165
|
-
constitution: ctx.constitution?.content,
|
|
166
|
-
dryRun: false,
|
|
167
|
-
lite: isLiteMode,
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
ctx.agentResult = {
|
|
171
|
-
success: tddResult.success,
|
|
172
|
-
estimatedCost: tddResult.totalCost,
|
|
173
|
-
rateLimited: false,
|
|
174
|
-
output: "",
|
|
175
|
-
exitCode: tddResult.success ? 0 : 1,
|
|
176
|
-
durationMs: 0, // TDD result doesn't track total duration
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
// Propagate full-suite gate result so verify stage can skip redundant run (BUG-054)
|
|
180
|
-
if (tddResult.fullSuiteGatePassed) {
|
|
181
|
-
ctx.fullSuiteGatePassed = true;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (!tddResult.success) {
|
|
185
|
-
// Store failure category in context for runner to use at max-attempts decision
|
|
186
|
-
ctx.tddFailureCategory = tddResult.failureCategory;
|
|
187
|
-
|
|
188
|
-
// Log and notify when human review is needed
|
|
189
|
-
if (tddResult.needsHumanReview) {
|
|
190
|
-
logger.warn("execution", "Human review needed", {
|
|
191
|
-
storyId: ctx.story.id,
|
|
192
|
-
reason: tddResult.reviewReason,
|
|
193
|
-
lite: tddResult.lite,
|
|
194
|
-
failureCategory: tddResult.failureCategory,
|
|
195
|
-
});
|
|
196
|
-
// Send notification via interaction chain (Telegram in headless mode)
|
|
197
|
-
if (ctx.interaction) {
|
|
198
|
-
try {
|
|
199
|
-
await ctx.interaction.send({
|
|
200
|
-
id: `human-review-${ctx.story.id}-${Date.now()}`,
|
|
201
|
-
type: "notify",
|
|
202
|
-
featureName: ctx.featureDir ? (ctx.featureDir.split("/").pop() ?? "unknown") : "unknown",
|
|
203
|
-
storyId: ctx.story.id,
|
|
204
|
-
stage: "execution",
|
|
205
|
-
summary: `⚠️ Human review needed: ${ctx.story.id}`,
|
|
206
|
-
detail: `Story: ${ctx.story.title}\nReason: ${tddResult.reviewReason ?? "No reason provided"}\nCategory: ${tddResult.failureCategory ?? "unknown"}`,
|
|
207
|
-
fallback: "continue",
|
|
208
|
-
createdAt: Date.now(),
|
|
209
|
-
});
|
|
210
|
-
} catch (notifyErr) {
|
|
211
|
-
logger.warn("execution", "Failed to send human review notification", {
|
|
212
|
-
storyId: ctx.story.id,
|
|
213
|
-
error: String(notifyErr),
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return routeTddFailure(tddResult.failureCategory, isLiteMode, ctx, tddResult.reviewReason);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return { action: "continue" };
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Single/batch session (test-after) path
|
|
226
|
-
// HARD FAILURE: Missing prompt indicates pipeline misconfiguration
|
|
227
|
-
if (!ctx.prompt) {
|
|
228
|
-
return { action: "fail", reason: "Prompt not built (prompt stage skipped?)" };
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Validate agent supports the requested tier
|
|
232
|
-
if (!_executionDeps.validateAgentForTier(agent, ctx.routing.modelTier)) {
|
|
233
|
-
logger.warn("execution", "Agent tier mismatch", {
|
|
234
|
-
storyId: ctx.story.id,
|
|
235
|
-
agentName: agent.name,
|
|
236
|
-
requestedTier: ctx.routing.modelTier,
|
|
237
|
-
supportedTiers: agent.capabilities.supportedTiers,
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const storyWorkdir = _executionDeps.resolveStoryWorkdir(ctx.workdir, ctx.story.workdir);
|
|
242
|
-
|
|
243
|
-
const result = await agent.run({
|
|
244
|
-
prompt: ctx.prompt,
|
|
245
|
-
workdir: storyWorkdir,
|
|
246
|
-
modelTier: ctx.routing.modelTier,
|
|
247
|
-
modelDef: resolveModel(ctx.config.models[ctx.routing.modelTier]),
|
|
248
|
-
timeoutSeconds: ctx.config.execution.sessionTimeoutSeconds,
|
|
249
|
-
dangerouslySkipPermissions: resolvePermissions(ctx.config, "run").skipPermissions,
|
|
250
|
-
pipelineStage: "run",
|
|
251
|
-
config: ctx.config,
|
|
252
|
-
maxInteractionTurns: ctx.config.agent?.maxInteractionTurns,
|
|
253
|
-
pidRegistry: ctx.pidRegistry,
|
|
254
|
-
featureName: ctx.prd.feature,
|
|
255
|
-
storyId: ctx.story.id,
|
|
256
|
-
// No sessionRole for single-session strategies (no role suffix in session name)
|
|
257
|
-
interactionBridge: (() => {
|
|
258
|
-
const plugin = ctx.interaction?.getPrimary();
|
|
259
|
-
if (!plugin) return undefined;
|
|
260
|
-
const QUESTION_PATTERNS = [/\?/, /\bwhich\b/i, /\bshould i\b/i, /\bunclear\b/i, /\bplease clarify\b/i];
|
|
261
|
-
return {
|
|
262
|
-
detectQuestion: async (text: string) => QUESTION_PATTERNS.some((p) => p.test(text)),
|
|
263
|
-
onQuestionDetected: async (text: string) => {
|
|
264
|
-
const requestId = `ix-acp-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
265
|
-
await plugin.send({
|
|
266
|
-
id: requestId,
|
|
267
|
-
type: "input",
|
|
268
|
-
featureName: ctx.prd.feature,
|
|
269
|
-
storyId: ctx.story.id,
|
|
270
|
-
stage: "execution",
|
|
271
|
-
summary: text,
|
|
272
|
-
fallback: "continue",
|
|
273
|
-
createdAt: Date.now(),
|
|
274
|
-
});
|
|
275
|
-
try {
|
|
276
|
-
const response = await plugin.receive(requestId, 120_000);
|
|
277
|
-
return response.value ?? "continue";
|
|
278
|
-
} catch {
|
|
279
|
-
return "continue";
|
|
280
|
-
}
|
|
281
|
-
},
|
|
282
|
-
};
|
|
283
|
-
})(),
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
ctx.agentResult = result;
|
|
287
|
-
|
|
288
|
-
// BUG-058: Auto-commit if agent left uncommitted changes (single-session/test-after)
|
|
289
|
-
await autoCommitIfDirty(storyWorkdir, "execution", "single-session", ctx.story.id);
|
|
290
|
-
|
|
291
|
-
// merge-conflict trigger: detect CONFLICT markers in agent output
|
|
292
|
-
const combinedOutput = (result.output ?? "") + (result.stderr ?? "");
|
|
293
|
-
if (
|
|
294
|
-
_executionDeps.detectMergeConflict(combinedOutput) &&
|
|
295
|
-
ctx.interaction &&
|
|
296
|
-
isTriggerEnabled("merge-conflict", ctx.config)
|
|
297
|
-
) {
|
|
298
|
-
const shouldProceed = await _executionDeps.checkMergeConflict(
|
|
299
|
-
{ featureName: ctx.prd.feature, storyId: ctx.story.id },
|
|
300
|
-
ctx.config,
|
|
301
|
-
ctx.interaction,
|
|
302
|
-
);
|
|
303
|
-
if (!shouldProceed) {
|
|
304
|
-
logger.error("execution", "Merge conflict detected — aborting story", { storyId: ctx.story.id });
|
|
305
|
-
return { action: "fail", reason: "Merge conflict detected" };
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// story-ambiguity trigger: detect ambiguity signals in agent output
|
|
310
|
-
if (
|
|
311
|
-
result.success &&
|
|
312
|
-
_executionDeps.isAmbiguousOutput(combinedOutput) &&
|
|
313
|
-
ctx.interaction &&
|
|
314
|
-
isTriggerEnabled("story-ambiguity", ctx.config)
|
|
315
|
-
) {
|
|
316
|
-
const shouldContinue = await _executionDeps.checkStoryAmbiguity(
|
|
317
|
-
{ featureName: ctx.prd.feature, storyId: ctx.story.id, reason: "Agent output suggests ambiguity" },
|
|
318
|
-
ctx.config,
|
|
319
|
-
ctx.interaction,
|
|
320
|
-
);
|
|
321
|
-
if (!shouldContinue) {
|
|
322
|
-
logger.warn("execution", "Story ambiguity detected — escalating story", { storyId: ctx.story.id });
|
|
323
|
-
return { action: "escalate", reason: "Story ambiguity detected — needs clarification" };
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (!result.success) {
|
|
328
|
-
logger.error("execution", "Agent session failed", {
|
|
329
|
-
exitCode: result.exitCode,
|
|
330
|
-
stderr: result.stderr || "",
|
|
331
|
-
rateLimited: result.rateLimited,
|
|
332
|
-
storyId: ctx.story.id,
|
|
333
|
-
});
|
|
334
|
-
if (result.rateLimited) {
|
|
335
|
-
logger.warn("execution", "Rate limited — will retry", { storyId: ctx.story.id });
|
|
336
|
-
}
|
|
337
|
-
return { action: "escalate" };
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
logger.info("execution", "Agent session complete", {
|
|
341
|
-
storyId: ctx.story.id,
|
|
342
|
-
cost: result.estimatedCost,
|
|
343
|
-
});
|
|
344
|
-
return { action: "continue" };
|
|
345
|
-
},
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Swappable dependencies for testing (avoids mock.module() which leaks in Bun 1.x).
|
|
350
|
-
*/
|
|
351
|
-
export const _executionDeps = {
|
|
352
|
-
getAgent,
|
|
353
|
-
validateAgentForTier,
|
|
354
|
-
detectMergeConflict,
|
|
355
|
-
checkMergeConflict,
|
|
356
|
-
isAmbiguousOutput,
|
|
357
|
-
checkStoryAmbiguity,
|
|
358
|
-
resolveStoryWorkdir,
|
|
359
|
-
};
|