@nathapp/nax 0.70.0-canary.1 → 0.70.0-canary.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/dist/nax.js +255 -106
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -17028,6 +17028,7 @@ var init_schemas_infra = __esm(() => {
|
|
|
17028
17028
|
testPath: exports_external.string().min(1, "acceptance.testPath must be non-empty"),
|
|
17029
17029
|
command: exports_external.string().optional(),
|
|
17030
17030
|
model: ConfiguredModelSchema.default("fast"),
|
|
17031
|
+
generateModel: ConfiguredModelSchema.optional(),
|
|
17031
17032
|
refinement: exports_external.boolean().default(true),
|
|
17032
17033
|
refinementConcurrency: exports_external.number().int().min(1).max(10).default(3),
|
|
17033
17034
|
redGate: exports_external.boolean().default(true),
|
|
@@ -19231,7 +19232,7 @@ function reshapeSelector(name, fn) {
|
|
|
19231
19232
|
var reviewConfigSelector, planConfigSelector, decomposeConfigSelector, rectifyConfigSelector, acceptanceConfigSelector, acceptanceFixConfigSelector, acceptanceGenConfigSelector, tddConfigSelector, debateConfigSelector, routingConfigSelector, verifyConfigSelector, rectificationGateConfigSelector, agentConfigSelector, agentManagerConfigSelector, interactionConfigSelector, precheckConfigSelector, qualityConfigSelector, autofixConfigSelector, executionGatesConfigSelector, testPatternConfigSelector, contextConfigSelector, contextToolRuntimeConfigSelector, promptLoaderConfigSelector, llmRoutingConfigSelector;
|
|
19232
19233
|
var init_selectors = __esm(() => {
|
|
19233
19234
|
reviewConfigSelector = pickSelector("review", "review", "debate", "models", "execution", "project", "quality", "agent");
|
|
19234
|
-
planConfigSelector = pickSelector("plan", "plan", "debate", "agent", "project");
|
|
19235
|
+
planConfigSelector = pickSelector("plan", "plan", "debate", "agent", "project", "routing");
|
|
19235
19236
|
decomposeConfigSelector = pickSelector("decompose", "plan", "agent", "routing");
|
|
19236
19237
|
rectifyConfigSelector = pickSelector("rectify", "execution");
|
|
19237
19238
|
acceptanceConfigSelector = pickSelector("acceptance", "acceptance");
|
|
@@ -22251,6 +22252,34 @@ function resolveDefaultAgent(config2) {
|
|
|
22251
22252
|
}
|
|
22252
22253
|
var FALLBACK_DEFAULT_AGENT = "claude";
|
|
22253
22254
|
|
|
22255
|
+
// src/agents/shared/agent-profile-resolver.ts
|
|
22256
|
+
function resolveAgentAssignment(selectedProfileId, agentRouting, storyId) {
|
|
22257
|
+
if (agentRouting?.enabled !== true)
|
|
22258
|
+
return null;
|
|
22259
|
+
const profiles = agentRouting.profiles ?? [];
|
|
22260
|
+
if (profiles.length === 0)
|
|
22261
|
+
return null;
|
|
22262
|
+
const defaultProfile = agentRouting.default ? profiles.find((p) => p.id === agentRouting.default) : undefined;
|
|
22263
|
+
if (selectedProfileId) {
|
|
22264
|
+
const profile = profiles.find((p) => p.id === selectedProfileId);
|
|
22265
|
+
if (profile)
|
|
22266
|
+
return toAssignment(profile);
|
|
22267
|
+
getSafeLogger()?.warn("routing", `Story ${storyId} selected unknown agent profile "${selectedProfileId}" \u2014 falling back to ${defaultProfile ? `default profile "${defaultProfile.id}"` : "no profile"}`, { storyId, agentProfileId: selectedProfileId });
|
|
22268
|
+
}
|
|
22269
|
+
return defaultProfile ? toAssignment(defaultProfile) : null;
|
|
22270
|
+
}
|
|
22271
|
+
function toAssignment(p) {
|
|
22272
|
+
return { agent: p.target.agent, agentProfileId: p.id, profileModelTier: p.target.model };
|
|
22273
|
+
}
|
|
22274
|
+
var init_agent_profile_resolver = __esm(() => {
|
|
22275
|
+
init_logger2();
|
|
22276
|
+
});
|
|
22277
|
+
|
|
22278
|
+
// src/agents/shared/index.ts
|
|
22279
|
+
var init_shared = __esm(() => {
|
|
22280
|
+
init_agent_profile_resolver();
|
|
22281
|
+
});
|
|
22282
|
+
|
|
22254
22283
|
// src/agents/retry/types.ts
|
|
22255
22284
|
var ParseValidationError;
|
|
22256
22285
|
var init_types4 = __esm(() => {
|
|
@@ -22497,6 +22526,7 @@ var init_agents = __esm(() => {
|
|
|
22497
22526
|
init_cost();
|
|
22498
22527
|
init_version_detection();
|
|
22499
22528
|
init_manager();
|
|
22529
|
+
init_shared();
|
|
22500
22530
|
init_retry();
|
|
22501
22531
|
});
|
|
22502
22532
|
|
|
@@ -28189,7 +28219,8 @@ function validateStory(raw, index, allIds) {
|
|
|
28189
28219
|
complexity,
|
|
28190
28220
|
testStrategy,
|
|
28191
28221
|
reasoning: typeof routing.reasoning === "string" && routing.reasoning.trim().length > 0 ? routing.reasoning.trim() : "validated from LLM output",
|
|
28192
|
-
...noTestJustification !== undefined ? { noTestJustification } : {}
|
|
28222
|
+
...noTestJustification !== undefined ? { noTestJustification } : {},
|
|
28223
|
+
...typeof routing.agentProfileId === "string" && routing.agentProfileId.trim().length > 0 ? { agentProfileId: routing.agentProfileId.trim() } : {}
|
|
28193
28224
|
},
|
|
28194
28225
|
...workdir !== undefined ? { workdir } : {},
|
|
28195
28226
|
...contextFiles.length > 0 ? { contextFiles } : {},
|
|
@@ -34691,8 +34722,10 @@ var init_plan = __esm(() => {
|
|
|
34691
34722
|
truncated: () => PlanPromptBuilder.jsonRepair(0, "JSON appears truncated \u2014 please rewrite completely")
|
|
34692
34723
|
}
|
|
34693
34724
|
}),
|
|
34694
|
-
build(input,
|
|
34695
|
-
const
|
|
34725
|
+
build(input, ctx) {
|
|
34726
|
+
const agentRouting = ctx.config.routing?.agents;
|
|
34727
|
+
const profiles = agentRouting?.enabled === true ? agentRouting.profiles ?? [] : [];
|
|
34728
|
+
const { taskContext, outputFormat } = new PlanPromptBuilder().build(input.specContent, input.codebaseContext, input.outputPath, input.packages, input.packageDetails, input.projectProfile, undefined, profiles);
|
|
34696
34729
|
return {
|
|
34697
34730
|
role: { id: "role", content: "", overridable: false },
|
|
34698
34731
|
task: { id: "task", content: `${taskContext}
|
|
@@ -34926,8 +34959,10 @@ var init_plan_refine = __esm(() => {
|
|
|
34926
34959
|
}
|
|
34927
34960
|
}),
|
|
34928
34961
|
fileOutput: (input) => input.outputPath,
|
|
34929
|
-
build(input,
|
|
34930
|
-
const
|
|
34962
|
+
build(input, ctx) {
|
|
34963
|
+
const agentRouting = ctx.config.routing?.agents;
|
|
34964
|
+
const profiles = agentRouting?.enabled === true ? agentRouting.profiles ?? [] : [];
|
|
34965
|
+
const { taskContext, outputFormat } = new PlanPromptBuilder().build(input.specContent, input.codebaseContext, input.outputPath, input.packages, input.packageDetails, input.projectProfile, undefined, profiles);
|
|
34931
34966
|
return {
|
|
34932
34967
|
role: { id: "role", content: "", overridable: false },
|
|
34933
34968
|
task: {
|
|
@@ -35203,15 +35238,12 @@ Consider:
|
|
|
35203
35238
|
});
|
|
35204
35239
|
|
|
35205
35240
|
// src/operations/decompose.ts
|
|
35206
|
-
var
|
|
35241
|
+
var decomposeOp;
|
|
35207
35242
|
var init_decompose2 = __esm(() => {
|
|
35208
35243
|
init_decompose();
|
|
35209
35244
|
init_decompose_prompt();
|
|
35210
35245
|
init_config();
|
|
35211
35246
|
init_logger2();
|
|
35212
|
-
_decomposeOpDeps = {
|
|
35213
|
-
getSafeLogger
|
|
35214
|
-
};
|
|
35215
35247
|
decomposeOp = {
|
|
35216
35248
|
kind: "complete",
|
|
35217
35249
|
name: "decompose",
|
|
@@ -35236,46 +35268,8 @@ var init_decompose2 = __esm(() => {
|
|
|
35236
35268
|
task: { id: "task", content: prompt, overridable: false }
|
|
35237
35269
|
};
|
|
35238
35270
|
},
|
|
35239
|
-
parse(output, _input,
|
|
35240
|
-
|
|
35241
|
-
const agentRouting = ctx.config.routing?.agents;
|
|
35242
|
-
if (agentRouting?.enabled !== true) {
|
|
35243
|
-
return stories;
|
|
35244
|
-
}
|
|
35245
|
-
const profiles = agentRouting.profiles ?? [];
|
|
35246
|
-
if (profiles.length === 0) {
|
|
35247
|
-
return stories;
|
|
35248
|
-
}
|
|
35249
|
-
const defaultProfile = agentRouting.default ? profiles.find((p) => p.id === agentRouting.default) : undefined;
|
|
35250
|
-
return stories.map((story) => {
|
|
35251
|
-
if (story.agentProfileId) {
|
|
35252
|
-
const profile = profiles.find((p) => p.id === story.agentProfileId);
|
|
35253
|
-
if (profile) {
|
|
35254
|
-
return {
|
|
35255
|
-
...story,
|
|
35256
|
-
routing: {
|
|
35257
|
-
...story.routing,
|
|
35258
|
-
agent: profile.target.agent,
|
|
35259
|
-
agentProfileId: profile.id,
|
|
35260
|
-
profileModelTier: profile.target.model
|
|
35261
|
-
}
|
|
35262
|
-
};
|
|
35263
|
-
}
|
|
35264
|
-
_decomposeOpDeps.getSafeLogger()?.warn("decompose", `Story ${story.id} selected unknown agent profile "${story.agentProfileId}" \u2014 falling back to ${defaultProfile ? `default profile "${defaultProfile.id}"` : "no profile"}`, { storyId: story.id, agentProfileId: story.agentProfileId });
|
|
35265
|
-
}
|
|
35266
|
-
if (defaultProfile) {
|
|
35267
|
-
return {
|
|
35268
|
-
...story,
|
|
35269
|
-
routing: {
|
|
35270
|
-
...story.routing,
|
|
35271
|
-
agent: defaultProfile.target.agent,
|
|
35272
|
-
agentProfileId: defaultProfile.id,
|
|
35273
|
-
profileModelTier: defaultProfile.target.model
|
|
35274
|
-
}
|
|
35275
|
-
};
|
|
35276
|
-
}
|
|
35277
|
-
return story;
|
|
35278
|
-
});
|
|
35271
|
+
parse(output, _input, _ctx) {
|
|
35272
|
+
return parseDecomposeOutput(output);
|
|
35279
35273
|
}
|
|
35280
35274
|
};
|
|
35281
35275
|
});
|
|
@@ -36140,7 +36134,7 @@ var init_acceptance_generate = __esm(() => {
|
|
|
36140
36134
|
stage: "acceptance",
|
|
36141
36135
|
session: { role: "acceptance-gen", lifetime: "fresh" },
|
|
36142
36136
|
config: acceptanceGenConfigSelector,
|
|
36143
|
-
model: (_input, ctx) => ctx.config.acceptance.model,
|
|
36137
|
+
model: (_input, ctx) => ctx.config.acceptance.generateModel ?? ctx.config.acceptance.model,
|
|
36144
36138
|
timeoutMs: (_input, ctx) => ctx.config.execution.sessionTimeoutSeconds * 1000,
|
|
36145
36139
|
build(input, _ctx) {
|
|
36146
36140
|
const prompt = new AcceptancePromptBuilder().buildGeneratorFromPRDPrompt({
|
|
@@ -36244,6 +36238,7 @@ var init_refinement = () => {};
|
|
|
36244
36238
|
var acceptanceRefineOp;
|
|
36245
36239
|
var init_acceptance_refine = __esm(() => {
|
|
36246
36240
|
init_refinement();
|
|
36241
|
+
init_retry();
|
|
36247
36242
|
init_config();
|
|
36248
36243
|
init_logger2();
|
|
36249
36244
|
init_prompts();
|
|
@@ -36253,7 +36248,8 @@ var init_acceptance_refine = __esm(() => {
|
|
|
36253
36248
|
stage: "acceptance",
|
|
36254
36249
|
jsonMode: true,
|
|
36255
36250
|
config: acceptanceConfigSelector,
|
|
36256
|
-
|
|
36251
|
+
retry: { preset: "transient-network", maxAttempts: 2, baseDelayMs: 0 },
|
|
36252
|
+
model: (_input, ctx) => ctx.config.acceptance.generateModel ?? ctx.config.acceptance.model,
|
|
36257
36253
|
timeoutMs: (_input, ctx) => ctx.config.acceptance.timeoutMs,
|
|
36258
36254
|
build(input, _ctx) {
|
|
36259
36255
|
const prompt = new AcceptancePromptBuilder().buildRefinementPrompt(input.criteria, input.codebaseContext, {
|
|
@@ -36268,8 +36264,11 @@ var init_acceptance_refine = __esm(() => {
|
|
|
36268
36264
|
};
|
|
36269
36265
|
},
|
|
36270
36266
|
parse(output, input, _ctx) {
|
|
36267
|
+
if (!output || !output.trim()) {
|
|
36268
|
+
throw new ParseValidationError("acceptance-refine: empty output");
|
|
36269
|
+
}
|
|
36271
36270
|
if (refinementWouldFallback(output)) {
|
|
36272
|
-
getSafeLogger()?.warn("acceptance", "AC refinement returned no usable JSON \u2014 falling back to unrefined criteria", { storyId: input.storyId, criteriaCount: input.criteria.length, responseBytes: output
|
|
36271
|
+
getSafeLogger()?.warn("acceptance", "AC refinement returned no usable JSON \u2014 falling back to unrefined criteria", { storyId: input.storyId, criteriaCount: input.criteria.length, responseBytes: output.length });
|
|
36273
36272
|
}
|
|
36274
36273
|
const items = parseRefinementResponse(output, input.criteria);
|
|
36275
36274
|
return items.map((item) => ({ ...item, storyId: item.storyId || input.storyId }));
|
|
@@ -38583,8 +38582,10 @@ var init_plan_draft = __esm(() => {
|
|
|
38583
38582
|
model: (_input, ctx) => ctx.config.plan?.model ?? "fast",
|
|
38584
38583
|
timeoutMs: (_input, ctx) => (ctx.config.plan?.timeoutSeconds ?? 600) * 1000,
|
|
38585
38584
|
retry: (input) => createDraftRetryStrategy(input.citationThreshold),
|
|
38586
|
-
build(input,
|
|
38587
|
-
|
|
38585
|
+
build(input, ctx) {
|
|
38586
|
+
const agentRouting = ctx.config.routing?.agents;
|
|
38587
|
+
const profiles = agentRouting?.enabled === true ? agentRouting.profiles ?? [] : [];
|
|
38588
|
+
return new PlanPromptBuilder().buildDraft({ ...input, profiles });
|
|
38588
38589
|
},
|
|
38589
38590
|
parse(output, input, _ctx) {
|
|
38590
38591
|
return parsePlanDraft(output, input);
|
|
@@ -39088,11 +39089,58 @@ var init_full_suite_gate = __esm(() => {
|
|
|
39088
39089
|
};
|
|
39089
39090
|
});
|
|
39090
39091
|
|
|
39092
|
+
// src/operations/full-suite-rectify-op.ts
|
|
39093
|
+
var fullSuiteRectifyOp;
|
|
39094
|
+
var init_full_suite_rectify_op = __esm(() => {
|
|
39095
|
+
init_config();
|
|
39096
|
+
init_prompts();
|
|
39097
|
+
init_test_edit_declaration();
|
|
39098
|
+
fullSuiteRectifyOp = {
|
|
39099
|
+
kind: "run",
|
|
39100
|
+
name: "full-suite-rectify",
|
|
39101
|
+
stage: "rectification",
|
|
39102
|
+
session: { role: "implementer", lifetime: "warm" },
|
|
39103
|
+
config: autofixConfigSelector,
|
|
39104
|
+
build(input, _ctx) {
|
|
39105
|
+
const prompt = RectifierPromptBuilder.failingTestRectification(input.findings, input.story);
|
|
39106
|
+
return {
|
|
39107
|
+
role: { id: "role", content: "", overridable: false },
|
|
39108
|
+
task: { id: "task", content: prompt, overridable: false }
|
|
39109
|
+
};
|
|
39110
|
+
},
|
|
39111
|
+
parse(output, _input, _ctx) {
|
|
39112
|
+
const declarations = parseTestEditDeclarations(output);
|
|
39113
|
+
return { applied: true, testEditDeclarations: declarations };
|
|
39114
|
+
}
|
|
39115
|
+
};
|
|
39116
|
+
});
|
|
39117
|
+
|
|
39091
39118
|
// src/operations/full-suite-rectify.ts
|
|
39092
|
-
function makeFullSuiteRectifyStrategy(story, config2) {
|
|
39119
|
+
function makeFullSuiteRectifyStrategy(story, config2, sink) {
|
|
39120
|
+
const appliesTo = (finding) => finding.source === "test-runner" && (finding.category === "failed-test" || finding.category === "execution-failed");
|
|
39121
|
+
if (sink) {
|
|
39122
|
+
return {
|
|
39123
|
+
name: "full-suite-rectify",
|
|
39124
|
+
appliesTo,
|
|
39125
|
+
fixOp: fullSuiteRectifyOp,
|
|
39126
|
+
buildInput: (findings) => ({ story, findings }),
|
|
39127
|
+
extractApplied: (output) => {
|
|
39128
|
+
for (const d of output.testEditDeclarations) {
|
|
39129
|
+
if (d.reason === "mock_structure" && d.files && d.files.length > 0) {
|
|
39130
|
+
sink.mockHandoffs.push({ files: d.files, reasonDetail: d.reasonDetail ?? "" });
|
|
39131
|
+
} else if (d.reason !== "mock_structure") {
|
|
39132
|
+
sink.testEdits.push(d);
|
|
39133
|
+
}
|
|
39134
|
+
}
|
|
39135
|
+
return { targetFiles: [], summary: "Fixed failing tests" };
|
|
39136
|
+
},
|
|
39137
|
+
maxAttempts: config2.execution.rectification.maxAttemptsPerStrategy,
|
|
39138
|
+
coRun: "exclusive"
|
|
39139
|
+
};
|
|
39140
|
+
}
|
|
39093
39141
|
return {
|
|
39094
39142
|
name: "full-suite-rectify",
|
|
39095
|
-
appliesTo
|
|
39143
|
+
appliesTo,
|
|
39096
39144
|
fixOp: implementerOp,
|
|
39097
39145
|
buildInput: (findings) => ({
|
|
39098
39146
|
story,
|
|
@@ -39105,6 +39153,7 @@ function makeFullSuiteRectifyStrategy(story, config2) {
|
|
|
39105
39153
|
}
|
|
39106
39154
|
var init_full_suite_rectify = __esm(() => {
|
|
39107
39155
|
init_prompts();
|
|
39156
|
+
init_full_suite_rectify_op();
|
|
39108
39157
|
init_implement();
|
|
39109
39158
|
});
|
|
39110
39159
|
|
|
@@ -39232,7 +39281,8 @@ function applyTestEditDeclarations(findings, declarations, story, invalidMockStr
|
|
|
39232
39281
|
const valid = validatePrdQuote(prdQuote, story);
|
|
39233
39282
|
if (valid) {
|
|
39234
39283
|
result = result.map((f) => {
|
|
39235
|
-
|
|
39284
|
+
const eligible = f.file === d.file && (f.fixTarget === "source" || f.fixTarget == null && f.source === "test-runner");
|
|
39285
|
+
if (eligible) {
|
|
39236
39286
|
return {
|
|
39237
39287
|
...f,
|
|
39238
39288
|
fixTarget: "test",
|
|
@@ -40141,6 +40191,7 @@ var init_operations = __esm(() => {
|
|
|
40141
40191
|
init_greenfield_gate();
|
|
40142
40192
|
init_full_suite_gate();
|
|
40143
40193
|
init_full_suite_rectify();
|
|
40194
|
+
init_full_suite_rectify_op();
|
|
40144
40195
|
init_autofix_implementer_strategy();
|
|
40145
40196
|
init_autofix_test_writer_strategy();
|
|
40146
40197
|
init_apply_test_edit_declarations();
|
|
@@ -42247,6 +42298,23 @@ ${adversarialErrors}
|
|
|
42247
42298
|
Do NOT add new features \u2014 only fix valid issues.
|
|
42248
42299
|
Commit your fixes when done.${scopeConstraint}${noTestIsolationBlock(story)}${escapeHatchFor(story)}`;
|
|
42249
42300
|
}
|
|
42301
|
+
function formatFailingTestsList(findings) {
|
|
42302
|
+
if (findings.length === 0) {
|
|
42303
|
+
return "The full test suite has failing tests. Fix the implementation to make all tests pass.";
|
|
42304
|
+
}
|
|
42305
|
+
const lines = [`Fix the following ${findings.length} failing test${findings.length === 1 ? "" : "s"}:
|
|
42306
|
+
`];
|
|
42307
|
+
for (const f of findings) {
|
|
42308
|
+
const location = f.file ? `${f.file}` : "(unknown file)";
|
|
42309
|
+
const rule = f.rule ? ` Test: ${f.rule}
|
|
42310
|
+
` : "";
|
|
42311
|
+
lines.push(`- ${location}
|
|
42312
|
+
${rule} Error: ${f.message}
|
|
42313
|
+
`);
|
|
42314
|
+
}
|
|
42315
|
+
return lines.join(`
|
|
42316
|
+
`);
|
|
42317
|
+
}
|
|
42250
42318
|
function mechanicalRectification(checks3, story, scopeConstraint, opts) {
|
|
42251
42319
|
const errors3 = formatCheckErrors(checks3, opts);
|
|
42252
42320
|
const scopeDirective = implementerOwnsTests(story) ? `Fix all errors listed above that are within this story's scope. ${SINGLE_SESSION_PERMIT_HEADLINE}` : `Fix all errors listed above that are within this story's scope \u2014 see the ${exceptionCountWord(story)} narrow exceptions appended below for sibling-story spillover. Do NOT change test files or test behavior except via those exceptions.`;
|
|
@@ -42931,22 +42999,23 @@ Before editing, break the work into one small fix per finding above (fix -> re-r
|
|
|
42931
42999
|
`);
|
|
42932
43000
|
}
|
|
42933
43001
|
static failingTestContext(findings) {
|
|
42934
|
-
|
|
42935
|
-
|
|
42936
|
-
|
|
42937
|
-
|
|
42938
|
-
|
|
42939
|
-
|
|
42940
|
-
|
|
42941
|
-
|
|
42942
|
-
|
|
42943
|
-
|
|
42944
|
-
|
|
42945
|
-
`
|
|
42946
|
-
|
|
42947
|
-
|
|
42948
|
-
|
|
42949
|
-
|
|
43002
|
+
const listing = formatFailingTestsList(findings);
|
|
43003
|
+
if (findings.length === 0)
|
|
43004
|
+
return listing;
|
|
43005
|
+
return `${listing}
|
|
43006
|
+
Fix the implementation (not the tests) to make all failing tests pass. Run the test suite to verify after each change.`;
|
|
43007
|
+
}
|
|
43008
|
+
static failingTestRectification(findings, story) {
|
|
43009
|
+
const listing = formatFailingTestsList(findings);
|
|
43010
|
+
const exCount = exceptionCountWord(story);
|
|
43011
|
+
const prohibition = `Do NOT change test files or test behavior \u2014 see the ${exCount} narrow exceptions appended below.`;
|
|
43012
|
+
const parts = [listing];
|
|
43013
|
+
parts.push(`
|
|
43014
|
+
Fix the implementation (not the tests) to make all failing tests pass. Do not loosen assertions or weaken test expectations. Run the test suite to verify after each change.`);
|
|
43015
|
+
parts.push(`
|
|
43016
|
+
${testEditHeadline(story, prohibition)}`);
|
|
43017
|
+
parts.push(escapeHatchFor(story));
|
|
43018
|
+
return parts.join(`
|
|
42950
43019
|
`);
|
|
42951
43020
|
}
|
|
42952
43021
|
}
|
|
@@ -43218,7 +43287,13 @@ For each one:
|
|
|
43218
43287
|
Write the corrected PRD to this file path: ${outputFilePath}
|
|
43219
43288
|
Do not output the PRD in chat. After writing the file, reply with a brief text confirmation only.`;
|
|
43220
43289
|
}
|
|
43221
|
-
build(specContent, codebaseContext, outputFilePath, packages, packageDetails, projectProfile, proposers) {
|
|
43290
|
+
build(specContent, codebaseContext, outputFilePath, packages, packageDetails, projectProfile, proposers, profiles) {
|
|
43291
|
+
const cards = OneShotPromptBuilder.agentCapabilityCards(profiles ?? []);
|
|
43292
|
+
const agentProfilesSection = cards.length > 0 ? `
|
|
43293
|
+
|
|
43294
|
+
${cards}
|
|
43295
|
+
|
|
43296
|
+
${OneShotPromptBuilder.agentProfileInstruction()}` : "";
|
|
43222
43297
|
const isMonorepo = packages && packages.length > 0;
|
|
43223
43298
|
const packageDetailsSection = packageDetails && packageDetails.length > 0 ? buildPackageDetailsSection(packageDetails) : "";
|
|
43224
43299
|
const monorepoHint = isMonorepo ? `
|
|
@@ -43273,7 +43348,7 @@ ${buildSharedQualityRules(specContent, projectProfile)}
|
|
|
43273
43348
|
|
|
43274
43349
|
For each story, set "contextFiles" to the key source files the agent should read before implementing (max 5 per story). Use your Step 2 analysis to identify the most relevant files. Leave empty for greenfield stories with no existing files to reference. Set "expectedFiles" to the NEW files the story creates.
|
|
43275
43350
|
|
|
43276
|
-
${CONTEXT_VS_EXPECTED_FILES_RULE}`;
|
|
43351
|
+
${CONTEXT_VS_EXPECTED_FILES_RULE}${agentProfilesSection}`;
|
|
43277
43352
|
const suggestedCriteriaField = specContent.trim() ? `
|
|
43278
43353
|
"suggestedCriteria": ["string \u2014 optional. Behavioral edge cases or negative paths you identified that are NOT in the spec. Plain assertions only \u2014 observable outputs, return values, state changes, or error conditions. No implementation details or vague descriptions. Omit this field if empty."],` : "";
|
|
43279
43354
|
const outputDirective = outputFilePath ? `Write the PRD JSON directly to this file path: ${outputFilePath}
|
|
@@ -43305,7 +43380,8 @@ Generate a JSON object with this exact structure (no markdown, no explanation \u
|
|
|
43305
43380
|
"complexity": "simple | medium | complex | expert",
|
|
43306
43381
|
"testStrategy": "no-test | tdd-simple | three-session-tdd-lite | three-session-tdd | test-after",
|
|
43307
43382
|
"noTestJustification": "string \u2014 REQUIRED when testStrategy is no-test, explains why tests are unnecessary",
|
|
43308
|
-
"reasoning": "string \u2014 brief classification rationale"
|
|
43383
|
+
"reasoning": "string \u2014 brief classification rationale"${cards.length > 0 ? `,
|
|
43384
|
+
"agentProfileId": "string \u2014 optional, the id of the best-matching profile from the Agent Profiles table above; omit if none fits"` : ""}
|
|
43309
43385
|
},
|
|
43310
43386
|
"escalations": [],
|
|
43311
43387
|
"attempts": 0
|
|
@@ -43322,6 +43398,12 @@ ${outputDirective}`;
|
|
|
43322
43398
|
content: "You are a senior software architect generating a product requirements document (PRD) as JSON. Your intent is to produce a thorough, evidence-grounded plan.",
|
|
43323
43399
|
overridable: false
|
|
43324
43400
|
};
|
|
43401
|
+
const cards = OneShotPromptBuilder.agentCapabilityCards(input.profiles ?? []);
|
|
43402
|
+
const agentProfilesSection = cards.length > 0 ? `
|
|
43403
|
+
|
|
43404
|
+
${cards}
|
|
43405
|
+
|
|
43406
|
+
${OneShotPromptBuilder.agentProfileInstruction()}` : "";
|
|
43325
43407
|
const revisionSection = input.revisionFindings && input.revisionFindings.length > 0 ? `
|
|
43326
43408
|
|
|
43327
43409
|
## Previous draft rejected for the following issues
|
|
@@ -43371,7 +43453,7 @@ ${buildSharedQualityRules(input.specContent, input.projectProfile)}
|
|
|
43371
43453
|
|
|
43372
43454
|
For each story, set "contextFiles" to the key source files the implementer should read before starting (max 5 per story). Cite manifest factIds where relevant. Set "expectedFiles" to the NEW files the story creates.
|
|
43373
43455
|
|
|
43374
|
-
${CONTEXT_VS_EXPECTED_FILES_RULE}
|
|
43456
|
+
${CONTEXT_VS_EXPECTED_FILES_RULE}${agentProfilesSection}
|
|
43375
43457
|
|
|
43376
43458
|
## Output Schema
|
|
43377
43459
|
|
|
@@ -43394,7 +43476,8 @@ Produce a JSON object with this exact structure. Field names are mandatory \u201
|
|
|
43394
43476
|
"routing": {
|
|
43395
43477
|
"complexity": "simple | medium | complex | expert",
|
|
43396
43478
|
"testStrategy": "no-test | tdd-simple | three-session-tdd-lite | three-session-tdd | test-after",
|
|
43397
|
-
"reasoning": "string \u2014 brief classification rationale"
|
|
43479
|
+
"reasoning": "string \u2014 brief classification rationale"${cards.length > 0 ? `,
|
|
43480
|
+
"agentProfileId": "string \u2014 optional, the id of the best-matching profile from the Agent Profiles table above; omit if none fits"` : ""}
|
|
43398
43481
|
}
|
|
43399
43482
|
}
|
|
43400
43483
|
]
|
|
@@ -43434,6 +43517,7 @@ var CONTEXT_VS_EXPECTED_FILES_RULE = `**\`contextFiles\` rule \u2014 files reada
|
|
|
43434
43517
|
**\`expectedFiles\` rule \u2014 files THIS story CREATES.** List every NEW file this story authors (relative paths). A file this story creates belongs here, NEVER in \`contextFiles\` \u2014 these are the story's outputs, not files to read first. A file created by an upstream dependency and only read/modified here belongs in \`contextFiles\`, NOT here (this story does not author it). A single path may appear in \`contextFiles\` (an existing sibling to mirror) AND \`expectedFiles\` (the new file itself), but the same path must never be in both.`, EXPECTED_FILES_SCHEMA_FIELD = `"expectedFiles": ["string \u2014 NEW files this story creates (relative paths, omit if none)"],`;
|
|
43435
43518
|
var init_plan_builder = __esm(() => {
|
|
43436
43519
|
init_config();
|
|
43520
|
+
init_one_shot_builder();
|
|
43437
43521
|
});
|
|
43438
43522
|
|
|
43439
43523
|
// src/prompts/builders/grounder-builder.ts
|
|
@@ -51771,7 +51855,7 @@ var init_precheck = __esm(() => {
|
|
|
51771
51855
|
});
|
|
51772
51856
|
|
|
51773
51857
|
// src/prd/decompose-mapper.ts
|
|
51774
|
-
function mapDecomposedStoriesToUserStories(stories, parentStoryId, parentWorkdir) {
|
|
51858
|
+
function mapDecomposedStoriesToUserStories(stories, parentStoryId, parentWorkdir, parentRouting) {
|
|
51775
51859
|
return stories.map((story, entryIndex) => {
|
|
51776
51860
|
if (!story.id) {
|
|
51777
51861
|
throw new NaxError(`Entry at index ${entryIndex} is missing required field: id`, "DECOMPOSE_VALIDATION_FAILED", {
|
|
@@ -51805,10 +51889,19 @@ function mapDecomposedStoriesToUserStories(stories, parentStoryId, parentWorkdir
|
|
|
51805
51889
|
complexity: story.complexity,
|
|
51806
51890
|
testStrategy: story.testStrategy ?? "test-after",
|
|
51807
51891
|
reasoning: story.reasoning,
|
|
51808
|
-
modelTier: story.routing?.profileModelTier ?? "balanced",
|
|
51892
|
+
modelTier: parentRouting?.profileModelTier ?? story.routing?.profileModelTier ?? "balanced",
|
|
51809
51893
|
...story.routing?.agent !== undefined && { agent: story.routing.agent },
|
|
51810
51894
|
...story.routing?.agentProfileId !== undefined && { agentProfileId: story.routing.agentProfileId },
|
|
51811
|
-
...story.routing?.profileModelTier !== undefined && { profileModelTier: story.routing.profileModelTier }
|
|
51895
|
+
...story.routing?.profileModelTier !== undefined && { profileModelTier: story.routing.profileModelTier },
|
|
51896
|
+
...parentRouting?.agent !== undefined && { agent: parentRouting.agent },
|
|
51897
|
+
...parentRouting?.agentProfileId !== undefined && { agentProfileId: parentRouting.agentProfileId },
|
|
51898
|
+
...parentRouting?.profileModelTier !== undefined && { profileModelTier: parentRouting.profileModelTier },
|
|
51899
|
+
...parentRouting?.agent !== undefined && {
|
|
51900
|
+
initialAgent: parentRouting.initialAgent ?? parentRouting.agent
|
|
51901
|
+
},
|
|
51902
|
+
...parentRouting?.agentProfileId !== undefined && {
|
|
51903
|
+
initialProfileId: parentRouting.initialProfileId ?? parentRouting.agentProfileId
|
|
51904
|
+
}
|
|
51812
51905
|
}
|
|
51813
51906
|
};
|
|
51814
51907
|
});
|
|
@@ -51874,8 +51967,7 @@ async function planDecomposeCommand(workdir, config2, options) {
|
|
|
51874
51967
|
for (let attempt = 0;attempt < maxReplanAttempts; attempt++) {
|
|
51875
51968
|
if (attempt === 0 && debateDecompEnabled) {
|
|
51876
51969
|
const decomposeStageConfig = debateStages.decompose;
|
|
51877
|
-
const
|
|
51878
|
-
const profilesForDebate = agentRoutingForDebate?.enabled === true ? agentRoutingForDebate.profiles ?? [] : [];
|
|
51970
|
+
const profilesForDebate = [];
|
|
51879
51971
|
const prompt = await buildDecomposePromptAsync({
|
|
51880
51972
|
specContent: "",
|
|
51881
51973
|
codebaseContext,
|
|
@@ -51950,7 +52042,7 @@ ${repairHint}` : codebaseContext;
|
|
|
51950
52042
|
} finally {
|
|
51951
52043
|
await rt.close().catch(() => {});
|
|
51952
52044
|
}
|
|
51953
|
-
const subStoriesWithParent = mapDecomposedStoriesToUserStories(decompStories, options.storyId, targetStory.workdir);
|
|
52045
|
+
const subStoriesWithParent = mapDecomposedStoriesToUserStories(decompStories, options.storyId, targetStory.workdir, targetStory.routing);
|
|
51954
52046
|
const updatedStories = prd.userStories.map((s) => s.id === options.storyId ? { ...s, status: "decomposed" } : s);
|
|
51955
52047
|
const originalIndex = updatedStories.findIndex((s) => s.id === options.storyId);
|
|
51956
52048
|
const finalStories = [
|
|
@@ -52912,15 +53004,28 @@ async function processPackageGroup(ctx, packageDir, groupStories, language, resu
|
|
|
52912
53004
|
featureName: ctx.prd.feature,
|
|
52913
53005
|
agentName: ctx.agentManager.getDefault()
|
|
52914
53006
|
};
|
|
52915
|
-
|
|
52916
|
-
|
|
52917
|
-
|
|
52918
|
-
|
|
52919
|
-
|
|
52920
|
-
|
|
52921
|
-
|
|
52922
|
-
|
|
52923
|
-
|
|
53007
|
+
let refined;
|
|
53008
|
+
try {
|
|
53009
|
+
refined = await _hardeningDeps.callOp(callCtx, acceptanceRefineOp, {
|
|
53010
|
+
criteria: story.suggestedCriteria ?? [],
|
|
53011
|
+
codebaseContext: "",
|
|
53012
|
+
storyId: story.id,
|
|
53013
|
+
testStrategy: ctx.config.acceptance?.testStrategy,
|
|
53014
|
+
testFramework: ctx.config.acceptance?.testFramework,
|
|
53015
|
+
storyTitle: story.title,
|
|
53016
|
+
storyDescription: story.description
|
|
53017
|
+
});
|
|
53018
|
+
} catch {
|
|
53019
|
+
logger?.warn("acceptance", "AC refinement failed after retries \u2014 using unrefined criteria", {
|
|
53020
|
+
storyId: story.id
|
|
53021
|
+
});
|
|
53022
|
+
refined = (story.suggestedCriteria ?? []).map((c) => ({
|
|
53023
|
+
original: c,
|
|
53024
|
+
refined: c,
|
|
53025
|
+
testable: true,
|
|
53026
|
+
storyId: story.id
|
|
53027
|
+
}));
|
|
53028
|
+
}
|
|
52924
53029
|
groupRefined.push(...refined);
|
|
52925
53030
|
}
|
|
52926
53031
|
const suggestedTestPath = resolveSuggestedPackageFeatureTestPath(packageDir, ctx.prd.feature, ctx.config.acceptance?.suggestedTestPath, language);
|
|
@@ -53066,6 +53171,7 @@ __export(exports_acceptance, {
|
|
|
53066
53171
|
resolveSuggestedTestFile: () => resolveSuggestedTestFile,
|
|
53067
53172
|
resolveSuggestedPackageFeatureTestPath: () => resolveSuggestedPackageFeatureTestPath,
|
|
53068
53173
|
resolveAcceptanceFeatureTestPath: () => resolveAcceptanceFeatureTestPath,
|
|
53174
|
+
refinementWouldFallback: () => refinementWouldFallback,
|
|
53069
53175
|
parseRefinementResponse: () => parseRefinementResponse,
|
|
53070
53176
|
parseAcceptanceCriteria: () => parseAcceptanceCriteria,
|
|
53071
53177
|
parseACTextFromSpec: () => parseACTextFromSpec,
|
|
@@ -53480,6 +53586,16 @@ ${stderr}` };
|
|
|
53480
53586
|
storyDescription: story.description
|
|
53481
53587
|
}, story.id).then((refined) => {
|
|
53482
53588
|
results[i] = refined;
|
|
53589
|
+
}).catch(() => {
|
|
53590
|
+
getSafeLogger()?.warn("acceptance-setup", "AC refinement failed after retries \u2014 using unrefined criteria", {
|
|
53591
|
+
storyId: story.id
|
|
53592
|
+
});
|
|
53593
|
+
results[i] = story.acceptanceCriteria.map((c) => ({
|
|
53594
|
+
original: c,
|
|
53595
|
+
refined: c,
|
|
53596
|
+
testable: true,
|
|
53597
|
+
storyId: story.id
|
|
53598
|
+
}));
|
|
53483
53599
|
}).finally(() => {
|
|
53484
53600
|
executing.delete(task);
|
|
53485
53601
|
});
|
|
@@ -55450,7 +55566,7 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
55450
55566
|
strategies.push(makeMechanicalFormatFixStrategy());
|
|
55451
55567
|
}
|
|
55452
55568
|
if (inputs.fullSuiteGate && (isThreeSession || regressionMode === "per-story")) {
|
|
55453
|
-
strategies.push(makeFullSuiteRectifyStrategy(story, config2));
|
|
55569
|
+
strategies.push(makeFullSuiteRectifyStrategy(story, config2, sink));
|
|
55454
55570
|
}
|
|
55455
55571
|
if (config2.quality.autofix?.enabled !== false) {
|
|
55456
55572
|
strategies.push(makeAutofixImplementerStrategy(story, config2, sink, {
|
|
@@ -55495,7 +55611,7 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
55495
55611
|
includeAdversarialReview: false
|
|
55496
55612
|
}), makeAutofixTestWriterStrategy(story, config2, nbSink));
|
|
55497
55613
|
}
|
|
55498
|
-
nbStrategies.push(makeFullSuiteRectifyStrategy(story, config2));
|
|
55614
|
+
nbStrategies.push(makeFullSuiteRectifyStrategy(story, config2, nbSink));
|
|
55499
55615
|
builder.addNonBlockingFix(nbf, nbStrategies);
|
|
55500
55616
|
}
|
|
55501
55617
|
return builder.build(ctx, { isThreeSession });
|
|
@@ -60095,7 +60211,7 @@ var package_default;
|
|
|
60095
60211
|
var init_package = __esm(() => {
|
|
60096
60212
|
package_default = {
|
|
60097
60213
|
name: "@nathapp/nax",
|
|
60098
|
-
version: "0.70.0-canary.
|
|
60214
|
+
version: "0.70.0-canary.2",
|
|
60099
60215
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
60100
60216
|
type: "module",
|
|
60101
60217
|
bin: {
|
|
@@ -60190,8 +60306,8 @@ var init_version = __esm(() => {
|
|
|
60190
60306
|
NAX_VERSION = package_default.version;
|
|
60191
60307
|
NAX_COMMIT = (() => {
|
|
60192
60308
|
try {
|
|
60193
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
60194
|
-
return "
|
|
60309
|
+
if (/^[0-9a-f]{6,10}$/.test("b070f4c1"))
|
|
60310
|
+
return "b070f4c1";
|
|
60195
60311
|
} catch {}
|
|
60196
60312
|
try {
|
|
60197
60313
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -95450,6 +95566,7 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
|
95450
95566
|
const branchName = options.branch ?? `feat/${options.feature}`;
|
|
95451
95567
|
const timeoutSeconds = fullConfig?.plan?.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS2;
|
|
95452
95568
|
const config2 = planConfigSelector.select(fullConfig);
|
|
95569
|
+
const profileName = typeof fullConfig?.profile === "string" && fullConfig.profile ? fullConfig.profile : undefined;
|
|
95453
95570
|
const runtime = createPlanRuntime(fullConfig, workdir, options.feature);
|
|
95454
95571
|
const interactionChain = fullConfig ? await deps.initInteractionChain(fullConfig, !process.stdin.isTTY) : null;
|
|
95455
95572
|
let configuredBridge;
|
|
@@ -95477,6 +95594,7 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
|
95477
95594
|
branchName,
|
|
95478
95595
|
timeoutSeconds,
|
|
95479
95596
|
config: config2,
|
|
95597
|
+
profileName,
|
|
95480
95598
|
options,
|
|
95481
95599
|
runtime,
|
|
95482
95600
|
interactionChain,
|
|
@@ -95525,6 +95643,28 @@ function buildPlanComposition(userStageConfig) {
|
|
|
95525
95643
|
// src/plan/strategies/write-prd.ts
|
|
95526
95644
|
init_errors();
|
|
95527
95645
|
init_prd();
|
|
95646
|
+
|
|
95647
|
+
// src/plan/strategies/finalize-routing.ts
|
|
95648
|
+
init_agents();
|
|
95649
|
+
function finalizePrdRouting(prd, agentRouting, profileName) {
|
|
95650
|
+
const userStories = prd.userStories.map((story) => {
|
|
95651
|
+
const assignment = resolveAgentAssignment(story.routing?.agentProfileId, agentRouting, story.id);
|
|
95652
|
+
if (!assignment)
|
|
95653
|
+
return story;
|
|
95654
|
+
const routing = {
|
|
95655
|
+
...story.routing,
|
|
95656
|
+
agent: assignment.agent,
|
|
95657
|
+
agentProfileId: assignment.agentProfileId,
|
|
95658
|
+
profileModelTier: assignment.profileModelTier,
|
|
95659
|
+
initialAgent: story.routing?.initialAgent ?? assignment.agent,
|
|
95660
|
+
initialProfileId: story.routing?.initialProfileId ?? assignment.agentProfileId
|
|
95661
|
+
};
|
|
95662
|
+
return { ...story, routing };
|
|
95663
|
+
});
|
|
95664
|
+
return { ...prd, userStories, routingProfile: profileName ?? "default" };
|
|
95665
|
+
}
|
|
95666
|
+
|
|
95667
|
+
// src/plan/strategies/write-prd.ts
|
|
95528
95668
|
async function writeOrRecoverPrd(ctx, prd, err) {
|
|
95529
95669
|
const tryExtractPrd = (value) => {
|
|
95530
95670
|
if (value === null || typeof value !== "object")
|
|
@@ -95543,12 +95683,14 @@ async function writeOrRecoverPrd(ctx, prd, err) {
|
|
|
95543
95683
|
};
|
|
95544
95684
|
if (prd !== null) {
|
|
95545
95685
|
if (Array.isArray(prd.userStories)) {
|
|
95546
|
-
|
|
95686
|
+
const finalized = finalizePrdRouting({ ...prd, project: ctx.projectName }, ctx.config.routing?.agents, ctx.profileName);
|
|
95687
|
+
await ctx.deps.writeFile(ctx.outputPath, JSON.stringify(finalized, null, 2));
|
|
95547
95688
|
return ctx.outputPath;
|
|
95548
95689
|
}
|
|
95549
95690
|
const normalizedPrd = tryExtractPrd(prd);
|
|
95550
95691
|
if (normalizedPrd !== null) {
|
|
95551
|
-
|
|
95692
|
+
const finalized = finalizePrdRouting({ ...normalizedPrd, project: ctx.projectName }, ctx.config.routing?.agents, ctx.profileName);
|
|
95693
|
+
await ctx.deps.writeFile(ctx.outputPath, JSON.stringify(finalized, null, 2));
|
|
95552
95694
|
return ctx.outputPath;
|
|
95553
95695
|
}
|
|
95554
95696
|
}
|
|
@@ -95568,7 +95710,8 @@ async function writeOrRecoverPrd(ctx, prd, err) {
|
|
|
95568
95710
|
}
|
|
95569
95711
|
}
|
|
95570
95712
|
recoveredPrd = recoveredPrd ?? validatePlanOutput(rawContent, ctx.options.feature, ctx.branchName);
|
|
95571
|
-
|
|
95713
|
+
const finalized = finalizePrdRouting({ ...recoveredPrd, project: ctx.projectName }, ctx.config.routing?.agents, ctx.profileName);
|
|
95714
|
+
await ctx.deps.writeFile(ctx.outputPath, JSON.stringify(finalized, null, 2));
|
|
95572
95715
|
return ctx.outputPath;
|
|
95573
95716
|
} catch {
|
|
95574
95717
|
throw err;
|
|
@@ -95587,7 +95730,9 @@ var _debatePlanDeps = {
|
|
|
95587
95730
|
class DebatePlanStrategy {
|
|
95588
95731
|
mode = "debate";
|
|
95589
95732
|
async execute(ctx) {
|
|
95590
|
-
const
|
|
95733
|
+
const agentRouting = ctx.config.routing?.agents;
|
|
95734
|
+
const profiles = agentRouting?.enabled === true ? agentRouting.profiles ?? [] : [];
|
|
95735
|
+
const { taskContext, outputFormat } = new PlanPromptBuilder().build(ctx.specContent, ctx.codebaseContext, undefined, ctx.relativePackages, ctx.packageDetails, ctx.config.project, undefined, profiles);
|
|
95591
95736
|
const planStage = ctx.config.debate?.stages?.plan;
|
|
95592
95737
|
if (!planStage) {
|
|
95593
95738
|
throw new NaxError("[plan] debate strategy requires config.debate.stages.plan", "PLAN_DEBATE_STAGE_CONFIG_MISSING", {
|
|
@@ -95723,7 +95868,8 @@ class PipelinePlanStrategy {
|
|
|
95723
95868
|
if (verdict.outcome !== "passed") {
|
|
95724
95869
|
throw new NaxError(verdict.specDeltasPath ? `Plan pipeline failed; see ${verdict.specDeltasPath}` : "Plan pipeline failed with no spec-deltas path", "PLAN_CRITIC_BLOCKED", { stage: "plan", specDeltasPath: verdict.specDeltasPath });
|
|
95725
95870
|
}
|
|
95726
|
-
|
|
95871
|
+
const prdToWrite = finalizePrdRouting({ ...verdict.prd, project: ctx.projectName }, ctx.config.routing?.agents, ctx.profileName);
|
|
95872
|
+
await ctx.deps.writeFile(ctx.outputPath, JSON.stringify(prdToWrite, null, 2));
|
|
95727
95873
|
return ctx.outputPath;
|
|
95728
95874
|
} finally {
|
|
95729
95875
|
await ctx.runtime.close().catch(() => {});
|
|
@@ -95804,13 +95950,15 @@ class SinglePlanStrategy {
|
|
|
95804
95950
|
projectProfile: ctx.config.project
|
|
95805
95951
|
});
|
|
95806
95952
|
assertIsValidPrd(prd);
|
|
95807
|
-
|
|
95953
|
+
const finalized = finalizePrdRouting({ ...prd, project: ctx.projectName }, ctx.config.routing?.agents, ctx.profileName);
|
|
95954
|
+
await ctx.deps.writeFile(ctx.outputPath, JSON.stringify(finalized, null, 2));
|
|
95808
95955
|
return ctx.outputPath;
|
|
95809
95956
|
} catch (err) {
|
|
95810
95957
|
if (ctx.deps.existsSync(ctx.outputPath)) {
|
|
95811
95958
|
const rawContent = await ctx.deps.readFile(ctx.outputPath);
|
|
95812
95959
|
const recoveredPrd = validatePlanOutput(rawContent, ctx.options.feature, ctx.branchName);
|
|
95813
|
-
|
|
95960
|
+
const finalizedRecovered = finalizePrdRouting({ ...recoveredPrd, project: ctx.projectName }, ctx.config.routing?.agents, ctx.profileName);
|
|
95961
|
+
await ctx.deps.writeFile(ctx.outputPath, JSON.stringify(finalizedRecovered, null, 2));
|
|
95814
95962
|
return ctx.outputPath;
|
|
95815
95963
|
}
|
|
95816
95964
|
throw err;
|
|
@@ -96856,6 +97004,7 @@ var FIELD_DESCRIPTIONS = {
|
|
|
96856
97004
|
"acceptance.testPath": "Path to acceptance test file (relative to feature dir)",
|
|
96857
97005
|
"acceptance.command": "Override command to run acceptance tests. Use {{FILE}} as placeholder for the test file path (default: 'bun test {{FILE}} --timeout=60000')",
|
|
96858
97006
|
"acceptance.model": 'Model selector for acceptance generation/refinement LLM calls. Accepts a tier string such as "fast", "balanced", or "powerful", or an explicit object like { agent: "codex", model: "gpt-5.4" }. Default: "fast".',
|
|
97007
|
+
"acceptance.generateModel": 'Model selector specifically for acceptance test generation and criteria refinement LLM calls. When set, overrides acceptance.model for these two operations. Accepts a tier string such as "fast", "balanced", or "powerful", or an explicit object like { agent: "opencode", model: "opencode-go/deepseek-v4-flash" }. Defaults to acceptance.model when not set.',
|
|
96859
97008
|
"acceptance.refinement": "Enable acceptance criteria refinement step before execution (default: true). Disable to skip refinement and use generated criteria as-is.",
|
|
96860
97009
|
"acceptance.timeoutMs": "Timeout for acceptance test generation in milliseconds (default: 1800000 = 30 min)",
|
|
96861
97010
|
context: "Context injection configuration",
|