@nathapp/nax 0.40.0 → 0.41.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/dist/nax.js +1166 -277
- package/package.json +2 -2
- package/src/acceptance/fix-generator.ts +4 -35
- package/src/acceptance/generator.ts +27 -28
- package/src/acceptance/refinement.ts +72 -5
- package/src/acceptance/templates/cli.ts +47 -0
- package/src/acceptance/templates/component.ts +78 -0
- package/src/acceptance/templates/e2e.ts +43 -0
- package/src/acceptance/templates/index.ts +21 -0
- package/src/acceptance/templates/snapshot.ts +50 -0
- package/src/acceptance/templates/unit.ts +48 -0
- package/src/acceptance/types.ts +9 -1
- package/src/agents/acp/adapter.ts +644 -0
- package/src/agents/acp/cost.ts +79 -0
- package/src/agents/acp/index.ts +9 -0
- package/src/agents/acp/interaction-bridge.ts +126 -0
- package/src/agents/acp/parser.ts +166 -0
- package/src/agents/acp/spawn-client.ts +309 -0
- package/src/agents/acp/types.ts +22 -0
- package/src/agents/claude-complete.ts +3 -3
- package/src/agents/registry.ts +83 -0
- package/src/agents/types-extended.ts +23 -0
- package/src/agents/types.ts +17 -0
- package/src/cli/analyze.ts +6 -2
- package/src/cli/init-detect.ts +94 -8
- package/src/cli/init.ts +2 -2
- package/src/cli/plan.ts +23 -0
- package/src/config/defaults.ts +1 -0
- package/src/config/index.ts +1 -1
- package/src/config/runtime-types.ts +17 -0
- package/src/config/schema.ts +3 -1
- package/src/config/schemas.ts +9 -1
- package/src/config/types.ts +2 -0
- package/src/execution/executor-types.ts +6 -0
- package/src/execution/iteration-runner.ts +2 -0
- package/src/execution/lifecycle/acceptance-loop.ts +5 -2
- package/src/execution/lifecycle/run-initialization.ts +16 -4
- package/src/execution/lifecycle/run-setup.ts +4 -0
- package/src/execution/runner-completion.ts +11 -1
- package/src/execution/runner-execution.ts +8 -0
- package/src/execution/runner-setup.ts +4 -0
- package/src/execution/runner.ts +10 -0
- package/src/pipeline/stages/acceptance-setup.ts +4 -0
- package/src/pipeline/stages/execution.ts +33 -1
- package/src/pipeline/stages/routing.ts +18 -7
- package/src/pipeline/types.ts +10 -0
- package/src/tdd/orchestrator.ts +7 -0
- package/src/tdd/rectification-gate.ts +6 -0
- package/src/tdd/session-runner.ts +4 -0
|
@@ -22,6 +22,7 @@ import type { InteractionChain } from "../../interaction";
|
|
|
22
22
|
import { initInteractionChain } from "../../interaction";
|
|
23
23
|
import { getSafeLogger } from "../../logger";
|
|
24
24
|
import { pipelineEventBus } from "../../pipeline/event-bus";
|
|
25
|
+
import type { AgentGetFn } from "../../pipeline/types";
|
|
25
26
|
import { loadPlugins } from "../../plugins/loader";
|
|
26
27
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
27
28
|
import type { PRD } from "../../prd";
|
|
@@ -53,6 +54,8 @@ export interface RunSetupOptions {
|
|
|
53
54
|
// BUG-017: Additional getters for run.complete event on SIGTERM
|
|
54
55
|
getStoriesCompleted: () => number;
|
|
55
56
|
getTotalStories: () => number;
|
|
57
|
+
/** Protocol-aware agent resolver — passed from runner.ts registry */
|
|
58
|
+
agentGetFn?: AgentGetFn;
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
export interface RunSetupResult {
|
|
@@ -206,6 +209,7 @@ export async function setupRun(options: RunSetupOptions): Promise<RunSetupResult
|
|
|
206
209
|
prdPath,
|
|
207
210
|
workdir,
|
|
208
211
|
dryRun,
|
|
212
|
+
agentGetFn: options.agentGetFn,
|
|
209
213
|
});
|
|
210
214
|
prd = initResult.prd;
|
|
211
215
|
const counts = initResult.storyCounts;
|
|
@@ -11,9 +11,11 @@ import { fireHook } from "../hooks";
|
|
|
11
11
|
import { getSafeLogger } from "../logger";
|
|
12
12
|
import type { StoryMetrics } from "../metrics";
|
|
13
13
|
import type { PipelineEventEmitter } from "../pipeline/events";
|
|
14
|
+
import type { AgentGetFn } from "../pipeline/types";
|
|
14
15
|
import type { PluginRegistry } from "../plugins/registry";
|
|
15
16
|
import { isComplete } from "../prd";
|
|
16
17
|
import type { PRD } from "../prd";
|
|
18
|
+
import { autoCommitIfDirty } from "../utils/git";
|
|
17
19
|
import { stopHeartbeat, writeExitSummary } from "./crash-recovery";
|
|
18
20
|
import { hookCtx } from "./story-context";
|
|
19
21
|
|
|
@@ -42,6 +44,10 @@ export interface RunnerCompletionOptions {
|
|
|
42
44
|
statusWriter: any;
|
|
43
45
|
pluginRegistry: PluginRegistry;
|
|
44
46
|
eventEmitter?: PipelineEventEmitter;
|
|
47
|
+
/** Protocol-aware agent resolver */
|
|
48
|
+
agentGetFn?: AgentGetFn;
|
|
49
|
+
/** Path to prd.json — required for acceptance fix story writes */
|
|
50
|
+
prdPath: string;
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
/**
|
|
@@ -67,7 +73,7 @@ export async function runCompletionPhase(options: RunnerCompletionOptions): Prom
|
|
|
67
73
|
const acceptanceResult = await runAcceptanceLoop({
|
|
68
74
|
config: options.config,
|
|
69
75
|
prd: options.prd,
|
|
70
|
-
prdPath:
|
|
76
|
+
prdPath: options.prdPath,
|
|
71
77
|
workdir: options.workdir,
|
|
72
78
|
featureDir: options.featureDir,
|
|
73
79
|
hooks: options.hooks,
|
|
@@ -79,6 +85,7 @@ export async function runCompletionPhase(options: RunnerCompletionOptions): Prom
|
|
|
79
85
|
pluginRegistry: options.pluginRegistry,
|
|
80
86
|
eventEmitter: options.eventEmitter,
|
|
81
87
|
statusWriter: options.statusWriter,
|
|
88
|
+
agentGetFn: options.agentGetFn,
|
|
82
89
|
});
|
|
83
90
|
|
|
84
91
|
Object.assign(options, {
|
|
@@ -153,6 +160,9 @@ export async function runCompletionPhase(options: RunnerCompletionOptions): Prom
|
|
|
153
160
|
durationMs,
|
|
154
161
|
);
|
|
155
162
|
|
|
163
|
+
// Commit status.json and any other nax runtime files left dirty at run end
|
|
164
|
+
await autoCommitIfDirty(options.workdir, "run.complete", "run-summary", options.feature);
|
|
165
|
+
|
|
156
166
|
return {
|
|
157
167
|
durationMs,
|
|
158
168
|
runCompletedAt,
|
|
@@ -10,6 +10,7 @@ import type { LoadedHooksConfig } from "../hooks";
|
|
|
10
10
|
import { getSafeLogger } from "../logger";
|
|
11
11
|
import type { StoryMetrics } from "../metrics";
|
|
12
12
|
import type { PipelineEventEmitter } from "../pipeline/events";
|
|
13
|
+
import type { AgentGetFn } from "../pipeline/types";
|
|
13
14
|
import type { PluginRegistry } from "../plugins/registry";
|
|
14
15
|
import type { PRD } from "../prd";
|
|
15
16
|
import { tryLlmBatchRoute } from "../routing/batch-route";
|
|
@@ -17,6 +18,7 @@ import { clearCache as clearLlmCache, routeBatch as llmRouteBatch } from "../rou
|
|
|
17
18
|
import { precomputeBatchPlan } from "./batching";
|
|
18
19
|
import { getAllReadyStories } from "./helpers";
|
|
19
20
|
import type { ParallelExecutorOptions, ParallelExecutorResult } from "./parallel-executor";
|
|
21
|
+
import type { PidRegistry } from "./pid-registry";
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Options for the execution phase.
|
|
@@ -42,6 +44,10 @@ export interface RunnerExecutionOptions {
|
|
|
42
44
|
headless: boolean;
|
|
43
45
|
parallel?: number;
|
|
44
46
|
runParallelExecution?: (options: ParallelExecutorOptions, prd: PRD) => Promise<ParallelExecutorResult>;
|
|
47
|
+
/** Protocol-aware agent resolver — created once in runner.ts from createAgentRegistry(config) */
|
|
48
|
+
agentGetFn?: AgentGetFn;
|
|
49
|
+
/** PID registry for crash recovery — passed to agent.run() to register child processes. */
|
|
50
|
+
pidRegistry?: PidRegistry;
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
/**
|
|
@@ -198,6 +204,8 @@ export async function runExecutionPhase(
|
|
|
198
204
|
runId: options.runId,
|
|
199
205
|
startTime: options.startTime,
|
|
200
206
|
batchPlan,
|
|
207
|
+
agentGetFn: options.agentGetFn,
|
|
208
|
+
pidRegistry: options.pidRegistry,
|
|
201
209
|
},
|
|
202
210
|
prd,
|
|
203
211
|
);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { NaxConfig } from "../config";
|
|
9
9
|
import type { LoadedHooksConfig } from "../hooks";
|
|
10
|
+
import type { AgentGetFn } from "../pipeline/types";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Options for the setup phase.
|
|
@@ -31,6 +32,8 @@ export interface RunnerSetupOptions {
|
|
|
31
32
|
getIterations: () => number;
|
|
32
33
|
getStoriesCompleted: () => number;
|
|
33
34
|
getTotalStories: () => number;
|
|
35
|
+
/** Protocol-aware agent resolver — created from createAgentRegistry(config) in runner.ts */
|
|
36
|
+
agentGetFn?: AgentGetFn;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
/**
|
|
@@ -76,6 +79,7 @@ export async function runSetupPhase(options: RunnerSetupOptions): Promise<Runner
|
|
|
76
79
|
// BUG-017: Pass getters for run.complete event on SIGTERM
|
|
77
80
|
getStoriesCompleted: options.getStoriesCompleted,
|
|
78
81
|
getTotalStories: options.getTotalStories,
|
|
82
|
+
agentGetFn: options.agentGetFn,
|
|
79
83
|
});
|
|
80
84
|
|
|
81
85
|
return setupResult;
|
package/src/execution/runner.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* - runner-completion.ts: Acceptance loop, hooks, metrics
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { createAgentRegistry } from "../agents/registry";
|
|
16
17
|
import type { NaxConfig } from "../config";
|
|
17
18
|
import type { LoadedHooksConfig } from "../hooks";
|
|
18
19
|
import { fireHook } from "../hooks";
|
|
@@ -116,6 +117,10 @@ export async function run(options: RunOptions): Promise<RunResult> {
|
|
|
116
117
|
|
|
117
118
|
const logger = getSafeLogger();
|
|
118
119
|
|
|
120
|
+
// Create protocol-aware agent registry (ACP wiring — ACP-003/registry-wiring)
|
|
121
|
+
const registry = createAgentRegistry(config);
|
|
122
|
+
const agentGetFn = registry.getAgent.bind(registry);
|
|
123
|
+
|
|
119
124
|
// Declare prd before crash handler setup to avoid TDZ if SIGTERM arrives during setup
|
|
120
125
|
// biome-ignore lint/suspicious/noExplicitAny: PRD type initialized during setup
|
|
121
126
|
let prd: any | undefined;
|
|
@@ -137,6 +142,7 @@ export async function run(options: RunOptions): Promise<RunResult> {
|
|
|
137
142
|
skipPrecheck,
|
|
138
143
|
headless,
|
|
139
144
|
formatterMode,
|
|
145
|
+
agentGetFn,
|
|
140
146
|
getTotalCost: () => totalCost,
|
|
141
147
|
getIterations: () => iterations,
|
|
142
148
|
// BUG-017: Pass getters for run.complete event on SIGTERM
|
|
@@ -170,6 +176,8 @@ export async function run(options: RunOptions): Promise<RunResult> {
|
|
|
170
176
|
headless,
|
|
171
177
|
parallel,
|
|
172
178
|
runParallelExecution: _runnerDeps.runParallelExecution ?? undefined,
|
|
179
|
+
agentGetFn,
|
|
180
|
+
pidRegistry,
|
|
173
181
|
},
|
|
174
182
|
prd,
|
|
175
183
|
pluginRegistry,
|
|
@@ -198,6 +206,7 @@ export async function run(options: RunOptions): Promise<RunResult> {
|
|
|
198
206
|
hooks,
|
|
199
207
|
feature,
|
|
200
208
|
workdir,
|
|
209
|
+
prdPath,
|
|
201
210
|
statusFile,
|
|
202
211
|
logFilePath,
|
|
203
212
|
runId,
|
|
@@ -214,6 +223,7 @@ export async function run(options: RunOptions): Promise<RunResult> {
|
|
|
214
223
|
statusWriter,
|
|
215
224
|
pluginRegistry,
|
|
216
225
|
eventEmitter,
|
|
226
|
+
agentGetFn,
|
|
217
227
|
});
|
|
218
228
|
|
|
219
229
|
const { durationMs } = completionResult;
|
|
@@ -89,6 +89,8 @@ export const acceptanceSetupStage: PipelineStage = {
|
|
|
89
89
|
storyId: ctx.prd.userStories[0]?.id ?? "US-001",
|
|
90
90
|
codebaseContext: "",
|
|
91
91
|
config: ctx.config,
|
|
92
|
+
testStrategy: ctx.config.acceptance.testStrategy,
|
|
93
|
+
testFramework: ctx.config.acceptance.testFramework,
|
|
92
94
|
});
|
|
93
95
|
} else {
|
|
94
96
|
refinedCriteria = allCriteria.map((c) => ({
|
|
@@ -108,6 +110,8 @@ export const acceptanceSetupStage: PipelineStage = {
|
|
|
108
110
|
modelTier: ctx.config.acceptance.model ?? "fast",
|
|
109
111
|
modelDef: resolveModel(ctx.config.models[ctx.config.acceptance.model ?? "fast"]),
|
|
110
112
|
config: ctx.config,
|
|
113
|
+
testStrategy: ctx.config.acceptance.testStrategy,
|
|
114
|
+
testFramework: ctx.config.acceptance.testFramework,
|
|
111
115
|
});
|
|
112
116
|
|
|
113
117
|
await _acceptanceSetupDeps.writeFile(testPath, result.testCode);
|
|
@@ -107,7 +107,7 @@ export const executionStage: PipelineStage = {
|
|
|
107
107
|
const logger = getLogger();
|
|
108
108
|
|
|
109
109
|
// HARD FAILURE: No agent available — cannot proceed without an agent
|
|
110
|
-
const agent = _executionDeps.getAgent(ctx.config.autoMode.defaultAgent);
|
|
110
|
+
const agent = (ctx.agentGetFn ?? _executionDeps.getAgent)(ctx.config.autoMode.defaultAgent);
|
|
111
111
|
if (!agent) {
|
|
112
112
|
return {
|
|
113
113
|
action: "fail",
|
|
@@ -133,6 +133,7 @@ export const executionStage: PipelineStage = {
|
|
|
133
133
|
config: ctx.config,
|
|
134
134
|
workdir: ctx.workdir,
|
|
135
135
|
modelTier: ctx.routing.modelTier,
|
|
136
|
+
featureName: ctx.prd.feature,
|
|
136
137
|
contextMarkdown: ctx.contextMarkdown,
|
|
137
138
|
constitution: ctx.constitution?.content,
|
|
138
139
|
dryRun: false,
|
|
@@ -217,6 +218,37 @@ export const executionStage: PipelineStage = {
|
|
|
217
218
|
modelDef: resolveModel(ctx.config.models[ctx.routing.modelTier]),
|
|
218
219
|
timeoutSeconds: ctx.config.execution.sessionTimeoutSeconds,
|
|
219
220
|
dangerouslySkipPermissions: ctx.config.execution.dangerouslySkipPermissions,
|
|
221
|
+
pidRegistry: ctx.pidRegistry,
|
|
222
|
+
featureName: ctx.prd.feature,
|
|
223
|
+
storyId: ctx.story.id,
|
|
224
|
+
// No sessionRole for single-session strategies (no role suffix in session name)
|
|
225
|
+
interactionBridge: (() => {
|
|
226
|
+
const plugin = ctx.interaction?.getPrimary();
|
|
227
|
+
if (!plugin) return undefined;
|
|
228
|
+
const QUESTION_PATTERNS = [/\?/, /\bwhich\b/i, /\bshould i\b/i, /\bunclear\b/i, /\bplease clarify\b/i];
|
|
229
|
+
return {
|
|
230
|
+
detectQuestion: async (text: string) => QUESTION_PATTERNS.some((p) => p.test(text)),
|
|
231
|
+
onQuestionDetected: async (text: string) => {
|
|
232
|
+
const requestId = `ix-acp-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
233
|
+
await plugin.send({
|
|
234
|
+
id: requestId,
|
|
235
|
+
type: "input",
|
|
236
|
+
featureName: ctx.prd.feature,
|
|
237
|
+
storyId: ctx.story.id,
|
|
238
|
+
stage: "execution",
|
|
239
|
+
summary: text,
|
|
240
|
+
fallback: "continue",
|
|
241
|
+
createdAt: Date.now(),
|
|
242
|
+
});
|
|
243
|
+
try {
|
|
244
|
+
const response = await plugin.receive(requestId, 120_000);
|
|
245
|
+
return response.value ?? "continue";
|
|
246
|
+
} catch {
|
|
247
|
+
return "continue";
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
})(),
|
|
220
252
|
});
|
|
221
253
|
|
|
222
254
|
ctx.agentResult = result;
|
|
@@ -44,7 +44,13 @@ import type { PipelineContext, PipelineStage, RoutingResult, StageResult } from
|
|
|
44
44
|
* Used as the default implementation in _routingDeps.runDecompose.
|
|
45
45
|
* In production, replace with an LLM-backed adapter.
|
|
46
46
|
*/
|
|
47
|
-
async function runDecompose(
|
|
47
|
+
async function runDecompose(
|
|
48
|
+
story: UserStory,
|
|
49
|
+
prd: PRD,
|
|
50
|
+
config: NaxConfig,
|
|
51
|
+
_workdir: string,
|
|
52
|
+
agentGetFn?: import("../types").AgentGetFn,
|
|
53
|
+
): Promise<DecomposeResult> {
|
|
48
54
|
const naxDecompose = config.decompose;
|
|
49
55
|
const builderConfig: BuilderDecomposeConfig = {
|
|
50
56
|
maxSubStories: naxDecompose?.maxSubstories ?? 5,
|
|
@@ -52,10 +58,15 @@ async function runDecompose(story: UserStory, prd: PRD, config: NaxConfig, _work
|
|
|
52
58
|
maxRetries: naxDecompose?.maxRetries ?? 2,
|
|
53
59
|
};
|
|
54
60
|
|
|
55
|
-
//
|
|
61
|
+
// Resolve the default agent adapter for LLM-backed decompose.
|
|
62
|
+
// Falls back to agent.complete() with JSON mode — works with both CLI and ACP adapters.
|
|
63
|
+
const agent = (agentGetFn ?? getAgent)(config.autoMode.defaultAgent);
|
|
64
|
+
if (!agent) {
|
|
65
|
+
throw new Error(`[decompose] Agent "${config.autoMode.defaultAgent}" not found — cannot decompose`);
|
|
66
|
+
}
|
|
56
67
|
const adapter = {
|
|
57
|
-
async decompose(
|
|
58
|
-
|
|
68
|
+
async decompose(prompt: string): Promise<string> {
|
|
69
|
+
return agent.complete(prompt, { jsonMode: true });
|
|
59
70
|
},
|
|
60
71
|
};
|
|
61
72
|
|
|
@@ -71,7 +82,7 @@ export const routingStage: PipelineStage = {
|
|
|
71
82
|
|
|
72
83
|
// Resolve agent adapter for LLM routing (shared with execution)
|
|
73
84
|
const agentName = ctx.config.execution?.agent ?? "claude";
|
|
74
|
-
const adapter = _routingDeps.getAgent(agentName);
|
|
85
|
+
const adapter = (ctx.agentGetFn ?? _routingDeps.getAgent)(agentName);
|
|
75
86
|
|
|
76
87
|
// Staleness detection (RRP-003):
|
|
77
88
|
// - story.routing absent → cache miss (no prior routing)
|
|
@@ -173,7 +184,7 @@ export const routingStage: PipelineStage = {
|
|
|
173
184
|
`Story ${ctx.story.id} is oversized (${acCount} ACs) but decompose is disabled — continuing with original`,
|
|
174
185
|
);
|
|
175
186
|
} else if (decomposeConfig.trigger === "auto") {
|
|
176
|
-
const result = await _routingDeps.runDecompose(ctx.story, ctx.prd, ctx.config, ctx.workdir);
|
|
187
|
+
const result = await _routingDeps.runDecompose(ctx.story, ctx.prd, ctx.config, ctx.workdir, ctx.agentGetFn);
|
|
177
188
|
if (result.validation.valid) {
|
|
178
189
|
_routingDeps.applyDecomposition(ctx.prd, result);
|
|
179
190
|
if (ctx.prdPath) {
|
|
@@ -193,7 +204,7 @@ export const routingStage: PipelineStage = {
|
|
|
193
204
|
ctx.interaction!,
|
|
194
205
|
);
|
|
195
206
|
if (action === "decompose") {
|
|
196
|
-
const result = await _routingDeps.runDecompose(ctx.story, ctx.prd, ctx.config, ctx.workdir);
|
|
207
|
+
const result = await _routingDeps.runDecompose(ctx.story, ctx.prd, ctx.config, ctx.workdir, ctx.agentGetFn);
|
|
197
208
|
if (result.validation.valid) {
|
|
198
209
|
_routingDeps.applyDecomposition(ctx.prd, result);
|
|
199
210
|
if (ctx.prdPath) {
|
package/src/pipeline/types.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { AgentResult } from "../agents/types";
|
|
|
8
8
|
import type { NaxConfig } from "../config/schema";
|
|
9
9
|
import type { ConstitutionResult } from "../constitution/types";
|
|
10
10
|
import type { BuiltContext } from "../context/types";
|
|
11
|
+
import type { PidRegistry } from "../execution/pid-registry";
|
|
11
12
|
import type { HooksConfig } from "../hooks/types";
|
|
12
13
|
import type { InteractionChain } from "../interaction/chain";
|
|
13
14
|
import type { StoryMetrics } from "../metrics/types";
|
|
@@ -52,6 +53,8 @@ export interface RoutingResult {
|
|
|
52
53
|
* };
|
|
53
54
|
* ```
|
|
54
55
|
*/
|
|
56
|
+
export type AgentGetFn = (name: string) => import("../agents/types").AgentAdapter | undefined;
|
|
57
|
+
|
|
55
58
|
export interface PipelineContext {
|
|
56
59
|
/** Ngent configuration */
|
|
57
60
|
config: NaxConfig;
|
|
@@ -73,6 +76,13 @@ export interface PipelineContext {
|
|
|
73
76
|
hooks: HooksConfig;
|
|
74
77
|
/** Plugin registry (optional, for plugin-provided extensions) */
|
|
75
78
|
plugins?: PluginRegistry;
|
|
79
|
+
/**
|
|
80
|
+
* Protocol-aware agent resolver. When set (ACP mode), returns AcpAgentAdapter;
|
|
81
|
+
* falls back to standalone getAgent (CLI mode) when absent.
|
|
82
|
+
*/
|
|
83
|
+
agentGetFn?: AgentGetFn;
|
|
84
|
+
/** PID registry for crash recovery — passed through to agent.run() for child process registration. */
|
|
85
|
+
pidRegistry?: PidRegistry;
|
|
76
86
|
/** Interaction chain (optional, for human-in-the-loop triggers) */
|
|
77
87
|
interaction?: InteractionChain;
|
|
78
88
|
/** Constitution result (set by constitutionStage) */
|
package/src/tdd/orchestrator.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface ThreeSessionTddOptions {
|
|
|
28
28
|
config: NaxConfig;
|
|
29
29
|
workdir: string;
|
|
30
30
|
modelTier: ModelTier;
|
|
31
|
+
/** Feature name — used for ACP session naming (nax-<hash>-<feature>-<story>-<role>) */
|
|
32
|
+
featureName?: string;
|
|
31
33
|
contextMarkdown?: string;
|
|
32
34
|
constitution?: string;
|
|
33
35
|
dryRun?: boolean;
|
|
@@ -45,6 +47,7 @@ export async function runThreeSessionTdd(options: ThreeSessionTddOptions): Promi
|
|
|
45
47
|
config,
|
|
46
48
|
workdir,
|
|
47
49
|
modelTier,
|
|
50
|
+
featureName,
|
|
48
51
|
contextMarkdown,
|
|
49
52
|
constitution,
|
|
50
53
|
dryRun = false,
|
|
@@ -135,6 +138,7 @@ export async function runThreeSessionTdd(options: ThreeSessionTddOptions): Promi
|
|
|
135
138
|
lite,
|
|
136
139
|
lite,
|
|
137
140
|
constitution,
|
|
141
|
+
featureName,
|
|
138
142
|
);
|
|
139
143
|
sessions.push(session1);
|
|
140
144
|
}
|
|
@@ -240,6 +244,7 @@ export async function runThreeSessionTdd(options: ThreeSessionTddOptions): Promi
|
|
|
240
244
|
lite,
|
|
241
245
|
lite,
|
|
242
246
|
constitution,
|
|
247
|
+
featureName,
|
|
243
248
|
);
|
|
244
249
|
sessions.push(session2);
|
|
245
250
|
|
|
@@ -269,6 +274,7 @@ export async function runThreeSessionTdd(options: ThreeSessionTddOptions): Promi
|
|
|
269
274
|
contextMarkdown,
|
|
270
275
|
lite,
|
|
271
276
|
logger,
|
|
277
|
+
featureName,
|
|
272
278
|
);
|
|
273
279
|
|
|
274
280
|
// Session 3: Verifier
|
|
@@ -286,6 +292,7 @@ export async function runThreeSessionTdd(options: ThreeSessionTddOptions): Promi
|
|
|
286
292
|
false,
|
|
287
293
|
false,
|
|
288
294
|
constitution,
|
|
295
|
+
featureName,
|
|
289
296
|
);
|
|
290
297
|
sessions.push(session3);
|
|
291
298
|
|
|
@@ -34,6 +34,7 @@ export async function runFullSuiteGate(
|
|
|
34
34
|
contextMarkdown: string | undefined,
|
|
35
35
|
lite: boolean,
|
|
36
36
|
logger: ReturnType<typeof getLogger>,
|
|
37
|
+
featureName?: string,
|
|
37
38
|
): Promise<boolean> {
|
|
38
39
|
const rectificationEnabled = config.execution.rectification?.enabled ?? false;
|
|
39
40
|
if (!rectificationEnabled) return false;
|
|
@@ -67,6 +68,7 @@ export async function runFullSuiteGate(
|
|
|
67
68
|
rectificationConfig,
|
|
68
69
|
testCmd,
|
|
69
70
|
fullSuiteTimeout,
|
|
71
|
+
featureName,
|
|
70
72
|
);
|
|
71
73
|
}
|
|
72
74
|
|
|
@@ -118,6 +120,7 @@ async function runRectificationLoop(
|
|
|
118
120
|
rectificationConfig: NonNullable<NaxConfig["execution"]["rectification"]>,
|
|
119
121
|
testCmd: string,
|
|
120
122
|
fullSuiteTimeout: number,
|
|
123
|
+
featureName?: string,
|
|
121
124
|
): Promise<boolean> {
|
|
122
125
|
const rectificationState: RectificationState = {
|
|
123
126
|
attempt: 0,
|
|
@@ -156,6 +159,9 @@ async function runRectificationLoop(
|
|
|
156
159
|
modelDef: resolveModel(config.models[implementerTier]),
|
|
157
160
|
timeoutSeconds: config.execution.sessionTimeoutSeconds,
|
|
158
161
|
dangerouslySkipPermissions: config.execution.dangerouslySkipPermissions,
|
|
162
|
+
featureName,
|
|
163
|
+
storyId: story.id,
|
|
164
|
+
sessionRole: "implementer",
|
|
159
165
|
});
|
|
160
166
|
|
|
161
167
|
if (!rectifyResult.success && rectifyResult.pid) {
|
|
@@ -84,6 +84,7 @@ export async function runTddSession(
|
|
|
84
84
|
lite = false,
|
|
85
85
|
skipIsolation = false,
|
|
86
86
|
constitution?: string,
|
|
87
|
+
featureName?: string,
|
|
87
88
|
): Promise<TddSessionResult> {
|
|
88
89
|
const startTime = Date.now();
|
|
89
90
|
|
|
@@ -130,6 +131,9 @@ export async function runTddSession(
|
|
|
130
131
|
modelDef: resolveModel(config.models[modelTier]),
|
|
131
132
|
timeoutSeconds: config.execution.sessionTimeoutSeconds,
|
|
132
133
|
dangerouslySkipPermissions: config.execution.dangerouslySkipPermissions,
|
|
134
|
+
featureName,
|
|
135
|
+
storyId: story.id,
|
|
136
|
+
sessionRole: role,
|
|
133
137
|
});
|
|
134
138
|
|
|
135
139
|
// BUG-21 Fix: Clean up orphaned child processes if agent failed
|