@nathapp/nax 0.67.0-canary.3 → 0.67.0-canary.4

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.
Files changed (2) hide show
  1. package/dist/nax.js +708 -622
  2. package/package.json +6 -4
package/dist/nax.js CHANGED
@@ -17904,7 +17904,7 @@ function formatConsole(entry) {
17904
17904
  const timestamp = new Date(entry.timestamp).toLocaleTimeString("en-US", {
17905
17905
  hour12: false
17906
17906
  });
17907
- let levelColor;
17907
+ let levelColor = source_default.gray;
17908
17908
  switch (entry.level) {
17909
17909
  case "error":
17910
17910
  levelColor = source_default.red;
@@ -17916,6 +17916,7 @@ function formatConsole(entry) {
17916
17916
  levelColor = source_default.blue;
17917
17917
  break;
17918
17918
  case "debug":
17919
+ case "silent":
17919
17920
  levelColor = source_default.gray;
17920
17921
  break;
17921
17922
  }
@@ -18064,7 +18065,7 @@ ${JSON.stringify(entry.data, null, 2)}`;
18064
18065
  }
18065
18066
  close() {}
18066
18067
  }
18067
- function initLogger(options = { level: "warn" }) {
18068
+ function initLogger(options = { level: "silent" }) {
18068
18069
  if (instance) {
18069
18070
  throw new Error("Logger already initialized. Call getLogger() to access existing instance.");
18070
18071
  }
@@ -18095,12 +18096,13 @@ var init_logger = __esm(() => {
18095
18096
  init_logging();
18096
18097
  init_formatters();
18097
18098
  LOG_LEVEL_PRIORITY = {
18099
+ silent: -1,
18098
18100
  error: 0,
18099
18101
  warn: 1,
18100
18102
  info: 2,
18101
18103
  debug: 3
18102
18104
  };
18103
- noopLogger = new Logger({ level: "error", useChalk: false, headless: false });
18105
+ noopLogger = new Logger({ level: "silent", useChalk: false, headless: false });
18104
18106
  });
18105
18107
 
18106
18108
  // src/logger/index.ts
@@ -34213,6 +34215,7 @@ var init_debate_propose = __esm(() => {
34213
34215
  return "fast";
34214
34216
  return { agent: debater.agent, model: debater.model ?? "fast" };
34215
34217
  },
34218
+ timeoutMs: (_input, ctx) => (ctx.config.debate?.stages?.review?.timeoutSeconds ?? 600) * 1000,
34216
34219
  build(input, _ctx) {
34217
34220
  const builder = new DebatePromptBuilder({ taskContext: input.taskContext, outputFormat: input.outputFormat, stage: input.stage }, { debaters: input.debaters, sessionMode: "one-shot", proposers: input.stageConfig?.proposers });
34218
34221
  return builder.proposeSlot(input.debaterIndex);
@@ -34235,6 +34238,7 @@ var init_debate_judge = __esm(() => {
34235
34238
  jsonMode: false,
34236
34239
  config: debateConfigSelector,
34237
34240
  model: (input) => ({ agent: input.resolverAgent, model: input.resolverModel }),
34241
+ timeoutMs: (input, ctx) => (input.timeoutSeconds ?? ctx.config.debate?.stages?.review?.timeoutSeconds ?? 600) * 1000,
34238
34242
  build(input, _ctx) {
34239
34243
  const prompt = DebatePromptBuilder.resolverJudgePrompt(input.proposals, input.critiques, input.debaters);
34240
34244
  return {
@@ -34260,6 +34264,7 @@ var init_debate_synthesis = __esm(() => {
34260
34264
  jsonMode: false,
34261
34265
  config: debateConfigSelector,
34262
34266
  model: (input) => ({ agent: input.resolverAgent, model: input.resolverModel }),
34267
+ timeoutMs: (input, ctx) => (input.timeoutSeconds ?? ctx.config.debate?.stages?.review?.timeoutSeconds ?? 600) * 1000,
34263
34268
  build(input, _ctx) {
34264
34269
  const base = DebatePromptBuilder.resolverSynthesisPrompt(input.proposals, input.critiques, input.debaters);
34265
34270
  const content = input.promptSuffix ? `${base}
@@ -34293,6 +34298,7 @@ var init_debate_rebut = __esm(() => {
34293
34298
  return "fast";
34294
34299
  return { agent: debater.agent, model: debater.model ?? "fast" };
34295
34300
  },
34301
+ timeoutMs: (_input, ctx) => (ctx.config.debate?.stages?.review?.timeoutSeconds ?? 600) * 1000,
34296
34302
  build(input, _ctx) {
34297
34303
  const builder = new DebatePromptBuilder({ taskContext: input.taskContext, outputFormat: "", stage: input.stage }, { debaters: input.debaters, sessionMode: "one-shot" });
34298
34304
  return builder.rebutSlot(input.debaterIndex, input.proposals);
@@ -34339,6 +34345,7 @@ var init_debate_stateful = __esm(() => {
34339
34345
  session: { role: "debate-stateful", lifetime: "fresh" },
34340
34346
  config: debateConfigSelector,
34341
34347
  model: (input) => ({ agent: input.debater.agent, model: input.debater.model ?? "fast" }),
34348
+ timeoutMs: (_input, ctx) => (ctx.config.debate?.stages?.review?.timeoutSeconds ?? 600) * 1000,
34342
34349
  async hopBody(initialPrompt, ctx) {
34343
34350
  const proposal = ctx.input.turnSemaphore ? await ctx.input.turnSemaphore.run(() => ctx.send(initialPrompt)) : await ctx.send(initialPrompt);
34344
34351
  ctx.input.proposalBarriers[ctx.input.index].resolve(proposal.output);
@@ -34510,7 +34517,8 @@ var RESOLVER_FALLBACK_AGENT = "synthesis", RESOLVER_FALLBACK_MODEL = "fast", jud
34510
34517
  critiques: ctx.critiques,
34511
34518
  debaters: ctx.debaters,
34512
34519
  resolverAgent,
34513
- resolverModel
34520
+ resolverModel,
34521
+ timeoutSeconds: ctx.stageConfig.timeoutSeconds
34514
34522
  });
34515
34523
  return {
34516
34524
  outcome: output.trim() ? "passed" : "failed",
@@ -34533,7 +34541,8 @@ var RESOLVER_FALLBACK_AGENT2 = "synthesis", RESOLVER_FALLBACK_MODEL2 = "fast", s
34533
34541
  debaters: ctx.debaters,
34534
34542
  resolverAgent,
34535
34543
  resolverModel,
34536
- promptSuffix: ctx.promptSuffix
34544
+ promptSuffix: ctx.promptSuffix,
34545
+ timeoutSeconds: ctx.stageConfig.timeoutSeconds
34537
34546
  });
34538
34547
  return {
34539
34548
  outcome: output.trim() ? "passed" : "failed",
@@ -34846,6 +34855,7 @@ var init_debate_hybrid = __esm(() => {
34846
34855
  session: { role: "debate-hybrid", lifetime: "fresh" },
34847
34856
  config: debateConfigSelector,
34848
34857
  model: (input) => ({ agent: input.debater.agent, model: input.debater.model ?? "fast" }),
34858
+ timeoutMs: (_input, ctx) => (ctx.config.debate?.stages?.review?.timeoutSeconds ?? 600) * 1000,
34849
34859
  async hopBody(initialPrompt, ctx) {
34850
34860
  const logger = _debateSessionDeps.getSafeLogger();
34851
34861
  let totalCostUsd = 0;
@@ -34909,6 +34919,7 @@ var init_debate_plan = __esm(() => {
34909
34919
  session: { role: "debate-plan", lifetime: "fresh" },
34910
34920
  config: debateConfigSelector,
34911
34921
  model: (input) => ({ agent: input.debater.agent, model: input.debater.model ?? "fast" }),
34922
+ timeoutMs: (_input, ctx) => (ctx.config.debate?.stages?.plan?.timeoutSeconds ?? 600) * 1000,
34912
34923
  fileOutput: (input) => input.outputPath,
34913
34924
  async hopBody(initialPrompt, ctx) {
34914
34925
  const proposal = ctx.input.turnSemaphore ? await ctx.input.turnSemaphore.run(() => ctx.send(initialPrompt)) : await ctx.send(initialPrompt);
@@ -36848,7 +36859,7 @@ function countTotalAttempts(iterations) {
36848
36859
  return iterations.reduce((sum, iter) => sum + iter.fixesApplied.length, 0);
36849
36860
  }
36850
36861
  async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
36851
- const logger = getSafeLogger();
36862
+ const logger = _deps.logger !== undefined ? _deps.logger : getSafeLogger();
36852
36863
  const doCallOp = _deps.callOp ?? _cycleDeps.callOp;
36853
36864
  const now = _deps.now ?? _cycleDeps.now;
36854
36865
  const storyId = ctx.storyId;
@@ -36978,29 +36989,77 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
36978
36989
  costUsd: totalCostUsd
36979
36990
  };
36980
36991
  }
36981
- const provisionalIterations = [...cycle.iterations, { fixesApplied }];
36982
- const allExhausted = group.every((s) => countStrategyAttempts(provisionalIterations, s.name) >= s.maxAttempts);
36992
+ const allExhausted = group.every((s) => {
36993
+ const prior = countStrategyAttempts(cycle.iterations, s.name);
36994
+ const current = fixesApplied.filter((fa) => fa.strategyName === s.name).length;
36995
+ return prior + current >= s.maxAttempts;
36996
+ });
36983
36997
  if (allExhausted) {
36998
+ let liteFindingsAfter;
36999
+ try {
37000
+ liteFindingsAfter = await cycle.validate(ctx, { mode: "lite" });
37001
+ } catch (err) {
37002
+ const finishedAt3 = now();
37003
+ cycle.iterations.push({
37004
+ iterationNum: cycle.iterations.length + 1,
37005
+ findingsBefore,
37006
+ fixesApplied,
37007
+ findingsAfter: cycle.findings,
37008
+ outcome: "unchanged",
37009
+ startedAt,
37010
+ finishedAt: finishedAt3
37011
+ });
37012
+ logger?.warn("findings.cycle", "lite validate failed on terminal exhausted branch", {
37013
+ storyId,
37014
+ packageDir,
37015
+ cycleName,
37016
+ error: errorMessage(err)
37017
+ });
37018
+ return {
37019
+ iterations: cycle.iterations,
37020
+ finalFindings: cycle.findings,
37021
+ exitReason: "max-attempts-per-strategy",
37022
+ exhaustedStrategy: group[0]?.name,
37023
+ costUsd: totalCostUsd
37024
+ };
37025
+ }
37026
+ const outcome2 = classifyOutcome(findingsBefore, liteFindingsAfter);
36984
37027
  const finishedAt2 = now();
36985
37028
  cycle.iterations.push({
36986
37029
  iterationNum: cycle.iterations.length + 1,
36987
37030
  findingsBefore,
36988
37031
  fixesApplied,
36989
- findingsAfter: cycle.findings,
36990
- outcome: "unchanged",
37032
+ findingsAfter: liteFindingsAfter,
37033
+ outcome: outcome2,
36991
37034
  startedAt,
36992
37035
  finishedAt: finishedAt2
36993
37036
  });
36994
- logger?.info("findings.cycle", "cycle exited \u2014 strategy attempt cap reached (skipped final validate)", {
37037
+ cycle.findings = liteFindingsAfter;
37038
+ if (liteFindingsAfter.length === 0) {
37039
+ logger?.info("findings.cycle", "cycle exited \u2014 resolved after terminal lite validate", {
37040
+ storyId,
37041
+ packageDir,
37042
+ cycleName,
37043
+ reason: "resolved"
37044
+ });
37045
+ return {
37046
+ iterations: cycle.iterations,
37047
+ finalFindings: [],
37048
+ exitReason: "resolved",
37049
+ costUsd: totalCostUsd
37050
+ };
37051
+ }
37052
+ logger?.info("findings.cycle", "cycle exited \u2014 strategy attempt cap reached (lite validate)", {
36995
37053
  storyId,
36996
37054
  packageDir,
36997
37055
  cycleName,
36998
37056
  reason: "max-attempts-per-strategy",
36999
- exhaustedStrategy: group[0]?.name
37057
+ exhaustedStrategy: group[0]?.name,
37058
+ liteFindingsAfterCount: liteFindingsAfter.length
37000
37059
  });
37001
37060
  return {
37002
37061
  iterations: cycle.iterations,
37003
- finalFindings: cycle.findings,
37062
+ finalFindings: liteFindingsAfter,
37004
37063
  exitReason: "max-attempts-per-strategy",
37005
37064
  exhaustedStrategy: group[0]?.name,
37006
37065
  costUsd: totalCostUsd
@@ -37010,7 +37069,7 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
37010
37069
  let validatorAttempt = 0;
37011
37070
  for (;; ) {
37012
37071
  try {
37013
- findingsAfter = await cycle.validate(ctx);
37072
+ findingsAfter = await cycle.validate(ctx, { mode: "full" });
37014
37073
  break;
37015
37074
  } catch (err) {
37016
37075
  if (validatorAttempt >= cycle.config.validatorRetries) {
@@ -39290,8 +39349,481 @@ async function runRetryLoop(input) {
39290
39349
  return { outcome: "exhausted", attempts: input.maxAttempts, finalFailure: currentFailure };
39291
39350
  }
39292
39351
 
39352
+ // src/execution/escalation/escalation.ts
39353
+ function escalateTier(currentTier, tierOrder) {
39354
+ const getName = (t) => t.tier ?? t.name ?? null;
39355
+ const currentIndex = tierOrder.findIndex((t) => getName(t) === currentTier);
39356
+ if (currentIndex === -1 || currentIndex === tierOrder.length - 1) {
39357
+ return null;
39358
+ }
39359
+ const next = tierOrder[currentIndex + 1];
39360
+ const nextName = getName(next);
39361
+ if (!nextName)
39362
+ return null;
39363
+ return { tier: nextName, agent: next.agent };
39364
+ }
39365
+ function calculateMaxIterations(tierOrder) {
39366
+ return tierOrder.reduce((sum, t) => sum + t.attempts, 0);
39367
+ }
39368
+
39369
+ // src/session/naming.ts
39370
+ var exports_naming = {};
39371
+ __export(exports_naming, {
39372
+ formatSessionName: () => formatSessionName
39373
+ });
39374
+ var init_naming = __esm(() => {
39375
+ init_session_name();
39376
+ });
39377
+
39378
+ // src/verification/failure-records.ts
39379
+ function truncateUnmappedFailureOutput(output) {
39380
+ const tailLines = output.split(`
39381
+ `).slice(-UNMAPPED_FAILURE_OUTPUT_MAX_LINES).join(`
39382
+ `);
39383
+ if (tailLines.length <= UNMAPPED_FAILURE_OUTPUT_MAX_CHARS) {
39384
+ return tailLines;
39385
+ }
39386
+ return `... (truncated)
39387
+ ${tailLines.slice(-UNMAPPED_FAILURE_OUTPUT_MAX_CHARS)}`;
39388
+ }
39389
+ function buildFailureRecords(testSummary, rawOutput) {
39390
+ if (testSummary.failures.length > 0) {
39391
+ return testSummary.failures.map((failure) => ({
39392
+ test: failure.testName,
39393
+ file: failure.file,
39394
+ message: failure.error,
39395
+ output: failure.stackTrace.length > 0 ? failure.stackTrace.join(`
39396
+ `) : undefined
39397
+ }));
39398
+ }
39399
+ if (testSummary.failed === 0) {
39400
+ return [];
39401
+ }
39402
+ return [
39403
+ {
39404
+ test: `Unmapped test failures (${testSummary.failed} detected)`,
39405
+ message: "Structured test failure parsing returned no failure records. Diagnose the regression from the raw test output.",
39406
+ output: rawOutput?.trim() ? truncateUnmappedFailureOutput(rawOutput.trim()) : undefined
39407
+ }
39408
+ ];
39409
+ }
39410
+ var UNMAPPED_FAILURE_OUTPUT_MAX_LINES = 200, UNMAPPED_FAILURE_OUTPUT_MAX_CHARS = 8000;
39411
+
39412
+ // src/verification/rectification-loop.ts
39413
+ async function _defaultRunDebate(storyId, stageConfig, prompt, config2, agentManager) {
39414
+ const logger = getSafeLogger();
39415
+ const debaters = stageConfig.debaters ?? [];
39416
+ const manager = agentManager;
39417
+ const resolved = [];
39418
+ for (const debater of debaters) {
39419
+ if (manager.getAgent(debater.agent)) {
39420
+ resolved.push({ debater, agentName: debater.agent });
39421
+ }
39422
+ }
39423
+ if (resolved.length === 0) {
39424
+ return { output: null, totalCostUsd: 0 };
39425
+ }
39426
+ const timeoutMs = (config2.execution?.sessionTimeoutSeconds ?? 600) * 1000;
39427
+ const defaultAgentName = resolveDefaultAgent(config2);
39428
+ const resolvedDebaters = resolved.map(({ debater, agentName }) => {
39429
+ let modelDef;
39430
+ try {
39431
+ modelDef = resolveConfiguredModel(config2.models, agentName, debater.model ?? "balanced", defaultAgentName).modelDef;
39432
+ } catch {
39433
+ modelDef = { provider: "unknown", model: debater.model ?? "default" };
39434
+ }
39435
+ return { debater, agentName, modelDef };
39436
+ });
39437
+ const startMs = Date.now();
39438
+ const proposalSettled = await Promise.allSettled(resolvedDebaters.map(({ agentName, modelDef }) => manager.completeAs(agentName, prompt, {
39439
+ modelDef,
39440
+ workdir: "",
39441
+ storyId,
39442
+ sessionRole: "debate-proposal",
39443
+ timeoutMs
39444
+ }).then((out) => typeof out === "string" ? out : out.output)));
39445
+ const durationMs = Date.now() - startMs;
39446
+ const successful = proposalSettled.filter((r) => r.status === "fulfilled").map((r) => r.value);
39447
+ if (successful.length === 0) {
39448
+ return { output: null, totalCostUsd: 0 };
39449
+ }
39450
+ const successCount = successful.length;
39451
+ const costPerDebater = estimateCostByDuration("balanced", durationMs / successCount);
39452
+ const totalCostUsd = costPerDebater.cost * successCount;
39453
+ logger?.debug("rectification", "debate diagnosis complete", { storyId, successCount, totalCostUsd });
39454
+ const output = successful.join(`
39455
+
39456
+ `);
39457
+ return { output, totalCostUsd };
39458
+ }
39459
+ async function runRectificationLoop(opts) {
39460
+ const loopStartMs = Date.now();
39461
+ const {
39462
+ config: config2,
39463
+ workdir,
39464
+ story,
39465
+ testCommand,
39466
+ timeoutSeconds,
39467
+ testOutput,
39468
+ promptPrefix,
39469
+ featureName,
39470
+ projectDir,
39471
+ testScopedTemplate,
39472
+ sessionId,
39473
+ runtime
39474
+ } = opts;
39475
+ const logger = getSafeLogger();
39476
+ const agentManager = opts.agentManager ?? _rectificationDeps.agentManager;
39477
+ if (!agentManager) {
39478
+ logger?.warn("rectification", "No agentManager threaded \u2014 skipping rectification loop", {
39479
+ storyId: story.id
39480
+ });
39481
+ return { succeeded: false, cost: 0, durationMs: 0 };
39482
+ }
39483
+ const rectificationConfig = config2.execution.rectification;
39484
+ const testSummary = parseTestOutput(testOutput);
39485
+ const label = promptPrefix ? "regression rectification" : "rectification";
39486
+ const rectificationSessionName = formatSessionName({
39487
+ workdir,
39488
+ featureName,
39489
+ storyId: story.id,
39490
+ role: "implementer"
39491
+ });
39492
+ let costAccum = 0;
39493
+ let currentAttempt = 0;
39494
+ let heldHandle;
39495
+ const initialFailure = {
39496
+ testOutput,
39497
+ testSummary
39498
+ };
39499
+ const outcome = await runRetryLoop({
39500
+ stage: "rectification",
39501
+ storyId: story.id,
39502
+ packageDir: workdir,
39503
+ maxAttempts: rectificationConfig.maxRetries,
39504
+ failure: initialFailure,
39505
+ previousAttempts: [],
39506
+ buildPrompt: (failure) => {
39507
+ currentAttempt++;
39508
+ const diagnosisPrefix = null;
39509
+ const debateStageConfig = config2.debate?.stages?.rectification;
39510
+ let debatePromise = Promise.resolve(null);
39511
+ if (debateStageConfig?.enabled) {
39512
+ const failureSummary = formatFailureSummary(failure.testSummary.failures);
39513
+ const diagnosisPrompt = `Analyze the following test failures and identify the root cause:
39514
+
39515
+ ${failureSummary}`;
39516
+ debatePromise = (async () => {
39517
+ try {
39518
+ const debateResult = await _rectificationDeps.runDebate(story.id, debateStageConfig, diagnosisPrompt, config2, agentManager);
39519
+ if (debateResult.totalCostUsd > 0 && story.routing) {
39520
+ story.routing.estimatedCostUsd = (story.routing.estimatedCostUsd ?? 0) + debateResult.totalCostUsd;
39521
+ }
39522
+ if (debateResult.output !== null) {
39523
+ return `## Root Cause Analysis
39524
+
39525
+ ${debateResult.output}`;
39526
+ }
39527
+ logger?.info("rectification", "debate diagnosis fallback \u2014 all debaters failed", {
39528
+ storyId: story.id,
39529
+ event: "fallback"
39530
+ });
39531
+ return null;
39532
+ } catch (_error) {
39533
+ logger?.info("rectification", "debate diagnosis fallback \u2014 debate threw error", {
39534
+ storyId: story.id,
39535
+ event: "fallback"
39536
+ });
39537
+ return null;
39538
+ }
39539
+ })();
39540
+ }
39541
+ const failureRecords = buildFailureRecords(failure.testSummary, failure.testOutput);
39542
+ const rectPrompt = RectifierPromptBuilder.regressionFailure({
39543
+ story,
39544
+ failures: failureRecords,
39545
+ testCommand,
39546
+ conventions: true
39547
+ });
39548
+ const rectPromise = Promise.resolve(rectPrompt);
39549
+ return (async () => {
39550
+ const [diagnosis, rectificationPrompt] = await Promise.all([debatePromise, rectPromise]);
39551
+ let finalPrompt = rectificationPrompt;
39552
+ if (diagnosis) {
39553
+ finalPrompt = `${diagnosis}
39554
+
39555
+ ${finalPrompt}`;
39556
+ }
39557
+ if (promptPrefix) {
39558
+ finalPrompt = `${promptPrefix}
39559
+
39560
+ ${finalPrompt}`;
39561
+ }
39562
+ return finalPrompt;
39563
+ })();
39564
+ },
39565
+ execute: async (prompt) => {
39566
+ const defaultAgent = agentManager.getDefault();
39567
+ const complexity = story.routing?.complexity ?? "medium";
39568
+ const modelTier = config2.autoMode.complexityRouting?.[complexity] || config2.autoMode.escalation.tierOrder[0]?.tier || "balanced";
39569
+ const modelDef = resolveModelForAgent(config2.models, story.routing?.agent ?? defaultAgent, modelTier, defaultAgent);
39570
+ let agentResult;
39571
+ {
39572
+ let transportRetries = 0;
39573
+ const maxTransportRetries = config2.execution?.sessionErrorRetryableMaxRetries ?? 3;
39574
+ while (true) {
39575
+ if (!heldHandle) {
39576
+ heldHandle = await runtime.sessionManager.openSession(rectificationSessionName, {
39577
+ agentName: defaultAgent,
39578
+ role: "implementer",
39579
+ workdir,
39580
+ pipelineStage: "rectification",
39581
+ modelDef,
39582
+ timeoutSeconds: config2.execution.sessionTimeoutSeconds,
39583
+ featureName,
39584
+ storyId: story.id,
39585
+ signal: runtime.signal
39586
+ });
39587
+ }
39588
+ try {
39589
+ const turn = await agentManager.runAsSession(defaultAgent, heldHandle, prompt, {
39590
+ storyId: story.id,
39591
+ featureName,
39592
+ workdir,
39593
+ projectDir,
39594
+ pipelineStage: "rectification",
39595
+ sessionRole: "implementer",
39596
+ signal: runtime.signal,
39597
+ maxTurns: config2.agent?.maxInteractionTurns
39598
+ });
39599
+ agentResult = {
39600
+ success: true,
39601
+ exitCode: 0,
39602
+ output: turn.output,
39603
+ rateLimited: false,
39604
+ durationMs: 0,
39605
+ estimatedCostUsd: turn.estimatedCostUsd,
39606
+ ...turn.exactCostUsd !== undefined && { exactCostUsd: turn.exactCostUsd },
39607
+ ...turn.tokenUsage && { tokenUsage: turn.tokenUsage },
39608
+ ...heldHandle.protocolIds && { protocolIds: heldHandle.protocolIds }
39609
+ };
39610
+ break;
39611
+ } catch (err) {
39612
+ const stale = heldHandle;
39613
+ heldHandle = undefined;
39614
+ await runtime.sessionManager.closeSession(stale).catch(() => {});
39615
+ if (err instanceof SessionTurnError && err.retryable && transportRetries < maxTransportRetries) {
39616
+ transportRetries++;
39617
+ getSafeLogger()?.warn("rectification", "fail-adapter-error: same-agent retry with fresh session", {
39618
+ storyId: story.id,
39619
+ attempt: transportRetries,
39620
+ maxAttempts: maxTransportRetries,
39621
+ retriable: true
39622
+ });
39623
+ continue;
39624
+ }
39625
+ throw err;
39626
+ }
39627
+ }
39628
+ }
39629
+ costAccum += agentResult.estimatedCostUsd ?? 0;
39630
+ if (sessionId && agentResult.protocolIds) {
39631
+ try {
39632
+ runtime.sessionManager.bindHandle(sessionId, rectificationSessionName, agentResult.protocolIds);
39633
+ } catch {}
39634
+ }
39635
+ if (agentResult.success) {
39636
+ logger?.info("rectification", `Agent ${label} session complete`, {
39637
+ storyId: story.id,
39638
+ cost: agentResult.estimatedCostUsd
39639
+ });
39640
+ } else {
39641
+ logger?.warn("rectification", `Agent ${label} session failed`, {
39642
+ storyId: story.id,
39643
+ exitCode: agentResult.exitCode
39644
+ });
39645
+ }
39646
+ return {
39647
+ agentSuccess: agentResult.success,
39648
+ cost: agentResult.estimatedCostUsd ?? 0,
39649
+ protocolIds: agentResult.protocolIds
39650
+ };
39651
+ },
39652
+ shouldAbort: (failure) => {
39653
+ if (rectificationConfig.abortOnIncreasingFailures) {
39654
+ return failure.testSummary.failed > initialFailure.testSummary.failed;
39655
+ }
39656
+ return false;
39657
+ },
39658
+ verify: async (result) => {
39659
+ const retryVerification = await _rectificationDeps.runVerification({
39660
+ workdir,
39661
+ expectedFiles: getExpectedFiles(story),
39662
+ command: testCommand,
39663
+ timeoutSeconds,
39664
+ forceExit: config2.quality.forceExit,
39665
+ detectOpenHandles: config2.quality.detectOpenHandles,
39666
+ detectOpenHandlesRetries: config2.quality.detectOpenHandlesRetries,
39667
+ timeoutRetryCount: 0,
39668
+ gracePeriodMs: config2.quality.gracePeriodMs,
39669
+ drainTimeoutMs: config2.quality.drainTimeoutMs,
39670
+ shell: config2.quality.shell,
39671
+ stripEnvVars: config2.quality.stripEnvVars
39672
+ });
39673
+ if (retryVerification.success) {
39674
+ logger?.info("rectification", `[OK] ${label} succeeded!`, {
39675
+ storyId: story.id,
39676
+ attempt: currentAttempt,
39677
+ initialFailures: initialFailure.testSummary.failed
39678
+ });
39679
+ return { passed: true };
39680
+ }
39681
+ if (retryVerification.output) {
39682
+ const newTestSummary = parseTestOutput(retryVerification.output);
39683
+ if (newTestSummary.failed === 0 && retryVerification.status !== "ENVIRONMENTAL_FAILURE" && (retryVerification.status === "SUCCESS" || newTestSummary.passed > 0)) {
39684
+ logger?.info("rectification", `[OK] ${label} succeeded after parsing retry output`, {
39685
+ storyId: story.id,
39686
+ attempt: currentAttempt,
39687
+ initialFailures: initialFailure.testSummary.failed
39688
+ });
39689
+ return { passed: true };
39690
+ }
39691
+ const failingTests = newTestSummary.failures.slice(0, 10).map((failure) => failure.testName);
39692
+ const logData = {
39693
+ storyId: story.id,
39694
+ attempt: currentAttempt,
39695
+ remainingFailures: newTestSummary.failed,
39696
+ failingTests
39697
+ };
39698
+ if (newTestSummary.failures.length > 10 || newTestSummary.failures.length === 0 && newTestSummary.failed > 0) {
39699
+ logData.totalFailingTests = newTestSummary.failed;
39700
+ }
39701
+ logger?.warn("rectification", `${label} still failing after attempt`, logData);
39702
+ return {
39703
+ passed: false,
39704
+ newFailure: {
39705
+ testOutput: retryVerification.output,
39706
+ testSummary: newTestSummary
39707
+ }
39708
+ };
39709
+ }
39710
+ return {
39711
+ passed: false,
39712
+ newFailure: {
39713
+ testOutput: retryVerification.output ?? "",
39714
+ testSummary: initialFailure.testSummary
39715
+ }
39716
+ };
39717
+ }
39718
+ }).finally(async () => {
39719
+ if (heldHandle) {
39720
+ const stale = heldHandle;
39721
+ heldHandle = undefined;
39722
+ await runtime.sessionManager.closeSession(stale).catch(() => {});
39723
+ }
39724
+ });
39725
+ const succeeded = outcome.outcome === "fixed";
39726
+ if (outcome.outcome === "exhausted") {
39727
+ const finalFailure = outcome.finalFailure;
39728
+ const shouldEscalate = rectificationConfig.escalateOnExhaustion !== false && config2.autoMode?.escalation?.enabled === true && finalFailure.testSummary.failed > 0;
39729
+ if (shouldEscalate) {
39730
+ const complexity = story.routing?.complexity ?? "medium";
39731
+ const currentTier = config2.autoMode.complexityRouting?.[complexity] || config2.autoMode.escalation.tierOrder[0]?.tier || "balanced";
39732
+ const tierOrder = config2.autoMode.escalation.tierOrder;
39733
+ const escalationResult = _rectificationDeps.escalateTier(currentTier, tierOrder);
39734
+ const escalatedTier = escalationResult?.tier ?? null;
39735
+ const escalatedAgent = escalationResult?.agent;
39736
+ if (escalatedTier !== null) {
39737
+ const escalationManager = agentManager;
39738
+ const agentName = escalatedAgent ?? story.routing?.agent ?? escalationManager.getDefault();
39739
+ if (escalationManager.getAgent(agentName)) {
39740
+ const escalatedModelDef = resolveModelForAgent(config2.models, agentName, escalatedTier, escalationManager.getDefault());
39741
+ let escalationPrompt = RectifierPromptBuilder.escalated(finalFailure.testSummary.failures, story, outcome.attempts, currentTier, escalatedTier, rectificationConfig, testCommand, testScopedTemplate);
39742
+ if (promptPrefix) {
39743
+ escalationPrompt = `${promptPrefix}
39744
+
39745
+ ${escalationPrompt}`;
39746
+ }
39747
+ const escalationRunResult = await escalationManager.runAs(agentName, {
39748
+ runOptions: {
39749
+ prompt: escalationPrompt,
39750
+ workdir,
39751
+ modelTier: escalatedTier,
39752
+ modelDef: escalatedModelDef,
39753
+ timeoutSeconds: config2.execution.sessionTimeoutSeconds,
39754
+ pipelineStage: "rectification",
39755
+ config: config2,
39756
+ projectDir,
39757
+ maxInteractionTurns: config2.agent?.maxInteractionTurns,
39758
+ featureName,
39759
+ storyId: story.id,
39760
+ sessionRole: "implementer"
39761
+ }
39762
+ });
39763
+ costAccum += escalationRunResult.estimatedCostUsd ?? 0;
39764
+ logger?.info("rectification", "escalated rectification attempt cost", {
39765
+ storyId: story.id,
39766
+ escalatedTier,
39767
+ cost: escalationRunResult.estimatedCostUsd
39768
+ });
39769
+ const escalationVerification = await _rectificationDeps.runVerification({
39770
+ workdir,
39771
+ expectedFiles: getExpectedFiles(story),
39772
+ command: testCommand,
39773
+ timeoutSeconds,
39774
+ forceExit: config2.quality.forceExit,
39775
+ detectOpenHandles: config2.quality.detectOpenHandles,
39776
+ detectOpenHandlesRetries: config2.quality.detectOpenHandlesRetries,
39777
+ timeoutRetryCount: 0,
39778
+ gracePeriodMs: config2.quality.gracePeriodMs,
39779
+ drainTimeoutMs: config2.quality.drainTimeoutMs,
39780
+ shell: config2.quality.shell,
39781
+ stripEnvVars: config2.quality.stripEnvVars
39782
+ });
39783
+ if (escalationVerification.success) {
39784
+ logger?.info("rectification", `${label} escalated from ${currentTier} to ${escalatedTier} and succeeded`, {
39785
+ storyId: story.id,
39786
+ currentTier,
39787
+ escalatedTier
39788
+ });
39789
+ return { succeeded: true, cost: costAccum, durationMs: Date.now() - loopStartMs };
39790
+ }
39791
+ logger?.warn("rectification", "escalated rectification also failed", { storyId: story.id, escalatedTier });
39792
+ }
39793
+ }
39794
+ }
39795
+ logger?.warn("rectification", `${label} exhausted max retries`, {
39796
+ storyId: story.id,
39797
+ attempts: outcome.attempts,
39798
+ remainingFailures: initialFailure.testSummary.failed
39799
+ });
39800
+ }
39801
+ return { succeeded, cost: costAccum, durationMs: Date.now() - loopStartMs };
39802
+ }
39803
+ var _rectificationDeps;
39804
+ var init_rectification_loop = __esm(() => {
39805
+ init_agents();
39806
+ init_cost();
39807
+ init_types3();
39808
+ init_config();
39809
+ init_logger2();
39810
+ init_prd();
39811
+ init_prompts();
39812
+ init_naming();
39813
+ init_parser2();
39814
+ init_parser2();
39815
+ init_runners();
39816
+ _rectificationDeps = {
39817
+ agentManager: undefined,
39818
+ runVerification: fullSuite,
39819
+ escalateTier,
39820
+ runDebate: _defaultRunDebate
39821
+ };
39822
+ });
39823
+
39293
39824
  // src/verification/index.ts
39294
39825
  var init_verification = __esm(() => {
39826
+ init_rectification_loop();
39295
39827
  init_executor();
39296
39828
  init_parser2();
39297
39829
  init_runners();
@@ -42861,15 +43393,6 @@ var init_manager_sweep = __esm(() => {
42861
43393
  DEFAULT_ORPHAN_TTL_MS = 4 * 60 * 60 * 1000;
42862
43394
  });
42863
43395
 
42864
- // src/session/naming.ts
42865
- var exports_naming = {};
42866
- __export(exports_naming, {
42867
- formatSessionName: () => formatSessionName
42868
- });
42869
- var init_naming = __esm(() => {
42870
- init_session_name();
42871
- });
42872
-
42873
43396
  // src/session/types.ts
42874
43397
  var SESSION_TRANSITIONS;
42875
43398
  var init_types7 = __esm(() => {
@@ -45211,7 +45734,11 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
45211
45734
  const proposalBarriers = resolved.map(() => Promise.withResolvers());
45212
45735
  const rebutBuilder = new DebatePromptBuilder({ taskContext, outputFormat: "", stage: "plan" }, { debaters: resolved.map((e) => e.debater), sessionMode: "stateful" });
45213
45736
  const callOpPromises = resolved.map(({ debater, agentName }, index) => {
45214
- const debaterCtx = { ...ctx.callContext, agentName };
45737
+ const debaterCtx = {
45738
+ ...ctx.callContext,
45739
+ agentName,
45740
+ sessionOverride: { role: `debate-plan-${index}` }
45741
+ };
45215
45742
  return callOp(debaterCtx, planDebaterOp, {
45216
45743
  debater,
45217
45744
  index,
@@ -45276,7 +45803,11 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
45276
45803
  const rebuttalBarriers = resolved.map(() => Promise.withResolvers());
45277
45804
  const rebutBuilder = new DebatePromptBuilder({ taskContext, outputFormat: "", stage: "plan" }, { debaters: resolved.map((e) => e.debater), sessionMode: "stateful" });
45278
45805
  const callOpPromisesB = resolved.map(({ debater, agentName }, index) => {
45279
- const debaterCtx = { ...ctx.callContext, agentName };
45806
+ const debaterCtx = {
45807
+ ...ctx.callContext,
45808
+ agentName,
45809
+ sessionOverride: { role: `debate-plan-${index}` }
45810
+ };
45280
45811
  return callOp(debaterCtx, planDebaterOp, {
45281
45812
  debater,
45282
45813
  index,
@@ -45335,7 +45866,11 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
45335
45866
  for (const barrier of rebuttalBarriersC)
45336
45867
  barrier.resolve("");
45337
45868
  const settled = await allSettledBounded(resolved.map(({ debater, agentName }, index) => async () => {
45338
- const debaterCtx = { ...ctx.callContext, agentName };
45869
+ const debaterCtx = {
45870
+ ...ctx.callContext,
45871
+ agentName,
45872
+ sessionOverride: { role: `debate-plan-${index}` }
45873
+ };
45339
45874
  const result = await callOp(debaterCtx, planDebaterOp, {
45340
45875
  debater,
45341
45876
  index,
@@ -50459,42 +50994,8 @@ var init_greenfield = __esm(() => {
50459
50994
  ]);
50460
50995
  });
50461
50996
 
50462
- // src/verification/failure-records.ts
50463
- function truncateUnmappedFailureOutput(output) {
50464
- const tailLines = output.split(`
50465
- `).slice(-UNMAPPED_FAILURE_OUTPUT_MAX_LINES).join(`
50466
- `);
50467
- if (tailLines.length <= UNMAPPED_FAILURE_OUTPUT_MAX_CHARS) {
50468
- return tailLines;
50469
- }
50470
- return `... (truncated)
50471
- ${tailLines.slice(-UNMAPPED_FAILURE_OUTPUT_MAX_CHARS)}`;
50472
- }
50473
- function buildFailureRecords(testSummary, rawOutput) {
50474
- if (testSummary.failures.length > 0) {
50475
- return testSummary.failures.map((failure) => ({
50476
- test: failure.testName,
50477
- file: failure.file,
50478
- message: failure.error,
50479
- output: failure.stackTrace.length > 0 ? failure.stackTrace.join(`
50480
- `) : undefined
50481
- }));
50482
- }
50483
- if (testSummary.failed === 0) {
50484
- return [];
50485
- }
50486
- return [
50487
- {
50488
- test: `Unmapped test failures (${testSummary.failed} detected)`,
50489
- message: "Structured test failure parsing returned no failure records. Diagnose the regression from the raw test output.",
50490
- output: rawOutput?.trim() ? truncateUnmappedFailureOutput(rawOutput.trim()) : undefined
50491
- }
50492
- ];
50493
- }
50494
- var UNMAPPED_FAILURE_OUTPUT_MAX_LINES = 200, UNMAPPED_FAILURE_OUTPUT_MAX_CHARS = 8000;
50495
-
50496
50997
  // src/tdd/rectification-gate.ts
50497
- async function runFullSuiteGate(story, config2, workdir, agentManager, implementerTier, lite, logger, featureName, projectDir, sessionManager, sessionId, runtime) {
50998
+ async function runFullSuiteGate(story, config2, workdir, agentManager, implementerTier, lite, logger, featureName, projectDir, sessionId, runtime) {
50498
50999
  const rectificationEnabled = config2.execution.rectification?.enabled ?? false;
50499
51000
  if (!rectificationEnabled) {
50500
51001
  return { passed: false, cost: 0, fullSuiteGatePassed: false, status: "disabled" };
@@ -50522,16 +51023,16 @@ async function runFullSuiteGate(story, config2, workdir, agentManager, implement
50522
51023
  });
50523
51024
  return { passed: true, cost: 0, fullSuiteGatePassed: false, status: "deferred-unattributable" };
50524
51025
  }
50525
- return await runRectificationLoop(story, config2, workdir, agentManager, implementerTier, lite, logger, testSummary, rectificationConfig, effectiveTestCmd, fullSuiteTimeout, fullSuiteResult.output, featureName, projectDir, sessionManager, sessionId, runtime);
51026
+ return await runRectificationLoop2(story, config2, workdir, agentManager, implementerTier, lite, logger, testSummary, rectificationConfig, effectiveTestCmd, fullSuiteTimeout, fullSuiteResult.output, runtime, featureName, projectDir, sessionId);
50526
51027
  }
50527
51028
  if (testSummary.failures.length > 0) {
50528
- logger.warn("tdd", "Full suite gate: parser counter mismatch \u2014 routing to rectification using failures.length", {
51029
+ logger.warn("tdd", "Full suite gate: parser counter mismatch \u2014 failing gate (unreliable failure count)", {
50529
51030
  storyId: story.id,
50530
51031
  failedCounter: testSummary.failed,
50531
51032
  failuresLength: testSummary.failures.length,
50532
51033
  exitCode: fullSuiteResult.exitCode
50533
51034
  });
50534
- return await runRectificationLoop(story, config2, workdir, agentManager, implementerTier, lite, logger, { ...testSummary, failed: Math.max(testSummary.failed, testSummary.failures.length) }, rectificationConfig, effectiveTestCmd, fullSuiteTimeout, fullSuiteResult.output, featureName, projectDir, sessionManager, sessionId, runtime);
51035
+ return { passed: false, cost: 0, fullSuiteGatePassed: false, status: "execution-failed" };
50535
51036
  }
50536
51037
  if (testSummary.passed > 0) {
50537
51038
  logger.info("tdd", "Full suite gate passed (non-zero exit, 0 failures, tests detected)", {
@@ -50559,7 +51060,7 @@ async function runFullSuiteGate(story, config2, workdir, agentManager, implement
50559
51060
  });
50560
51061
  return { passed: false, cost: 0, fullSuiteGatePassed: false, status: "execution-failed" };
50561
51062
  }
50562
- async function runRectificationLoop(story, config2, workdir, agentManager, implementerTier, lite, logger, testSummary, rectificationConfig, testCmd, fullSuiteTimeout, testOutput, featureName, projectDir, sessionManager, sessionId, runtime) {
51063
+ async function runRectificationLoop2(story, config2, workdir, agentManager, implementerTier, lite, logger, testSummary, rectificationConfig, testCmd, fullSuiteTimeout, testOutput, runtime, featureName, projectDir, sessionId) {
50563
51064
  logger.warn("tdd", "Full suite gate detected regressions", {
50564
51065
  storyId: story.id,
50565
51066
  failedTests: testSummary.failed,
@@ -50597,7 +51098,6 @@ async function runRectificationLoop(story, config2, workdir, agentManager, imple
50597
51098
  },
50598
51099
  execute: async (prompt) => {
50599
51100
  currentAttempt++;
50600
- const isLastAttempt = currentAttempt >= rectificationConfig.maxRetries;
50601
51101
  const rectifyBeforeRef = await captureGitRef(workdir) ?? "HEAD";
50602
51102
  const defaultAgent = agentManager.getDefault();
50603
51103
  const runOptions = {
@@ -50615,71 +51115,65 @@ async function runRectificationLoop(story, config2, workdir, agentManager, imple
50615
51115
  sessionRole: "implementer"
50616
51116
  };
50617
51117
  let rectifyResult;
50618
- if (runtime) {
50619
- let transportRetries = 0;
50620
- const maxTransportRetries = config2.execution?.sessionErrorRetryableMaxRetries ?? 3;
50621
- while (transportRetries <= maxTransportRetries) {
50622
- if (!heldHandle) {
50623
- heldHandle = await runtime.sessionManager.openSession(rectificationSessionName, {
50624
- agentName: defaultAgent,
50625
- role: "implementer",
50626
- workdir,
50627
- pipelineStage: "rectification",
50628
- modelDef: runOptions.modelDef,
50629
- timeoutSeconds: config2.execution.sessionTimeoutSeconds,
50630
- featureName,
50631
- storyId: story.id,
50632
- signal: runtime.signal
50633
- });
50634
- }
50635
- try {
50636
- const turn = await agentManager.runAsSession(defaultAgent, heldHandle, prompt, {
51118
+ let transportRetries = 0;
51119
+ const maxTransportRetries = config2.execution?.sessionErrorRetryableMaxRetries ?? 3;
51120
+ while (true) {
51121
+ if (!heldHandle) {
51122
+ heldHandle = await runtime.sessionManager.openSession(rectificationSessionName, {
51123
+ agentName: defaultAgent,
51124
+ role: "implementer",
51125
+ workdir,
51126
+ pipelineStage: "rectification",
51127
+ modelDef: runOptions.modelDef,
51128
+ timeoutSeconds: config2.execution.sessionTimeoutSeconds,
51129
+ featureName,
51130
+ storyId: story.id,
51131
+ signal: runtime.signal
51132
+ });
51133
+ }
51134
+ try {
51135
+ const turn = await agentManager.runAsSession(defaultAgent, heldHandle, prompt, {
51136
+ storyId: story.id,
51137
+ featureName,
51138
+ workdir,
51139
+ projectDir,
51140
+ pipelineStage: "rectification",
51141
+ sessionRole: "implementer",
51142
+ signal: runtime.signal,
51143
+ maxTurns: config2.agent?.maxInteractionTurns
51144
+ });
51145
+ rectifyResult = {
51146
+ success: true,
51147
+ exitCode: 0,
51148
+ output: turn.output,
51149
+ rateLimited: false,
51150
+ durationMs: 0,
51151
+ estimatedCostUsd: turn.estimatedCostUsd,
51152
+ ...turn.exactCostUsd !== undefined && { exactCostUsd: turn.exactCostUsd },
51153
+ ...turn.tokenUsage && { tokenUsage: turn.tokenUsage },
51154
+ ...heldHandle.protocolIds && { protocolIds: heldHandle.protocolIds }
51155
+ };
51156
+ break;
51157
+ } catch (err) {
51158
+ const stale = heldHandle;
51159
+ heldHandle = undefined;
51160
+ await runtime.sessionManager.closeSession(stale).catch(() => {});
51161
+ if (err instanceof SessionTurnError && err.retryable && transportRetries < maxTransportRetries) {
51162
+ transportRetries++;
51163
+ getSafeLogger()?.warn("tdd", "fail-adapter-error: same-agent retry with fresh session", {
50637
51164
  storyId: story.id,
50638
- featureName,
50639
- workdir,
50640
- projectDir,
50641
- pipelineStage: "rectification",
50642
- sessionRole: "implementer",
50643
- signal: runtime.signal,
50644
- maxTurns: config2.agent?.maxInteractionTurns
51165
+ attempt: transportRetries,
51166
+ maxAttempts: maxTransportRetries,
51167
+ retriable: true
50645
51168
  });
50646
- rectifyResult = {
50647
- success: true,
50648
- exitCode: 0,
50649
- output: turn.output,
50650
- rateLimited: false,
50651
- durationMs: 0,
50652
- estimatedCostUsd: turn.estimatedCostUsd,
50653
- ...turn.exactCostUsd !== undefined && { exactCostUsd: turn.exactCostUsd },
50654
- ...turn.tokenUsage && { tokenUsage: turn.tokenUsage },
50655
- ...heldHandle.protocolIds && { protocolIds: heldHandle.protocolIds }
50656
- };
50657
- break;
50658
- } catch (err) {
50659
- const stale = heldHandle;
50660
- heldHandle = undefined;
50661
- await runtime.sessionManager.closeSession(stale).catch(() => {});
50662
- if (err instanceof SessionTurnError && err.retryable && transportRetries < maxTransportRetries) {
50663
- transportRetries++;
50664
- getSafeLogger()?.warn("tdd", "fail-adapter-error: same-agent retry with fresh session", {
50665
- storyId: story.id,
50666
- attempt: transportRetries,
50667
- maxAttempts: maxTransportRetries,
50668
- retriable: true
50669
- });
50670
- continue;
50671
- }
50672
- throw err;
51169
+ continue;
50673
51170
  }
51171
+ throw err;
50674
51172
  }
50675
- } else {
50676
- rectifyResult = await agentManager.run({
50677
- runOptions: { ...runOptions, keepOpen: !isLastAttempt }
50678
- });
50679
51173
  }
50680
- if (sessionManager && sessionId && rectifyResult.protocolIds) {
51174
+ if (sessionId && rectifyResult.protocolIds) {
50681
51175
  try {
50682
- sessionManager.bindHandle(sessionId, rectificationSessionName, rectifyResult.protocolIds);
51176
+ runtime.sessionManager.bindHandle(sessionId, rectificationSessionName, rectifyResult.protocolIds);
50683
51177
  } catch {}
50684
51178
  }
50685
51179
  if (!rectifyResult.success && rectifyResult.pid) {
@@ -50745,7 +51239,7 @@ async function runRectificationLoop(story, config2, workdir, agentManager, imple
50745
51239
  };
50746
51240
  }
50747
51241
  }).finally(async () => {
50748
- if (heldHandle && runtime) {
51242
+ if (heldHandle) {
50749
51243
  const stale = heldHandle;
50750
51244
  heldHandle = undefined;
50751
51245
  await runtime.sessionManager.closeSession(stale).catch(() => {});
@@ -51236,7 +51730,7 @@ async function runThreeSessionTdd(options) {
51236
51730
  };
51237
51731
  }
51238
51732
  const implementerBinding = getTddSessionBinding?.("implementer");
51239
- const fullSuiteGate = await runFullSuiteGate(story, config2, workdir, agentManager, implementerTier, lite, logger, featureName, projectDir, implementerBinding?.sessionManager, implementerBinding?.sessionId, runtime);
51733
+ const fullSuiteGate = await runFullSuiteGate(story, config2, workdir, agentManager, implementerTier, lite, logger, featureName, projectDir, implementerBinding?.sessionId, runtime);
51240
51734
  const { cost: fullSuiteGateCost, fullSuiteGatePassed } = fullSuiteGate;
51241
51735
  if (fullSuiteGate.status === "rectification-exhausted") {
51242
51736
  const failureCategory = "full-suite-gate-exhausted";
@@ -51522,6 +52016,7 @@ var init_tdd = __esm(() => {
51522
52016
  init_isolation();
51523
52017
  init_orchestrator3();
51524
52018
  init_orchestrator_ctx();
52019
+ init_rectification_gate();
51525
52020
  init_session_op();
51526
52021
  init_cleanup();
51527
52022
  init_verdict();
@@ -51936,9 +52431,9 @@ async function runAgentRectificationV2(ctx, _lintFixCmd, _formatFixCmd, _effecti
51936
52431
  maxAttemptsTotal: maxTotalAttempts,
51937
52432
  validatorRetries: 1
51938
52433
  },
51939
- async validate(_cycleCtx) {
52434
+ async validate(_cycleCtx, _opts) {
51940
52435
  iterationBeforeRef = await _autofixCycleGuardDeps.captureGitRef(ctx.workdir) ?? iterationBeforeRef;
51941
- await _autofixDeps.recheckReview(ctx);
52436
+ await _autofixDeps.recheckReview(ctx, { lite: _opts.mode === "lite" });
51942
52437
  const fresh = collectCurrentFindings(ctx);
51943
52438
  const pending = ctx.testEditDeclarations ?? [];
51944
52439
  if (pending.length === 0)
@@ -52734,7 +53229,7 @@ var init_review2 = __esm(() => {
52734
53229
  const reviewDebateEnabled = ctx.rootConfig?.debate?.enabled && ctx.rootConfig?.debate?.stages?.review?.enabled;
52735
53230
  const dialogueEnabled = ctx.config.review?.dialogue?.enabled ?? false;
52736
53231
  logger.info("review", "Running review phase", { storyId: ctx.story.id });
52737
- if (dialogueEnabled && !reviewDebateEnabled && ctx.reviewerSession) {
53232
+ if (dialogueEnabled && !reviewDebateEnabled && ctx.reviewerSession && !ctx.skipLLMReviewers) {
52738
53233
  try {
52739
53234
  const diff = ctx.storyGitRef ?? "";
52740
53235
  const reReviewResult = await ctx.reviewerSession.reReview(diff);
@@ -52769,7 +53264,7 @@ var init_review2 = __esm(() => {
52769
53264
  });
52770
53265
  }
52771
53266
  }
52772
- if (dialogueEnabled && !ctx.reviewerSession && ctx.agentManager && ctx.sessionManager) {
53267
+ if (dialogueEnabled && !ctx.reviewerSession && ctx.agentManager && ctx.sessionManager && !ctx.skipLLMReviewers) {
52773
53268
  ctx.reviewerSession = _reviewDeps.createReviewerSession(ctx.agentManager, ctx.sessionManager, ctx.story.id, ctx.workdir, ctx.prd.feature ?? "", ctx.config);
52774
53269
  if (!reviewDebateEnabled) {
52775
53270
  const semanticConfig = ctx.config.review?.semantic;
@@ -52873,16 +53368,36 @@ var init_review2 = __esm(() => {
52873
53368
  });
52874
53369
 
52875
53370
  // src/pipeline/stages/autofix.ts
52876
- async function recheckReview(ctx) {
53371
+ async function recheckReview(ctx, opts) {
53372
+ const lite = opts?.lite === true;
53373
+ if (lite) {
53374
+ const origSkipChecks = ctx.retrySkipChecks;
53375
+ const origSkipLLMReviewers = ctx.skipLLMReviewers;
53376
+ ctx.retrySkipChecks = new Set([...ctx.retrySkipChecks ?? [], "adversarial", "semantic"]);
53377
+ ctx.skipLLMReviewers = true;
53378
+ try {
53379
+ await _autofixDeps.runReviewStage(ctx);
53380
+ } finally {
53381
+ ctx.retrySkipChecks = origSkipChecks;
53382
+ ctx.skipLLMReviewers = origSkipLLMReviewers;
53383
+ }
53384
+ return ctx.reviewResult?.success === true;
53385
+ }
52877
53386
  const { reviewStage: reviewStage2 } = await Promise.resolve().then(() => (init_review2(), exports_review));
52878
53387
  if (!reviewStage2.enabled(ctx))
52879
53388
  return true;
52880
- await reviewStage2.execute(ctx);
53389
+ await _autofixDeps.runReviewStage(ctx);
52881
53390
  const hasFailOpen = (ctx.reviewResult?.checks ?? []).some((c) => c.failOpen);
52882
53391
  if (hasFailOpen)
52883
53392
  return false;
52884
53393
  return ctx.reviewResult?.success === true;
52885
53394
  }
53395
+ async function runReviewStage(ctx) {
53396
+ const { reviewStage: reviewStage2 } = await Promise.resolve().then(() => (init_review2(), exports_review));
53397
+ if (!reviewStage2.enabled(ctx))
53398
+ return;
53399
+ await reviewStage2.execute(ctx);
53400
+ }
52886
53401
  async function runMechanicalFixes(ctx, failedCheckNames) {
52887
53402
  const commands = resolveMechanicalFixCommands(ctx, failedCheckNames);
52888
53403
  for (const resolved of commands) {
@@ -53146,6 +53661,7 @@ var init_autofix = __esm(() => {
53146
53661
  runQualityCommand,
53147
53662
  recheckReview,
53148
53663
  runAgentRectification: runAgentRectificationV2,
53664
+ runReviewStage,
53149
53665
  runTestWriterRectification: (ctx, testWriterChecks, story, agentManager) => runTestWriterRectification(ctx, testWriterChecks, story, agentManager)
53150
53666
  };
53151
53667
  });
@@ -54780,455 +55296,6 @@ var init_queue_check = __esm(() => {
54780
55296
  };
54781
55297
  });
54782
55298
 
54783
- // src/execution/escalation/escalation.ts
54784
- function escalateTier(currentTier, tierOrder) {
54785
- const getName = (t) => t.tier ?? t.name ?? null;
54786
- const currentIndex = tierOrder.findIndex((t) => getName(t) === currentTier);
54787
- if (currentIndex === -1 || currentIndex === tierOrder.length - 1) {
54788
- return null;
54789
- }
54790
- const next = tierOrder[currentIndex + 1];
54791
- const nextName = getName(next);
54792
- if (!nextName)
54793
- return null;
54794
- return { tier: nextName, agent: next.agent };
54795
- }
54796
- function calculateMaxIterations(tierOrder) {
54797
- return tierOrder.reduce((sum, t) => sum + t.attempts, 0);
54798
- }
54799
-
54800
- // src/verification/rectification-loop.ts
54801
- async function _defaultRunDebate(storyId, stageConfig, prompt, config2, agentManager) {
54802
- const logger = getSafeLogger();
54803
- const debaters = stageConfig.debaters ?? [];
54804
- const manager = agentManager;
54805
- const resolved = [];
54806
- for (const debater of debaters) {
54807
- if (manager.getAgent(debater.agent)) {
54808
- resolved.push({ debater, agentName: debater.agent });
54809
- }
54810
- }
54811
- if (resolved.length === 0) {
54812
- return { output: null, totalCostUsd: 0 };
54813
- }
54814
- const timeoutMs = (config2.execution?.sessionTimeoutSeconds ?? 600) * 1000;
54815
- const defaultAgentName = resolveDefaultAgent(config2);
54816
- const resolvedDebaters = resolved.map(({ debater, agentName }) => {
54817
- let modelDef;
54818
- try {
54819
- modelDef = resolveConfiguredModel(config2.models, agentName, debater.model ?? "balanced", defaultAgentName).modelDef;
54820
- } catch {
54821
- modelDef = { provider: "unknown", model: debater.model ?? "default" };
54822
- }
54823
- return { debater, agentName, modelDef };
54824
- });
54825
- const startMs = Date.now();
54826
- const proposalSettled = await Promise.allSettled(resolvedDebaters.map(({ agentName, modelDef }) => manager.completeAs(agentName, prompt, {
54827
- modelDef,
54828
- workdir: "",
54829
- storyId,
54830
- sessionRole: "debate-proposal",
54831
- timeoutMs
54832
- }).then((out) => typeof out === "string" ? out : out.output)));
54833
- const durationMs = Date.now() - startMs;
54834
- const successful = proposalSettled.filter((r) => r.status === "fulfilled").map((r) => r.value);
54835
- if (successful.length === 0) {
54836
- return { output: null, totalCostUsd: 0 };
54837
- }
54838
- const successCount = successful.length;
54839
- const costPerDebater = estimateCostByDuration("balanced", durationMs / successCount);
54840
- const totalCostUsd = costPerDebater.cost * successCount;
54841
- logger?.debug("rectification", "debate diagnosis complete", { storyId, successCount, totalCostUsd });
54842
- const output = successful.join(`
54843
-
54844
- `);
54845
- return { output, totalCostUsd };
54846
- }
54847
- async function runRectificationLoop2(opts) {
54848
- const loopStartMs = Date.now();
54849
- const {
54850
- config: config2,
54851
- workdir,
54852
- story,
54853
- testCommand,
54854
- timeoutSeconds,
54855
- testOutput,
54856
- promptPrefix,
54857
- featureName,
54858
- projectDir,
54859
- testScopedTemplate,
54860
- sessionManager,
54861
- sessionId,
54862
- runtime
54863
- } = opts;
54864
- const logger = getSafeLogger();
54865
- const agentManager = opts.agentManager ?? _rectificationDeps.agentManager;
54866
- if (!agentManager) {
54867
- logger?.warn("rectification", "No agentManager threaded \u2014 skipping rectification loop", {
54868
- storyId: story.id
54869
- });
54870
- return { succeeded: false, cost: 0, durationMs: 0 };
54871
- }
54872
- const rectificationConfig = config2.execution.rectification;
54873
- const testSummary = parseTestOutput(testOutput);
54874
- const label = promptPrefix ? "regression rectification" : "rectification";
54875
- const rectificationSessionName = formatSessionName({
54876
- workdir,
54877
- featureName,
54878
- storyId: story.id,
54879
- role: "implementer"
54880
- });
54881
- let costAccum = 0;
54882
- let currentAttempt = 0;
54883
- let heldHandle;
54884
- const initialFailure = {
54885
- testOutput,
54886
- testSummary
54887
- };
54888
- const outcome = await runRetryLoop({
54889
- stage: "rectification",
54890
- storyId: story.id,
54891
- packageDir: workdir,
54892
- maxAttempts: rectificationConfig.maxRetries,
54893
- failure: initialFailure,
54894
- previousAttempts: [],
54895
- buildPrompt: (failure) => {
54896
- currentAttempt++;
54897
- const diagnosisPrefix = null;
54898
- const debateStageConfig = config2.debate?.stages?.rectification;
54899
- let debatePromise = Promise.resolve(null);
54900
- if (debateStageConfig?.enabled) {
54901
- const failureSummary = formatFailureSummary(failure.testSummary.failures);
54902
- const diagnosisPrompt = `Analyze the following test failures and identify the root cause:
54903
-
54904
- ${failureSummary}`;
54905
- debatePromise = (async () => {
54906
- try {
54907
- const debateResult = await _rectificationDeps.runDebate(story.id, debateStageConfig, diagnosisPrompt, config2, agentManager);
54908
- if (debateResult.totalCostUsd > 0 && story.routing) {
54909
- story.routing.estimatedCostUsd = (story.routing.estimatedCostUsd ?? 0) + debateResult.totalCostUsd;
54910
- }
54911
- if (debateResult.output !== null) {
54912
- return `## Root Cause Analysis
54913
-
54914
- ${debateResult.output}`;
54915
- }
54916
- logger?.info("rectification", "debate diagnosis fallback \u2014 all debaters failed", {
54917
- storyId: story.id,
54918
- event: "fallback"
54919
- });
54920
- return null;
54921
- } catch (_error) {
54922
- logger?.info("rectification", "debate diagnosis fallback \u2014 debate threw error", {
54923
- storyId: story.id,
54924
- event: "fallback"
54925
- });
54926
- return null;
54927
- }
54928
- })();
54929
- }
54930
- const failureRecords = buildFailureRecords(failure.testSummary, failure.testOutput);
54931
- const rectPrompt = RectifierPromptBuilder.regressionFailure({
54932
- story,
54933
- failures: failureRecords,
54934
- testCommand,
54935
- conventions: true
54936
- });
54937
- const rectPromise = Promise.resolve(rectPrompt);
54938
- return (async () => {
54939
- const [diagnosis, rectificationPrompt] = await Promise.all([debatePromise, rectPromise]);
54940
- let finalPrompt = rectificationPrompt;
54941
- if (diagnosis) {
54942
- finalPrompt = `${diagnosis}
54943
-
54944
- ${finalPrompt}`;
54945
- }
54946
- if (promptPrefix) {
54947
- finalPrompt = `${promptPrefix}
54948
-
54949
- ${finalPrompt}`;
54950
- }
54951
- return finalPrompt;
54952
- })();
54953
- },
54954
- execute: async (prompt) => {
54955
- const defaultAgent = agentManager.getDefault();
54956
- const complexity = story.routing?.complexity ?? "medium";
54957
- const modelTier = config2.autoMode.complexityRouting?.[complexity] || config2.autoMode.escalation.tierOrder[0]?.tier || "balanced";
54958
- const modelDef = resolveModelForAgent(config2.models, story.routing?.agent ?? defaultAgent, modelTier, defaultAgent);
54959
- const isLastAttempt = currentAttempt >= rectificationConfig.maxRetries;
54960
- const runOptions = {
54961
- prompt,
54962
- workdir,
54963
- modelTier,
54964
- modelDef,
54965
- timeoutSeconds: config2.execution.sessionTimeoutSeconds,
54966
- pipelineStage: "rectification",
54967
- config: config2,
54968
- projectDir,
54969
- maxInteractionTurns: config2.agent?.maxInteractionTurns,
54970
- featureName,
54971
- storyId: story.id,
54972
- sessionRole: "implementer"
54973
- };
54974
- let agentResult;
54975
- if (runtime) {
54976
- let transportRetries = 0;
54977
- const maxTransportRetries = config2.execution?.sessionErrorRetryableMaxRetries ?? 3;
54978
- while (true) {
54979
- if (!heldHandle) {
54980
- heldHandle = await runtime.sessionManager.openSession(rectificationSessionName, {
54981
- agentName: defaultAgent,
54982
- role: "implementer",
54983
- workdir,
54984
- pipelineStage: "rectification",
54985
- modelDef,
54986
- timeoutSeconds: config2.execution.sessionTimeoutSeconds,
54987
- featureName,
54988
- storyId: story.id,
54989
- signal: runtime.signal
54990
- });
54991
- }
54992
- try {
54993
- const turn = await agentManager.runAsSession(defaultAgent, heldHandle, prompt, {
54994
- storyId: story.id,
54995
- featureName,
54996
- workdir,
54997
- projectDir,
54998
- pipelineStage: "rectification",
54999
- sessionRole: "implementer",
55000
- signal: runtime.signal,
55001
- maxTurns: config2.agent?.maxInteractionTurns
55002
- });
55003
- agentResult = {
55004
- success: true,
55005
- exitCode: 0,
55006
- output: turn.output,
55007
- rateLimited: false,
55008
- durationMs: 0,
55009
- estimatedCostUsd: turn.estimatedCostUsd,
55010
- ...turn.exactCostUsd !== undefined && { exactCostUsd: turn.exactCostUsd },
55011
- ...turn.tokenUsage && { tokenUsage: turn.tokenUsage },
55012
- ...heldHandle.protocolIds && { protocolIds: heldHandle.protocolIds }
55013
- };
55014
- break;
55015
- } catch (err) {
55016
- const stale = heldHandle;
55017
- heldHandle = undefined;
55018
- await runtime.sessionManager.closeSession(stale).catch(() => {});
55019
- if (err instanceof SessionTurnError && err.retryable && transportRetries < maxTransportRetries) {
55020
- transportRetries++;
55021
- getSafeLogger()?.warn("rectification", "fail-adapter-error: same-agent retry with fresh session", {
55022
- storyId: story.id,
55023
- attempt: transportRetries,
55024
- maxAttempts: maxTransportRetries,
55025
- retriable: true
55026
- });
55027
- continue;
55028
- }
55029
- throw err;
55030
- }
55031
- }
55032
- } else {
55033
- agentResult = await agentManager.run({
55034
- runOptions: { ...runOptions, keepOpen: !isLastAttempt }
55035
- });
55036
- }
55037
- costAccum += agentResult.estimatedCostUsd ?? 0;
55038
- if (sessionManager && sessionId && agentResult.protocolIds) {
55039
- try {
55040
- sessionManager.bindHandle(sessionId, rectificationSessionName, agentResult.protocolIds);
55041
- } catch {}
55042
- }
55043
- if (agentResult.success) {
55044
- logger?.info("rectification", `Agent ${label} session complete`, {
55045
- storyId: story.id,
55046
- cost: agentResult.estimatedCostUsd
55047
- });
55048
- } else {
55049
- logger?.warn("rectification", `Agent ${label} session failed`, {
55050
- storyId: story.id,
55051
- exitCode: agentResult.exitCode
55052
- });
55053
- }
55054
- return {
55055
- agentSuccess: agentResult.success,
55056
- cost: agentResult.estimatedCostUsd ?? 0,
55057
- protocolIds: agentResult.protocolIds
55058
- };
55059
- },
55060
- shouldAbort: (failure) => {
55061
- if (rectificationConfig.abortOnIncreasingFailures) {
55062
- return failure.testSummary.failed > initialFailure.testSummary.failed;
55063
- }
55064
- return false;
55065
- },
55066
- verify: async (result) => {
55067
- const retryVerification = await _rectificationDeps.runVerification({
55068
- workdir,
55069
- expectedFiles: getExpectedFiles(story),
55070
- command: testCommand,
55071
- timeoutSeconds,
55072
- forceExit: config2.quality.forceExit,
55073
- detectOpenHandles: config2.quality.detectOpenHandles,
55074
- detectOpenHandlesRetries: config2.quality.detectOpenHandlesRetries,
55075
- timeoutRetryCount: 0,
55076
- gracePeriodMs: config2.quality.gracePeriodMs,
55077
- drainTimeoutMs: config2.quality.drainTimeoutMs,
55078
- shell: config2.quality.shell,
55079
- stripEnvVars: config2.quality.stripEnvVars
55080
- });
55081
- if (retryVerification.success) {
55082
- logger?.info("rectification", `[OK] ${label} succeeded!`, {
55083
- storyId: story.id,
55084
- attempt: currentAttempt,
55085
- initialFailures: initialFailure.testSummary.failed
55086
- });
55087
- return { passed: true };
55088
- }
55089
- if (retryVerification.output) {
55090
- const newTestSummary = parseTestOutput(retryVerification.output);
55091
- if (newTestSummary.failed === 0 && retryVerification.status !== "ENVIRONMENTAL_FAILURE" && (retryVerification.status === "SUCCESS" || newTestSummary.passed > 0)) {
55092
- logger?.info("rectification", `[OK] ${label} succeeded after parsing retry output`, {
55093
- storyId: story.id,
55094
- attempt: currentAttempt,
55095
- initialFailures: initialFailure.testSummary.failed
55096
- });
55097
- return { passed: true };
55098
- }
55099
- const failingTests = newTestSummary.failures.slice(0, 10).map((failure) => failure.testName);
55100
- const logData = {
55101
- storyId: story.id,
55102
- attempt: currentAttempt,
55103
- remainingFailures: newTestSummary.failed,
55104
- failingTests
55105
- };
55106
- if (newTestSummary.failures.length > 10 || newTestSummary.failures.length === 0 && newTestSummary.failed > 0) {
55107
- logData.totalFailingTests = newTestSummary.failed;
55108
- }
55109
- logger?.warn("rectification", `${label} still failing after attempt`, logData);
55110
- return {
55111
- passed: false,
55112
- newFailure: {
55113
- testOutput: retryVerification.output,
55114
- testSummary: newTestSummary
55115
- }
55116
- };
55117
- }
55118
- return {
55119
- passed: false,
55120
- newFailure: {
55121
- testOutput: retryVerification.output ?? "",
55122
- testSummary: initialFailure.testSummary
55123
- }
55124
- };
55125
- }
55126
- }).finally(async () => {
55127
- if (heldHandle && runtime) {
55128
- const stale = heldHandle;
55129
- heldHandle = undefined;
55130
- await runtime.sessionManager.closeSession(stale).catch(() => {});
55131
- }
55132
- });
55133
- const succeeded = outcome.outcome === "fixed";
55134
- if (outcome.outcome === "exhausted") {
55135
- const finalFailure = outcome.finalFailure;
55136
- const shouldEscalate = rectificationConfig.escalateOnExhaustion !== false && config2.autoMode?.escalation?.enabled === true && finalFailure.testSummary.failed > 0;
55137
- if (shouldEscalate) {
55138
- const complexity = story.routing?.complexity ?? "medium";
55139
- const currentTier = config2.autoMode.complexityRouting?.[complexity] || config2.autoMode.escalation.tierOrder[0]?.tier || "balanced";
55140
- const tierOrder = config2.autoMode.escalation.tierOrder;
55141
- const escalationResult = _rectificationDeps.escalateTier(currentTier, tierOrder);
55142
- const escalatedTier = escalationResult?.tier ?? null;
55143
- const escalatedAgent = escalationResult?.agent;
55144
- if (escalatedTier !== null) {
55145
- const escalationManager = agentManager;
55146
- const agentName = escalatedAgent ?? story.routing?.agent ?? escalationManager.getDefault();
55147
- if (escalationManager.getAgent(agentName)) {
55148
- const escalatedModelDef = resolveModelForAgent(config2.models, agentName, escalatedTier, escalationManager.getDefault());
55149
- let escalationPrompt = RectifierPromptBuilder.escalated(finalFailure.testSummary.failures, story, outcome.attempts, currentTier, escalatedTier, rectificationConfig, testCommand, testScopedTemplate);
55150
- if (promptPrefix) {
55151
- escalationPrompt = `${promptPrefix}
55152
-
55153
- ${escalationPrompt}`;
55154
- }
55155
- const escalationRunResult = await escalationManager.runAs(agentName, {
55156
- runOptions: {
55157
- prompt: escalationPrompt,
55158
- workdir,
55159
- modelTier: escalatedTier,
55160
- modelDef: escalatedModelDef,
55161
- timeoutSeconds: config2.execution.sessionTimeoutSeconds,
55162
- pipelineStage: "rectification",
55163
- config: config2,
55164
- projectDir,
55165
- maxInteractionTurns: config2.agent?.maxInteractionTurns,
55166
- featureName,
55167
- storyId: story.id,
55168
- sessionRole: "implementer"
55169
- }
55170
- });
55171
- costAccum += escalationRunResult.estimatedCostUsd ?? 0;
55172
- logger?.info("rectification", "escalated rectification attempt cost", {
55173
- storyId: story.id,
55174
- escalatedTier,
55175
- cost: escalationRunResult.estimatedCostUsd
55176
- });
55177
- const escalationVerification = await _rectificationDeps.runVerification({
55178
- workdir,
55179
- expectedFiles: getExpectedFiles(story),
55180
- command: testCommand,
55181
- timeoutSeconds,
55182
- forceExit: config2.quality.forceExit,
55183
- detectOpenHandles: config2.quality.detectOpenHandles,
55184
- detectOpenHandlesRetries: config2.quality.detectOpenHandlesRetries,
55185
- timeoutRetryCount: 0,
55186
- gracePeriodMs: config2.quality.gracePeriodMs,
55187
- drainTimeoutMs: config2.quality.drainTimeoutMs,
55188
- shell: config2.quality.shell,
55189
- stripEnvVars: config2.quality.stripEnvVars
55190
- });
55191
- if (escalationVerification.success) {
55192
- logger?.info("rectification", `${label} escalated from ${currentTier} to ${escalatedTier} and succeeded`, {
55193
- storyId: story.id,
55194
- currentTier,
55195
- escalatedTier
55196
- });
55197
- return { succeeded: true, cost: costAccum, durationMs: Date.now() - loopStartMs };
55198
- }
55199
- logger?.warn("rectification", "escalated rectification also failed", { storyId: story.id, escalatedTier });
55200
- }
55201
- }
55202
- }
55203
- logger?.warn("rectification", `${label} exhausted max retries`, {
55204
- storyId: story.id,
55205
- attempts: outcome.attempts,
55206
- remainingFailures: initialFailure.testSummary.failed
55207
- });
55208
- }
55209
- return { succeeded, cost: costAccum, durationMs: Date.now() - loopStartMs };
55210
- }
55211
- var _rectificationDeps;
55212
- var init_rectification_loop = __esm(() => {
55213
- init_agents();
55214
- init_cost();
55215
- init_types3();
55216
- init_config();
55217
- init_logger2();
55218
- init_prd();
55219
- init_prompts();
55220
- init_naming();
55221
- init_parser2();
55222
- init_parser2();
55223
- init_runners();
55224
- _rectificationDeps = {
55225
- agentManager: undefined,
55226
- runVerification: fullSuite,
55227
- escalateTier,
55228
- runDebate: _defaultRunDebate
55229
- };
55230
- });
55231
-
55232
55299
  // src/pipeline/stages/rectify.ts
55233
55300
  var rectifyStage, _rectifyDeps;
55234
55301
  var init_rectify2 = __esm(() => {
@@ -55327,7 +55394,7 @@ var init_rectify2 = __esm(() => {
55327
55394
  }
55328
55395
  };
55329
55396
  _rectifyDeps = {
55330
- runRectificationLoop: (ctx, opts) => runRectificationLoop2({
55397
+ runRectificationLoop: (ctx, opts) => runRectificationLoop({
55331
55398
  config: ctx.config,
55332
55399
  workdir: ctx.workdir,
55333
55400
  story: ctx.story,
@@ -55339,7 +55406,7 @@ var init_rectify2 = __esm(() => {
55339
55406
  agentManager: ctx.agentManager,
55340
55407
  projectDir: ctx.projectDir,
55341
55408
  testScopedTemplate: opts.testScopedTemplate,
55342
- sessionManager: ctx.sessionManager,
55409
+ runtime: ctx.runtime,
55343
55410
  sessionId: ctx.sessionId
55344
55411
  }),
55345
55412
  resolveTestCommands: resolveQualityTestCommands,
@@ -56190,7 +56257,7 @@ async function promptsInitCommand(options) {
56190
56257
  mkdirSync4(templatesDir, { recursive: true });
56191
56258
  const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(join50(templatesDir, f)));
56192
56259
  if (existingFiles.length > 0 && !force) {
56193
- console.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
56260
+ _promptsInitDeps.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
56194
56261
  Pass --force to overwrite existing templates.`);
56195
56262
  return [];
56196
56263
  }
@@ -56202,9 +56269,9 @@ async function promptsInitCommand(options) {
56202
56269
  await Bun.write(filePath, content);
56203
56270
  written.push(filePath);
56204
56271
  }
56205
- console.log(`[OK] Written ${written.length} template files to nax/templates/:`);
56272
+ _promptsInitDeps.log(`[OK] Written ${written.length} template files to nax/templates/:`);
56206
56273
  for (const filePath of written) {
56207
- console.log(` - ${filePath.replace(`${workdir}/`, "")}`);
56274
+ _promptsInitDeps.log(` - ${filePath.replace(`${workdir}/`, "")}`);
56208
56275
  }
56209
56276
  if (autoWireConfig) {
56210
56277
  await autoWirePromptsConfig(workdir);
@@ -56225,7 +56292,7 @@ async function autoWirePromptsConfig(workdir) {
56225
56292
  }
56226
56293
  }
56227
56294
  }, null, 2);
56228
- console.log(`
56295
+ _promptsInitDeps.log(`
56229
56296
  No nax.config.json found. To activate overrides, create nax/config.json with:
56230
56297
  ${exampleConfig}`);
56231
56298
  return;
@@ -56234,7 +56301,7 @@ ${exampleConfig}`);
56234
56301
  const configContent = await configFile.text();
56235
56302
  const config2 = JSON.parse(configContent);
56236
56303
  if (config2.prompts?.overrides && Object.keys(config2.prompts.overrides).length > 0) {
56237
- console.log(`[INFO] prompts.overrides already configured in nax.config.json. Skipping auto-wiring.
56304
+ _promptsInitDeps.log(`[INFO] prompts.overrides already configured in nax.config.json. Skipping auto-wiring.
56238
56305
  ` + " To reset overrides, remove the prompts.overrides section and re-run this command.");
56239
56306
  return;
56240
56307
  }
@@ -56251,7 +56318,7 @@ ${exampleConfig}`);
56251
56318
  config2.prompts.overrides = overrides;
56252
56319
  const updatedConfig = formatConfigJson(config2);
56253
56320
  await Bun.write(configPath, updatedConfig);
56254
- console.log("[OK] Auto-wired prompts.overrides in nax.config.json");
56321
+ _promptsInitDeps.log("[OK] Auto-wired prompts.overrides in nax.config.json");
56255
56322
  }
56256
56323
  function formatConfigJson(config2) {
56257
56324
  const lines = ["{"];
@@ -56276,7 +56343,7 @@ function formatConfigJson(config2) {
56276
56343
  return lines.join(`
56277
56344
  `);
56278
56345
  }
56279
- var TEMPLATE_ROLES, TEMPLATE_HEADER = `<!--
56346
+ var _promptsInitDeps, TEMPLATE_ROLES, TEMPLATE_HEADER = `<!--
56280
56347
  This file controls the role-body section of the nax prompt for this role.
56281
56348
  Edit the content below to customize the task instructions given to the agent.
56282
56349
 
@@ -56292,6 +56359,10 @@ var TEMPLATE_ROLES, TEMPLATE_HEADER = `<!--
56292
56359
  `;
56293
56360
  var init_prompts_init = __esm(() => {
56294
56361
  init_role_task();
56362
+ _promptsInitDeps = {
56363
+ log: console.log.bind(console),
56364
+ warn: console.warn.bind(console)
56365
+ };
56295
56366
  TEMPLATE_ROLES = [
56296
56367
  { file: "test-writer.md", role: "test-writer" },
56297
56368
  { file: "implementer.md", role: "implementer", variant: "standard" },
@@ -56878,7 +56949,8 @@ __export(exports_init, {
56878
56949
  validateProjectName: () => validateProjectName,
56879
56950
  initProject: () => initProject,
56880
56951
  initCommand: () => initCommand,
56881
- checkInitCollision: () => checkInitCollision
56952
+ checkInitCollision: () => checkInitCollision,
56953
+ _initDeps: () => _initDeps
56882
56954
  });
56883
56955
  import { existsSync as existsSync24 } from "fs";
56884
56956
  import { mkdir as mkdir10 } from "fs/promises";
@@ -57105,20 +57177,20 @@ async function initProject(projectRoot, options) {
57105
57177
  }
57106
57178
  await updateGitignore(projectRoot);
57107
57179
  await promptsInitCommand({ workdir: projectRoot, force: false, autoWireConfig: false });
57108
- console.log(`
57180
+ _initDeps.log(`
57109
57181
  [OK] nax init complete. Created files:`);
57110
- console.log(" - .nax/config.json");
57111
- console.log(" - .nax/context.md");
57112
- console.log(" - .nax/constitution.md");
57113
- console.log(" - .nax/hooks/");
57114
- console.log(" - .nax/templates/");
57115
- console.log(`
57182
+ _initDeps.log(" - .nax/config.json");
57183
+ _initDeps.log(" - .nax/context.md");
57184
+ _initDeps.log(" - .nax/constitution.md");
57185
+ _initDeps.log(" - .nax/hooks/");
57186
+ _initDeps.log(" - .nax/templates/");
57187
+ _initDeps.log(`
57116
57188
  Next steps:`);
57117
- console.log(" 1. Review .nax/context.md and fill in TODOs");
57118
- console.log(" 2. Review .nax/config.json and adjust quality commands");
57119
- console.log(" 3. Run: nax generate");
57120
- console.log(" 4. Run: nax plan");
57121
- console.log(" 5. Run: nax run");
57189
+ _initDeps.log(" 1. Review .nax/context.md and fill in TODOs");
57190
+ _initDeps.log(" 2. Review .nax/config.json and adjust quality commands");
57191
+ _initDeps.log(" 3. Run: nax generate");
57192
+ _initDeps.log(" 4. Run: nax plan");
57193
+ _initDeps.log(" 5. Run: nax run");
57122
57194
  logger.info("init", "Project config initialized successfully", { path: projectDir });
57123
57195
  }
57124
57196
  async function initCommand(options = {}) {
@@ -57127,19 +57199,19 @@ async function initCommand(options = {}) {
57127
57199
  } else if (options.package) {
57128
57200
  const projectRoot = options.projectRoot ?? process.cwd();
57129
57201
  await initPackage(projectRoot, options.package);
57130
- console.log(`
57202
+ _initDeps.log(`
57131
57203
  [OK] Package scaffold created.`);
57132
- console.log(` Created: .nax/mono/${options.package}/context.md`);
57133
- console.log(`
57204
+ _initDeps.log(` Created: .nax/mono/${options.package}/context.md`);
57205
+ _initDeps.log(`
57134
57206
  Next steps:`);
57135
- console.log(` 1. Review .nax/mono/${options.package}/context.md and fill in TODOs`);
57136
- console.log(` 2. Run: nax generate --package ${options.package}`);
57207
+ _initDeps.log(` 1. Review .nax/mono/${options.package}/context.md and fill in TODOs`);
57208
+ _initDeps.log(` 2. Run: nax generate --package ${options.package}`);
57137
57209
  } else {
57138
57210
  const projectRoot = options.projectRoot ?? process.cwd();
57139
57211
  await initProject(projectRoot, { name: options.name, force: options.force });
57140
57212
  }
57141
57213
  }
57142
- var MINIMAL_GLOBAL_CONFIG;
57214
+ var _initDeps, MINIMAL_GLOBAL_CONFIG;
57143
57215
  var init_init2 = __esm(() => {
57144
57216
  init_paths();
57145
57217
  init_errors();
@@ -57149,6 +57221,9 @@ var init_init2 = __esm(() => {
57149
57221
  init_init_context();
57150
57222
  init_init_detect();
57151
57223
  init_prompts2();
57224
+ _initDeps = {
57225
+ log: console.log.bind(console)
57226
+ };
57152
57227
  MINIMAL_GLOBAL_CONFIG = {
57153
57228
  version: 1
57154
57229
  };
@@ -58789,7 +58864,7 @@ var package_default;
58789
58864
  var init_package = __esm(() => {
58790
58865
  package_default = {
58791
58866
  name: "@nathapp/nax",
58792
- version: "0.67.0-canary.3",
58867
+ version: "0.67.0-canary.4",
58793
58868
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
58794
58869
  type: "module",
58795
58870
  bin: {
@@ -58799,7 +58874,7 @@ var init_package = __esm(() => {
58799
58874
  prepare: "git config core.hooksPath .githooks",
58800
58875
  dev: "bun run bin/nax.ts",
58801
58876
  build: 'bun build bin/nax.ts --outdir dist --target bun --define "GIT_COMMIT=\\"$(git rev-parse --short HEAD)\\""',
58802
- typecheck: "bun x tsc --noEmit",
58877
+ typecheck: "bun x tsc --noEmit && bun x tsc --noEmit -p tsconfig.contracts.json",
58803
58878
  lint: "bun x biome check src/ bin/ && bun run check:no-real-global-nax && bun run check:alias-internals && bun run check:deep-relatives && bun run check:nax-error && bun run check:logger-storyid",
58804
58879
  "lint:json": "bun x biome check src/ bin/ --reporter json && bun run check:nax-error 1>&2 && bun run check:logger-storyid 1>&2",
58805
58880
  "lint:fix": "bun x biome check --write src/ bin/",
@@ -58812,8 +58887,10 @@ var init_package = __esm(() => {
58812
58887
  "check:logger-storyid": "bun run scripts/check-logger-storyid.ts",
58813
58888
  "check:logger-storyid:update": "bun run scripts/check-logger-storyid.ts --update-baseline",
58814
58889
  release: "bun scripts/release.ts",
58815
- test: "bun run scripts/run-tests.ts",
58816
- "test:bail": "bun run scripts/run-tests.ts --bail",
58890
+ test: "AGENT=1 bun run scripts/run-tests.ts",
58891
+ "test:verbose": "bun run scripts/run-tests.ts",
58892
+ "test:bail": "AGENT=1 bun run scripts/run-tests.ts --bail",
58893
+ "test:bail-verbose": "bun run scripts/run-tests.ts --bail",
58817
58894
  "test:no-wrap": "bun test test/unit/ --timeout=5000 && bun test test/integration/ --timeout=5000 && bun test test/ui/ --timeout=5000",
58818
58895
  "test:watch": "bun test --watch",
58819
58896
  "test:unit": "bun test ./test/unit/ --timeout=60000",
@@ -58882,8 +58959,8 @@ var init_version = __esm(() => {
58882
58959
  NAX_VERSION = package_default.version;
58883
58960
  NAX_COMMIT = (() => {
58884
58961
  try {
58885
- if (/^[0-9a-f]{6,10}$/.test("1ca4b2e1"))
58886
- return "1ca4b2e1";
58962
+ if (/^[0-9a-f]{6,10}$/.test("22d1f67c"))
58963
+ return "22d1f67c";
58887
58964
  } catch {}
58888
58965
  try {
58889
58966
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -59607,7 +59684,7 @@ async function runAcceptanceFixCycle(ctx, prd, initialFailures, diagnosis, accep
59607
59684
  coRun: "co-run-sequential"
59608
59685
  }
59609
59686
  ],
59610
- validate: async (_ctx) => {
59687
+ validate: async (_ctx, _opts) => {
59611
59688
  const result = await runAcceptanceTestsOnce(ctx, prd);
59612
59689
  if (result.passed)
59613
59690
  return [];
@@ -59825,7 +59902,7 @@ async function findResponsibleStory(testFile, workdir, passedStories) {
59825
59902
  }
59826
59903
  async function runDeferredRegression(options) {
59827
59904
  const logger = getSafeLogger();
59828
- const { config: config2, prd, workdir, agentManager } = options;
59905
+ const { config: config2, prd, workdir, runtime } = options;
59829
59906
  const regressionMode = config2.execution.regressionGate?.mode ?? "deferred";
59830
59907
  if (regressionMode === "disabled") {
59831
59908
  logger?.info("regression", "Deferred regression gate disabled");
@@ -60015,8 +60092,9 @@ async function runDeferredRegression(options) {
60015
60092
  promptPrefix: `# DEFERRED REGRESSION: Full-Suite Failures
60016
60093
 
60017
60094
  Your story ${story.id} broke tests in the full suite. Fix these regressions.`,
60018
- agentManager,
60019
- featureName: prd.feature
60095
+ agentManager: runtime.agentManager,
60096
+ featureName: prd.feature,
60097
+ runtime
60020
60098
  });
60021
60099
  storyCostAccum[story.id] = (storyCostAccum[story.id] ?? 0) + rectResult.cost;
60022
60100
  storyDurationAccum[story.id] = (storyDurationAccum[story.id] ?? 0) + rectResult.durationMs;
@@ -60095,7 +60173,7 @@ var init_run_regression = __esm(() => {
60095
60173
  init_runners();
60096
60174
  _regressionDeps = {
60097
60175
  runVerification: fullSuite,
60098
- runRectificationLoop: runRectificationLoop2,
60176
+ runRectificationLoop,
60099
60177
  parseTestOutput
60100
60178
  };
60101
60179
  });
@@ -60149,7 +60227,7 @@ async function handleRunCompletion(options) {
60149
60227
  config: config2,
60150
60228
  prd,
60151
60229
  workdir,
60152
- agentManager: options.agentManager
60230
+ runtime: options.runtime
60153
60231
  });
60154
60232
  const lastRunAt = new Date().toISOString();
60155
60233
  logger?.info("regression", "Deferred regression gate completed", {
@@ -98394,6 +98472,7 @@ async function extractRunSummary(filePath) {
98394
98472
 
98395
98473
  // src/commands/logs-formatter.ts
98396
98474
  var LOG_LEVEL_PRIORITY2 = {
98475
+ silent: -1,
98397
98476
  debug: 0,
98398
98477
  info: 1,
98399
98478
  warn: 2,
@@ -99277,6 +99356,13 @@ init_helpers();
99277
99356
  init_crash_recovery();
99278
99357
  init_pid_registry();
99279
99358
 
99359
+ // src/execution/lifecycle/index.ts
99360
+ init_acceptance_loop();
99361
+ init_headless_formatter();
99362
+ init_run_completion();
99363
+ init_run_cleanup();
99364
+ init_run_setup();
99365
+ init_run_regression();
99280
99366
  // bin/nax.ts
99281
99367
  init_hooks();
99282
99368
  init_logger2();