@nathapp/nax 0.70.0-canary.5 → 0.70.0-canary.7

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 +109 -32
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -16828,7 +16828,8 @@ var init_schemas_execution = __esm(() => {
16828
16828
  escalation: exports_external.object({
16829
16829
  enabled: exports_external.boolean(),
16830
16830
  tierOrder: exports_external.array(TierConfigSchema).min(1, { message: "tierOrder must have at least one tier" }),
16831
- escalateEntireBatch: exports_external.boolean().optional()
16831
+ escalateEntireBatch: exports_external.boolean().optional(),
16832
+ resetMode: exports_external.enum(["initial", "last"]).default("initial")
16832
16833
  })
16833
16834
  });
16834
16835
  RectificationConfigSchema = exports_external.object({
@@ -16872,7 +16873,7 @@ var init_schemas_execution = __esm(() => {
16872
16873
  }).superRefine((value, ctx) => {
16873
16874
  if (value.mode !== "provision" && value.setupCommand !== null) {
16874
16875
  ctx.addIssue({
16875
- code: exports_external.ZodIssueCode.custom,
16876
+ code: "custom",
16876
16877
  path: ["setupCommand"],
16877
16878
  message: "execution.worktreeDependencies.setupCommand requires mode 'provision'"
16878
16879
  });
@@ -17354,7 +17355,8 @@ var init_schemas3 = __esm(() => {
17354
17355
  { tier: "balanced", attempts: 3 },
17355
17356
  { tier: "powerful", attempts: 2 }
17356
17357
  ],
17357
- escalateEntireBatch: true
17358
+ escalateEntireBatch: true,
17359
+ resetMode: "initial"
17358
17360
  }
17359
17361
  }),
17360
17362
  routing: RoutingConfigSchema.default({
@@ -27956,7 +27958,8 @@ var init_static_rules = __esm(() => {
27956
27958
 
27957
27959
  // src/prd/types.ts
27958
27960
  function getContextFiles(story) {
27959
- const files = story.contextFiles ?? story.relevantFiles ?? [];
27961
+ const legacyFiles = story.relevantFiles;
27962
+ const files = story.contextFiles ?? legacyFiles ?? [];
27960
27963
  return files.map((f) => typeof f === "string" ? f : f.path);
27961
27964
  }
27962
27965
  function getExpectedFiles(story) {
@@ -28468,16 +28471,28 @@ function markStoryFailed(prd, storyId, failureCategory, failureStage, statusWrit
28468
28471
  }
28469
28472
  }
28470
28473
  }
28471
- function resetFailedStoriesToPending(prd, resetRef = false, storyIsolation) {
28474
+ function resetFailedStoriesToPending(prd, opts = {}) {
28475
+ const { resetRef = false, storyIsolation, resetMode = "initial" } = opts;
28472
28476
  const reset = [];
28473
28477
  for (const story of prd.userStories) {
28474
- if (story.status === "failed") {
28475
- story.status = "pending";
28476
- if (resetRef || storyIsolation === "worktree") {
28477
- story.storyGitRef = undefined;
28478
- }
28479
- reset.push(story);
28478
+ if (story.status !== "failed")
28479
+ continue;
28480
+ story.status = "pending";
28481
+ story.attempts = 0;
28482
+ if (resetMode === "initial" && story.routing) {
28483
+ story.routing = {
28484
+ ...story.routing,
28485
+ ...story.routing.initialModelTier !== undefined && {
28486
+ modelTier: story.routing.initialModelTier
28487
+ },
28488
+ agent: story.routing.initialAgent
28489
+ };
28490
+ story.escalations = [];
28491
+ }
28492
+ if (resetRef || storyIsolation === "worktree") {
28493
+ story.storyGitRef = undefined;
28480
28494
  }
28495
+ reset.push(story);
28481
28496
  }
28482
28497
  return reset;
28483
28498
  }
@@ -29675,10 +29690,12 @@ function formatPriorFailures(failures) {
29675
29690
  return "";
29676
29691
  }
29677
29692
  const parts = [];
29678
- parts.push(`## Prior Failures (Structured Context)
29679
- `);
29680
29693
  for (const failure of failures) {
29681
29694
  parts.push(`### Attempt ${failure.attempt} \u2014 ${failure.modelTier}`);
29695
+ if (failure.agent) {
29696
+ const profilePart = failure.agentProfileId ? ` (profile: ${failure.agentProfileId})` : "";
29697
+ parts.push(`**Agent:** ${failure.agent}${profilePart}`);
29698
+ }
29682
29699
  parts.push(`**Stage:** ${failure.stage}`);
29683
29700
  parts.push(`**Summary:** ${failure.summary}`);
29684
29701
  if (failure.testFailures && failure.testFailures.length > 0) {
@@ -29784,10 +29801,14 @@ function formatContextAsMarkdown(built) {
29784
29801
  byType.set(element.type, existing);
29785
29802
  }
29786
29803
  renderSection(sections, byType, "progress", `## Progress
29804
+ `, renderSimple);
29805
+ renderSection(sections, byType, "prior-failures", `## Prior Failures (Structured Context)
29787
29806
  `, renderSimple);
29788
29807
  renderErrorSection(sections, byType);
29789
29808
  renderSection(sections, byType, "test-coverage", "", renderSimple);
29790
29809
  renderSection(sections, byType, "story", `## Current Story
29810
+ `, renderSimple);
29811
+ renderSection(sections, byType, "planning-analysis", `## Planning Analysis
29791
29812
  `, renderSimple);
29792
29813
  renderSection(sections, byType, "dependency", `## Dependency Stories
29793
29814
  `, renderSimple);
@@ -36411,7 +36432,7 @@ var init_acceptance_refine = __esm(() => {
36411
36432
  jsonMode: true,
36412
36433
  config: acceptanceConfigSelector,
36413
36434
  retry: { preset: "transient-network", maxAttempts: 2, baseDelayMs: 0 },
36414
- model: (_input, ctx) => ctx.config.acceptance.generateModel ?? ctx.config.acceptance.model,
36435
+ model: (_input, ctx) => ctx.config.acceptance.model,
36415
36436
  timeoutMs: (_input, ctx) => ctx.config.acceptance.timeoutMs,
36416
36437
  build(input, _ctx) {
36417
36438
  const prompt = new AcceptancePromptBuilder().buildRefinementPrompt(input.criteria, input.codebaseContext, {
@@ -52512,6 +52533,7 @@ async function collectStoryMetrics(ctx, storyStartTime) {
52512
52533
  startedAt: storyStartTime,
52513
52534
  completedAt: new Date().toISOString(),
52514
52535
  fullSuiteGatePassed,
52536
+ ...ctx.fullSuiteGateFailingFiles && ctx.fullSuiteGateFailingFiles.length > 0 ? { failingTestFiles: ctx.fullSuiteGateFailingFiles } : {},
52515
52537
  runtimeCrashes: ctx.storyRuntimeCrashes ?? 0,
52516
52538
  tokens: agentResult?.tokenUsage ? new TokenUsage({
52517
52539
  inputTokens: agentResult.tokenUsage.inputTokens,
@@ -55044,6 +55066,15 @@ function extractPhaseFindings(output) {
55044
55066
  const success2 = "success" in record2 ? record2.success === true : ("passed" in record2) ? record2.passed === true : findings.length === 0;
55045
55067
  return success2 ? [] : findings;
55046
55068
  }
55069
+ function gateFailureKeys(gateOutput) {
55070
+ const keys = new Set;
55071
+ for (const f of extractPhaseFindings(gateOutput)) {
55072
+ if (f.source !== "test-runner")
55073
+ continue;
55074
+ keys.add(`${f.file ?? ""}::${f.rule ?? ""}`);
55075
+ }
55076
+ return keys;
55077
+ }
55047
55078
  function shouldSkipPhaseForRectification(phase, state, phaseOutputs) {
55048
55079
  if (phase.kind !== "full-suite-gate")
55049
55080
  return false;
@@ -55433,6 +55464,8 @@ class ExecutionPlan {
55433
55464
  break;
55434
55465
  }
55435
55466
  }
55467
+ const gateName = this.state.fullSuiteGate?.slot.op.name;
55468
+ const preRectGateFailureKeys = gateName ? gateFailureKeys(phaseOutputs[gateName]) : new Set;
55436
55469
  const rectResult = await runRectification(this.ctx, this.state, phaseCosts, phaseOutputs);
55437
55470
  if (this.state.rectification && (!rectResult.rectificationExhausted || rectResult.liteScopeIncomplete)) {
55438
55471
  let resumeRectifyUsed = false;
@@ -55526,9 +55559,12 @@ class ExecutionPlan {
55526
55559
  });
55527
55560
  }
55528
55561
  const verifierName = this.state.verifier?.slot.op.name;
55529
- const gateName = this.state.fullSuiteGate?.slot.op.name;
55530
- const verifierPassedSsot = verifierName !== undefined && phaseExplicitlyPassed(phaseOutputs[verifierName]);
55531
- if (verifierPassedSsot && gateName !== undefined && !phasePassed(gateName, phaseOutputs[gateName], this.ctx.storyId)) {
55562
+ const verifierExplicitlyPassed = verifierName !== undefined && phaseExplicitlyPassed(phaseOutputs[verifierName]);
55563
+ const gateRegressedDuringRect = gateName !== undefined && [...gateFailureKeys(phaseOutputs[gateName])].some((k) => !preRectGateFailureKeys.has(k));
55564
+ const verifierPassedSsot = verifierExplicitlyPassed && !gateRegressedDuringRect;
55565
+ if (verifierExplicitlyPassed && gateRegressedDuringRect) {
55566
+ logger?.warn("story-orchestrator", "Gate regressed during rectification after verifier passed \u2014 verifier verdict is stale, failing story", { storyId: this.ctx.storyId, packageDir: this.ctx.packageDir });
55567
+ } else if (verifierPassedSsot && gateName !== undefined && !phasePassed(gateName, phaseOutputs[gateName], this.ctx.storyId)) {
55532
55568
  logger?.warn("story-orchestrator", "Full-suite gate failed but verifier judged story OK \u2014 treating gate failures as unrelated regressions", { storyId: this.ctx.storyId, packageDir: this.ctx.packageDir });
55533
55569
  }
55534
55570
  const success2 = Object.entries(phaseOutputs).every(([name, output]) => {
@@ -55566,7 +55602,8 @@ class ExecutionPlan {
55566
55602
  totalCostUsd,
55567
55603
  durationMs,
55568
55604
  phaseOutputs,
55569
- ...rectResult
55605
+ ...rectResult,
55606
+ gateRegressedDuringRect
55570
55607
  };
55571
55608
  }
55572
55609
  }
@@ -56217,7 +56254,7 @@ function extractPauseReason(phaseOutputs) {
56217
56254
  }
56218
56255
  return;
56219
56256
  }
56220
- function deriveTddFailureCategory(phaseOutputs, unfixedFindings) {
56257
+ function deriveTddFailureCategory(phaseOutputs, unfixedFindings, gateRegressedDuringRect) {
56221
56258
  const testWriterOutput = phaseOutputs[testWriterOp.name];
56222
56259
  if (testWriterOutput?.success === false) {
56223
56260
  return "session-failure";
@@ -56233,7 +56270,7 @@ function deriveTddFailureCategory(phaseOutputs, unfixedFindings) {
56233
56270
  }
56234
56271
  return "tests-failing";
56235
56272
  }
56236
- const verifierPassed = verifierOutput?.success === true;
56273
+ const verifierPassed = verifierOutput?.success === true && !gateRegressedDuringRect;
56237
56274
  if (!verifierPassed && unfixedFindings && unfixedFindings.length > 0) {
56238
56275
  const rectOutput = phaseOutputs.rectification;
56239
56276
  if (rectOutput?.exitReason && EXHAUSTED_EXIT_REASONS.has(rectOutput.exitReason) && unfixedFindings.some((f) => f.source === "test-runner")) {
@@ -56282,6 +56319,11 @@ async function applyPostRunInspection(ctx, planResult, opts) {
56282
56319
  if (fullSuiteGateOutput?.passed) {
56283
56320
  ctx.fullSuiteGatePassed = true;
56284
56321
  }
56322
+ const gateFailingFiles = [
56323
+ ...new Set((fullSuiteGateOutput?.findings ?? []).map((f) => f.file).filter((f) => !!f))
56324
+ ];
56325
+ if (gateFailingFiles.length > 0)
56326
+ ctx.fullSuiteGateFailingFiles = gateFailingFiles;
56285
56327
  ctx.selfVerification = parseSelfVerificationMarker(agentResult.output ?? "", ctx.workdir);
56286
56328
  const selfVerificationFailed = ctx.selfVerification.lint === "fail" || ctx.selfVerification.typecheck === "fail";
56287
56329
  if (ctx.config.context?.v2?.enabled && ctx.sessionScratchDir) {
@@ -56348,7 +56390,7 @@ async function applyPostRunInspection(ctx, planResult, opts) {
56348
56390
  }
56349
56391
  }
56350
56392
  const pauseReason = extractPauseReason(planResult.phaseOutputs);
56351
- const failureCategory = isTdd && !planResult.success ? deriveTddFailureCategory(planResult.phaseOutputs, planResult.unfixedFindings) : undefined;
56393
+ const failureCategory = isTdd && !planResult.success ? deriveTddFailureCategory(planResult.phaseOutputs, planResult.unfixedFindings, planResult.gateRegressedDuringRect) : undefined;
56352
56394
  if (isTdd && !planResult.success && !failureCategory) {
56353
56395
  const phaseSignals = {};
56354
56396
  for (const [name, output] of Object.entries(planResult.phaseOutputs)) {
@@ -57203,6 +57245,7 @@ var init_routing2 = __esm(() => {
57203
57245
  const neverEscalated = !hasEscalationRecords;
57204
57246
  const initialAgent = ctx.story.routing?.initialAgent ?? (neverEscalated ? routing.agent : undefined);
57205
57247
  const initialProfileId = ctx.story.routing?.initialProfileId ?? (neverEscalated ? ctx.story.routing?.agentProfileId : undefined);
57248
+ const initialModelTier = ctx.story.routing?.initialModelTier ?? (neverEscalated ? routing.modelTier : undefined);
57206
57249
  ctx.story.routing = {
57207
57250
  ...ctx.story.routing ?? {},
57208
57251
  complexity: routing.complexity,
@@ -57212,7 +57255,8 @@ var init_routing2 = __esm(() => {
57212
57255
  modelTier: routing.modelTier,
57213
57256
  ...routing.agent !== undefined && { agent: routing.agent },
57214
57257
  ...initialAgent !== undefined && { initialAgent },
57215
- ...initialProfileId !== undefined && { initialProfileId }
57258
+ ...initialProfileId !== undefined && { initialProfileId },
57259
+ ...initialModelTier !== undefined && { initialModelTier }
57216
57260
  };
57217
57261
  if (ctx.prdPath) {
57218
57262
  await _routingDeps.savePRD(ctx.prd, ctx.prdPath);
@@ -60425,7 +60469,7 @@ var package_default;
60425
60469
  var init_package = __esm(() => {
60426
60470
  package_default = {
60427
60471
  name: "@nathapp/nax",
60428
- version: "0.70.0-canary.5",
60472
+ version: "0.70.0-canary.7",
60429
60473
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
60430
60474
  type: "module",
60431
60475
  bin: {
@@ -60520,8 +60564,8 @@ var init_version = __esm(() => {
60520
60564
  NAX_VERSION = package_default.version;
60521
60565
  NAX_COMMIT = (() => {
60522
60566
  try {
60523
- if (/^[0-9a-f]{6,10}$/.test("e8c2ab46"))
60524
- return "e8c2ab46";
60567
+ if (/^[0-9a-f]{6,10}$/.test("08567b02"))
60568
+ return "08567b02";
60525
60569
  } catch {}
60526
60570
  try {
60527
60571
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -61475,6 +61519,15 @@ async function findResponsibleStory(testFile, workdir, passedStories) {
61475
61519
  }
61476
61520
  return;
61477
61521
  }
61522
+ function findResponsibleStoryByTransition(testFile, snapshots) {
61523
+ const ordered = [...snapshots].sort((a, b) => a.completedAt.localeCompare(b.completedAt) || a.storyId.localeCompare(b.storyId));
61524
+ for (const snap of ordered) {
61525
+ if (snap.failingTestFiles?.includes(testFile)) {
61526
+ return snap.storyId;
61527
+ }
61528
+ }
61529
+ return;
61530
+ }
61478
61531
  async function runDeferredRegression(options) {
61479
61532
  const logger = getSafeLogger();
61480
61533
  const { config: config2, prd, workdir, runtime } = options;
@@ -61622,8 +61675,21 @@ async function runDeferredRegression(options) {
61622
61675
  }
61623
61676
  } else {
61624
61677
  const testFilesArray = Array.from(testFilesInFailures);
61678
+ const snapshots = options.storyMetrics ?? [];
61679
+ const passedById = new Map(passedStories.map((s) => [s.id, s]));
61625
61680
  for (const testFile of testFilesArray) {
61626
- const responsibleStory = await findResponsibleStory(testFile, workdir, passedStories);
61681
+ let responsibleStory;
61682
+ const transitionId = findResponsibleStoryByTransition(testFile, snapshots);
61683
+ if (transitionId && passedById.has(transitionId)) {
61684
+ responsibleStory = passedById.get(transitionId);
61685
+ logger?.info("regression", "Mapped test to story via gate transition", {
61686
+ storyId: transitionId,
61687
+ testFile
61688
+ });
61689
+ }
61690
+ if (!responsibleStory) {
61691
+ responsibleStory = await findResponsibleStory(testFile, workdir, passedStories);
61692
+ }
61627
61693
  if (responsibleStory) {
61628
61694
  affectedStories.add(responsibleStory.id);
61629
61695
  affectedStoriesObjs.set(responsibleStory.id, responsibleStory);
@@ -61808,7 +61874,12 @@ async function handleRunCompletion(options) {
61808
61874
  config: config2,
61809
61875
  prd,
61810
61876
  workdir,
61811
- runtime: options.runtime
61877
+ runtime: options.runtime,
61878
+ storyMetrics: options.isSequential === false ? undefined : allStoryMetrics.map((m) => ({
61879
+ storyId: m.storyId,
61880
+ completedAt: m.completedAt,
61881
+ failingTestFiles: m.failingTestFiles
61882
+ }))
61812
61883
  });
61813
61884
  const lastRunAt = new Date().toISOString();
61814
61885
  logger?.info("regression", "Deferred regression gate completed", {
@@ -63341,13 +63412,17 @@ function buildEscalationFailure(story, currentTier, reviewFindings, cost, pipeli
63341
63412
  summary,
63342
63413
  reviewFindings: reviewFindings && reviewFindings.length > 0 ? reviewFindings : undefined,
63343
63414
  cost: cost ?? 0,
63344
- timestamp: new Date().toISOString()
63415
+ timestamp: new Date().toISOString(),
63416
+ ...story.routing?.agent !== undefined ? { agent: story.routing.agent } : {},
63417
+ ...story.routing?.agentProfileId !== undefined ? { agentProfileId: story.routing.agentProfileId } : {}
63345
63418
  };
63346
63419
  }
63347
- function buildEscalationRecord(currentTier, nextTier, reason) {
63420
+ function buildEscalationRecord(currentTier, nextTier, reason, agents) {
63348
63421
  return {
63349
63422
  fromTier: currentTier,
63350
63423
  toTier: nextTier,
63424
+ ...agents?.fromAgent !== undefined ? { fromAgent: agents.fromAgent } : {},
63425
+ ...agents?.toAgent !== undefined ? { toAgent: agents.toAgent } : {},
63351
63426
  reason,
63352
63427
  timestamp: new Date().toISOString()
63353
63428
  };
@@ -63444,7 +63519,7 @@ async function handleTierEscalation(ctx) {
63444
63519
  const currentStoryTier = s.routing?.modelTier ?? ctx.routing.modelTier;
63445
63520
  const isChangingTier = currentStoryTier !== escalatedTier;
63446
63521
  const shouldResetAttempts = isChangingTier || shouldSwitchToTestAfter;
63447
- const escalationRecord = isChangingTier || shouldSwitchToTestAfter ? buildEscalationRecord(currentStoryTier, shouldSwitchToTestAfter ? currentStoryTier : escalatedTier, ctx.pipelineResult.reason ?? "Escalated to next retry path") : undefined;
63522
+ const escalationRecord = isChangingTier || shouldSwitchToTestAfter ? buildEscalationRecord(currentStoryTier, shouldSwitchToTestAfter ? currentStoryTier : escalatedTier, ctx.pipelineResult.reason ?? "Escalated to next retry path", { fromAgent: s.routing?.agent, toAgent: nextAgent }) : undefined;
63448
63523
  const escalationFailure = buildEscalationFailure(s, currentStoryTier, escalateReviewFindings, ctx.attemptCost, verifiedPipelineReason, escalateFailureCategory);
63449
63524
  return {
63450
63525
  ...s,
@@ -65663,7 +65738,8 @@ async function initializeRun(ctx) {
65663
65738
  prd = await reconcileState(prd, ctx.prdPath, ctx.workdir, ctx.config);
65664
65739
  const resetRef = ctx.config.review?.semantic?.resetRefOnRerun ?? false;
65665
65740
  const storyIsolation = ctx.config.execution.storyIsolation;
65666
- const resetStories = resetFailedStoriesToPending(prd, resetRef, storyIsolation);
65741
+ const resetMode = ctx.config.autoMode.escalation.resetMode;
65742
+ const resetStories = resetFailedStoriesToPending(prd, { resetRef, storyIsolation, resetMode });
65667
65743
  if (resetStories.length > 0) {
65668
65744
  const resetIds = resetStories.map((s) => s.id);
65669
65745
  logger?.info("run-initialization", "Reset failed stories to pending for re-run", { storyIds: resetIds });
@@ -95894,7 +95970,8 @@ function finalizePrdRouting(prd, agentRouting, profileName) {
95894
95970
  agentProfileId: assignment.agentProfileId,
95895
95971
  profileModelTier: assignment.profileModelTier,
95896
95972
  initialAgent: story.routing?.initialAgent ?? assignment.agent,
95897
- initialProfileId: story.routing?.initialProfileId ?? assignment.agentProfileId
95973
+ initialProfileId: story.routing?.initialProfileId ?? assignment.agentProfileId,
95974
+ initialModelTier: story.routing?.initialModelTier ?? assignment.profileModelTier
95898
95975
  };
95899
95976
  return { ...story, routing };
95900
95977
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.70.0-canary.5",
3
+ "version": "0.70.0-canary.7",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {