@nathapp/nax 0.67.15 → 0.67.17

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 +157 -24
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -52720,6 +52720,65 @@ var init_context2 = __esm(() => {
52720
52720
  });
52721
52721
 
52722
52722
  // src/execution/story-orchestrator.ts
52723
+ async function refreshReviewInputForDispatch(opName, input) {
52724
+ if (opName !== "semantic-review" && opName !== "adversarial-review")
52725
+ return input;
52726
+ const i = input;
52727
+ const { _refresh } = i;
52728
+ if (!_refresh || !i.workdir)
52729
+ return input;
52730
+ try {
52731
+ if (opName === "semantic-review") {
52732
+ const { _refresh: _, ...semInput } = input;
52733
+ const fresh2 = await _storyOrchestratorDeps.prepareSemanticReviewInput({
52734
+ workdir: semInput.workdir,
52735
+ projectDir: _refresh.projectDir,
52736
+ storyId: _refresh.storyId,
52737
+ storyGitRef: _refresh.storyGitRef,
52738
+ config: _refresh.config,
52739
+ naxIgnoreIndex: _refresh.naxIgnoreIndex,
52740
+ resolvedTestPatterns: _refresh.resolvedTestPatterns,
52741
+ semanticConfig: semInput.semanticConfig
52742
+ });
52743
+ return {
52744
+ ...semInput,
52745
+ stat: fresh2.stat,
52746
+ diff: fresh2.diff,
52747
+ excludePatterns: fresh2.excludePatterns,
52748
+ storyGitRef: fresh2.effectiveRef ?? semInput.storyGitRef
52749
+ };
52750
+ }
52751
+ const { _refresh: __, ...advInput } = input;
52752
+ const fresh = await _storyOrchestratorDeps.prepareAdversarialReviewInput({
52753
+ workdir: advInput.workdir,
52754
+ projectDir: _refresh.projectDir,
52755
+ storyId: _refresh.storyId,
52756
+ storyGitRef: _refresh.storyGitRef,
52757
+ config: _refresh.config,
52758
+ naxIgnoreIndex: _refresh.naxIgnoreIndex,
52759
+ resolvedTestPatterns: _refresh.resolvedTestPatterns,
52760
+ adversarialConfig: advInput.adversarialConfig
52761
+ });
52762
+ return {
52763
+ ...advInput,
52764
+ stat: fresh.stat,
52765
+ diff: fresh.diff,
52766
+ testInventory: fresh.testInventory,
52767
+ excludePatterns: fresh.excludePatterns,
52768
+ testGlobs: fresh.testGlobs,
52769
+ refExcludePatterns: fresh.refExcludePatterns,
52770
+ storyGitRef: fresh.effectiveRef ?? advInput.storyGitRef
52771
+ };
52772
+ } catch (err) {
52773
+ getSafeLogger()?.warn("story-orchestrator", "review input refresh failed \u2014 dispatching with stale input", {
52774
+ storyId: _refresh.storyId,
52775
+ phase: opName,
52776
+ error: errorMessage(err)
52777
+ });
52778
+ const { _refresh: _stripped, ...fallback } = input;
52779
+ return fallback;
52780
+ }
52781
+ }
52723
52782
  function formatPhaseResultMessage(opName, success2) {
52724
52783
  if (opName === "greenfield-gate") {
52725
52784
  return success2 ? "Greenfield-gate: pre-existing tests detected (not greenfield) \u2014 proceeding with normal TDD" : "Greenfield-gate: no pre-existing tests \u2014 greenfield run, pausing TDD test-writer";
@@ -52995,7 +53054,8 @@ async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = fa
52995
53054
  const opName = slot.op.name;
52996
53055
  const isTddPhase = isThreeSession && TDD_OP_NAMES.has(opName);
52997
53056
  const beforeRef = isTddPhase ? await _storyOrchestratorDeps.captureGitRef(ctx.packageDir) : undefined;
52998
- const dispatchInput = isTddPhase && beforeRef ? { ...slot.input, beforeRef } : slot.input;
53057
+ let dispatchInput = isTddPhase && beforeRef ? { ...slot.input, beforeRef } : slot.input;
53058
+ dispatchInput = await refreshReviewInputForDispatch(opName, dispatchInput);
52999
53059
  if (isTddPhase) {
53000
53060
  logger?.info("tdd", `-> Session: ${opName}`, { storyId: ctx.storyId, role: opName });
53001
53061
  } else if (isThreeSession && opName === "full-suite-gate") {
@@ -53106,7 +53166,15 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53106
53166
  await runPhase(ctx, phase.slot, phaseCosts, phaseOutputs);
53107
53167
  if (shouldSkipPhaseForRectification(phase, state, phaseOutputs))
53108
53168
  continue;
53109
- findings.push(...extractPhaseFindings(phaseOutputs[phase.slot.op.name]));
53169
+ const output = phaseOutputs[phase.slot.op.name];
53170
+ findings.push(...extractPhaseFindings(output));
53171
+ if (!phasePassed(phase.slot.op.name, output, ctx.storyId)) {
53172
+ getSafeLogger()?.warn("story-orchestrator", "Short-circuiting revalidation on phase failure", {
53173
+ storyId: ctx.storyId,
53174
+ phase: phase.slot.op.name
53175
+ });
53176
+ break;
53177
+ }
53110
53178
  }
53111
53179
  return rectification.postValidate ? await rectification.postValidate(findings, _validateCtx) : findings;
53112
53180
  }
@@ -53137,14 +53205,7 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53137
53205
  storyId: ctx.storyId
53138
53206
  });
53139
53207
  }
53140
- const exhaustedReasons = new Set([
53141
- "max-attempts-total",
53142
- "max-attempts-per-strategy",
53143
- "bail-when",
53144
- "no-strategy",
53145
- "agent-gave-up"
53146
- ]);
53147
- if (exhaustedReasons.has(cycleResult.exitReason) && cycleResult.finalFindings.length > 0) {
53208
+ if (EXHAUSTED_EXIT_REASONS.has(cycleResult.exitReason) && cycleResult.finalFindings.length > 0) {
53148
53209
  return { rectificationExhausted: true, unfixedFindings: cycleResult.finalFindings };
53149
53210
  }
53150
53211
  return {};
@@ -53191,6 +53252,50 @@ class ExecutionPlan {
53191
53252
  }
53192
53253
  }
53193
53254
  const rectResult = await runRectification(this.ctx, this.state, phaseCosts, phaseOutputs);
53255
+ if (this.state.rectification && !rectResult.rectificationExhausted) {
53256
+ let resumeRectifyUsed = false;
53257
+ for (const phase of collectOrderedPhases(this.state)) {
53258
+ const name = phase.slot.op.name;
53259
+ if (name in phaseOutputs && phasePassed(name, phaseOutputs[name], this.ctx.storyId)) {
53260
+ continue;
53261
+ }
53262
+ try {
53263
+ await runPhase(this.ctx, phase.slot, phaseCosts, phaseOutputs, this.isThreeSession);
53264
+ } catch (error48) {
53265
+ logger?.error("story-orchestrator", "Phase threw unexpected error (post-rectification resume)", {
53266
+ storyId: this.ctx.storyId,
53267
+ phase: name,
53268
+ error: errorMessage(error48)
53269
+ });
53270
+ throw error48;
53271
+ }
53272
+ if (!phasePassed(name, phaseOutputs[name], this.ctx.storyId)) {
53273
+ if (!resumeRectifyUsed) {
53274
+ resumeRectifyUsed = true;
53275
+ logger?.info("story-orchestrator", "Phase failed in post-rectification resume \u2014 invoking second rectification pass", { storyId: this.ctx.storyId, phase: name, source: "post-rectification-resume" });
53276
+ const secondRect = await runRectification(this.ctx, this.state, phaseCosts, phaseOutputs);
53277
+ if (secondRect.rectificationExhausted) {
53278
+ logger?.warn("story-orchestrator", "Second rectification pass exhausted \u2014 terminal failure", {
53279
+ storyId: this.ctx.storyId,
53280
+ phase: name,
53281
+ source: "post-rectification-resume"
53282
+ });
53283
+ break;
53284
+ }
53285
+ if (phasePassed(name, phaseOutputs[name], this.ctx.storyId)) {
53286
+ continue;
53287
+ }
53288
+ }
53289
+ logger?.warn("story-orchestrator", "Terminal phase failure (post-rectification resume \u2014 bypasses rectification)", {
53290
+ storyId: this.ctx.storyId,
53291
+ phase: name,
53292
+ source: "post-rectification-resume",
53293
+ secondRectifyUsed: resumeRectifyUsed
53294
+ });
53295
+ break;
53296
+ }
53297
+ }
53298
+ }
53194
53299
  const verifierName = this.state.verifier?.slot.op.name;
53195
53300
  const gateName = this.state.fullSuiteGate?.slot.op.name;
53196
53301
  const verifierPassedSsot = verifierName !== undefined && phaseExplicitlyPassed(phaseOutputs[verifierName]);
@@ -53290,19 +53395,29 @@ class StoryOrchestratorBuilder {
53290
53395
  return new ExecutionPlan(ctx, { ...this.state }, opts.isThreeSession ?? false);
53291
53396
  }
53292
53397
  }
53293
- var _storyOrchestratorDeps, TDD_OP_NAMES, STRICT_VERDICT_PHASE_NAMES, CANONICAL_ORDER, PHASE_KIND_TO_STATE_KEY, STRATEGY_TO_REVALIDATION_PHASES;
53398
+ var _storyOrchestratorDeps, EXHAUSTED_EXIT_REASONS, TDD_OP_NAMES, STRICT_VERDICT_PHASE_NAMES, CANONICAL_ORDER, PHASE_KIND_TO_STATE_KEY, STRATEGY_TO_REVALIDATION_PHASES;
53294
53399
  var init_story_orchestrator = __esm(() => {
53295
53400
  init_errors();
53296
53401
  init_findings();
53297
53402
  init_logger2();
53298
53403
  init_operations();
53299
53404
  init_call();
53405
+ init_prepare_inputs();
53300
53406
  init_git();
53301
53407
  _storyOrchestratorDeps = {
53302
53408
  callOp,
53303
53409
  runFixCycle,
53304
- captureGitRef
53410
+ captureGitRef,
53411
+ prepareSemanticReviewInput,
53412
+ prepareAdversarialReviewInput
53305
53413
  };
53414
+ EXHAUSTED_EXIT_REASONS = new Set([
53415
+ "max-attempts-total",
53416
+ "max-attempts-per-strategy",
53417
+ "bail-when",
53418
+ "no-strategy",
53419
+ "agent-gave-up"
53420
+ ]);
53306
53421
  TDD_OP_NAMES = new Set(["test-writer", "implementer", "verifier"]);
53307
53422
  STRICT_VERDICT_PHASE_NAMES = new Set([
53308
53423
  fullSuiteGateOp.name,
@@ -53601,8 +53716,6 @@ async function assemblePlanInputsFromCtx(ctx) {
53601
53716
  resolvedTestPatterns,
53602
53717
  semanticConfig: ctx.config.review.semantic
53603
53718
  });
53604
- if (prepared.skipReason)
53605
- return;
53606
53719
  return {
53607
53720
  workdir: ctx.workdir,
53608
53721
  story,
@@ -53614,7 +53727,15 @@ async function assemblePlanInputsFromCtx(ctx) {
53614
53727
  excludePatterns: prepared.excludePatterns,
53615
53728
  featureCtxBlock: buildFeatureCtxBlock(ctx, "reviewer-semantic"),
53616
53729
  priorSemanticIterations: ctx.priorSemanticIterations,
53617
- blockingThreshold: ctx.config.review.blockingThreshold
53730
+ blockingThreshold: ctx.config.review.blockingThreshold,
53731
+ _refresh: {
53732
+ projectDir: ctx.projectDir,
53733
+ storyId: story.id,
53734
+ storyGitRef: ctx.storyGitRef,
53735
+ config: ctx.config,
53736
+ naxIgnoreIndex: ctx.naxIgnoreIndex,
53737
+ resolvedTestPatterns
53738
+ }
53618
53739
  };
53619
53740
  })() : undefined;
53620
53741
  const adversarialEnabled = ctx.config.review?.enabled === true && ctx.config.review.checks?.includes("adversarial") && !!ctx.config.review.adversarial;
@@ -53629,8 +53750,6 @@ async function assemblePlanInputsFromCtx(ctx) {
53629
53750
  resolvedTestPatterns,
53630
53751
  adversarialConfig: ctx.config.review.adversarial
53631
53752
  });
53632
- if (prepared.skipReason)
53633
- return;
53634
53753
  return {
53635
53754
  workdir: ctx.workdir,
53636
53755
  story,
@@ -53645,7 +53764,15 @@ async function assemblePlanInputsFromCtx(ctx) {
53645
53764
  refExcludePatterns: prepared.refExcludePatterns,
53646
53765
  featureCtxBlock: buildFeatureCtxBlock(ctx, "reviewer-adversarial"),
53647
53766
  priorAdversarialIterations: ctx.priorAdversarialIterations,
53648
- blockingThreshold: ctx.config.review.blockingThreshold
53767
+ blockingThreshold: ctx.config.review.blockingThreshold,
53768
+ _refresh: {
53769
+ projectDir: ctx.projectDir,
53770
+ storyId: story.id,
53771
+ storyGitRef: ctx.storyGitRef,
53772
+ config: ctx.config,
53773
+ naxIgnoreIndex: ctx.naxIgnoreIndex,
53774
+ resolvedTestPatterns
53775
+ }
53649
53776
  };
53650
53777
  })() : undefined;
53651
53778
  const rectificationInput = ctx.config.execution?.rectification?.enabled === true ? {
@@ -53839,7 +53966,7 @@ function extractPauseReason(phaseOutputs) {
53839
53966
  }
53840
53967
  return;
53841
53968
  }
53842
- function deriveTddFailureCategory(phaseOutputs) {
53969
+ function deriveTddFailureCategory(phaseOutputs, unfixedFindings) {
53843
53970
  const testWriterOutput = phaseOutputs[testWriterOp.name];
53844
53971
  if (testWriterOutput?.success === false) {
53845
53972
  return "session-failure";
@@ -53856,6 +53983,12 @@ function deriveTddFailureCategory(phaseOutputs) {
53856
53983
  return "tests-failing";
53857
53984
  }
53858
53985
  const verifierPassed = verifierOutput?.success === true;
53986
+ if (!verifierPassed && unfixedFindings && unfixedFindings.length > 0) {
53987
+ const rectOutput = phaseOutputs.rectification;
53988
+ if (rectOutput?.exitReason && EXHAUSTED_EXIT_REASONS.has(rectOutput.exitReason) && unfixedFindings.some((f) => f.source === "test-runner")) {
53989
+ return "full-suite-gate-exhausted";
53990
+ }
53991
+ }
53859
53992
  if (!verifierPassed) {
53860
53993
  const gateOutput = phaseOutputs[fullSuiteGateOp.name];
53861
53994
  if (gateOutput && (gateOutput.success === false || gateOutput.passed === false)) {
@@ -53888,7 +54021,6 @@ async function applyPostRunInspection(ctx, planResult, opts) {
53888
54021
  ...capturedTokenUsage ? { tokenUsage: capturedTokenUsage } : {}
53889
54022
  };
53890
54023
  ctx.agentResult = agentResult;
53891
- ctx.agentSwapCount = 0;
53892
54024
  const fullSuiteGateOutput = planResult.phaseOutputs[fullSuiteGateOp.name];
53893
54025
  if (fullSuiteGateOutput?.passed) {
53894
54026
  ctx.fullSuiteGatePassed = true;
@@ -53959,7 +54091,7 @@ async function applyPostRunInspection(ctx, planResult, opts) {
53959
54091
  }
53960
54092
  }
53961
54093
  const pauseReason = extractPauseReason(planResult.phaseOutputs);
53962
- const failureCategory = isTdd && !planResult.success ? deriveTddFailureCategory(planResult.phaseOutputs) : undefined;
54094
+ const failureCategory = isTdd && !planResult.success ? deriveTddFailureCategory(planResult.phaseOutputs, planResult.unfixedFindings) : undefined;
53963
54095
  if (isTdd && !planResult.success && !failureCategory) {
53964
54096
  const phaseSignals = {};
53965
54097
  for (const [name, output] of Object.entries(planResult.phaseOutputs)) {
@@ -54178,6 +54310,7 @@ var init_post_run = __esm(() => {
54178
54310
  init_scratch_writer();
54179
54311
  init_rollback();
54180
54312
  init_git();
54313
+ init_story_orchestrator();
54181
54314
  _postRunDeps = {
54182
54315
  detectMergeConflict,
54183
54316
  checkMergeConflict,
@@ -57700,7 +57833,7 @@ var package_default;
57700
57833
  var init_package = __esm(() => {
57701
57834
  package_default = {
57702
57835
  name: "@nathapp/nax",
57703
- version: "0.67.15",
57836
+ version: "0.67.17",
57704
57837
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
57705
57838
  type: "module",
57706
57839
  bin: {
@@ -57795,8 +57928,8 @@ var init_version = __esm(() => {
57795
57928
  NAX_VERSION = package_default.version;
57796
57929
  NAX_COMMIT = (() => {
57797
57930
  try {
57798
- if (/^[0-9a-f]{6,10}$/.test("035dd857"))
57799
- return "035dd857";
57931
+ if (/^[0-9a-f]{6,10}$/.test("74621ad5"))
57932
+ return "74621ad5";
57800
57933
  } catch {}
57801
57934
  try {
57802
57935
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.67.15",
3
+ "version": "0.67.17",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {