@nathapp/nax 0.83.1 → 0.83.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/nax.js +151 -7
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -2668,7 +2668,7 @@ var package_default;
2668
2668
  var init_package = __esm(() => {
2669
2669
  package_default = {
2670
2670
  name: "@nathapp/nax",
2671
- version: "0.83.1",
2671
+ version: "0.83.2",
2672
2672
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
2673
2673
  type: "module",
2674
2674
  bin: {
@@ -57251,8 +57251,8 @@ var init_version = __esm(() => {
57251
57251
  NAX_AI_VERSION = CATALOG_VERSION;
57252
57252
  NAX_COMMIT = (() => {
57253
57253
  try {
57254
- if (/^[0-9a-f]{6,10}$/.test("edd97363"))
57255
- return "edd97363";
57254
+ if (/^[0-9a-f]{6,10}$/.test("bcfcddb0"))
57255
+ return "bcfcddb0";
57256
57256
  } catch {}
57257
57257
  try {
57258
57258
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -92508,6 +92508,78 @@ var init_run_phase = __esm(() => {
92508
92508
  ];
92509
92509
  });
92510
92510
 
92511
+ // src/execution/story-orchestrator/review-pair.ts
92512
+ function shouldRunReviewsConcurrently(reviewConfig, phases) {
92513
+ const adversarial = reviewConfig?.adversarial;
92514
+ const maxConcurrentSessions = adversarial?.maxConcurrentSessions ?? 2;
92515
+ return adversarial?.parallel === true && maxConcurrentSessions >= 2 && phases.some((phase) => phase.kind === "semantic-review") && phases.some((phase) => phase.kind === "adversarial-review");
92516
+ }
92517
+ async function runReviewPair(ctx, pair, tracking, isThreeSession = false, progress) {
92518
+ getSafeLogger()?.info("story-orchestrator", "Running semantic-review and adversarial-review concurrently", {
92519
+ storyId: ctx.storyId
92520
+ });
92521
+ const outcomes = await Promise.allSettled(pair.map((phase, index) => _reviewPairDeps.runPhase(ctx, phase.slot, tracking.phaseCosts, tracking.phaseOutputs, isThreeSession, progress ? { index: index === 0 ? progress.indices[0] : progress.indices[1], total: progress.total } : undefined)));
92522
+ const logger = getSafeLogger();
92523
+ for (const [index, outcome] of outcomes.entries()) {
92524
+ if (outcome.status === "rejected") {
92525
+ logger?.error("story-orchestrator", "Phase threw unexpected error", {
92526
+ storyId: ctx.storyId,
92527
+ phase: index === 0 ? pair[0].kind : pair[1].kind,
92528
+ error: errorMessage(outcome.reason)
92529
+ });
92530
+ }
92531
+ }
92532
+ const semanticOutcome = outcomes[0];
92533
+ if (semanticOutcome?.status === "rejected")
92534
+ throw semanticOutcome.reason;
92535
+ const adversarialOutcome = outcomes[1];
92536
+ if (adversarialOutcome?.status === "rejected")
92537
+ throw adversarialOutcome.reason;
92538
+ }
92539
+ var _reviewPairDeps;
92540
+ var init_review_pair = __esm(() => {
92541
+ init_logger2();
92542
+ init_run_phase();
92543
+ _reviewPairDeps = { runPhase };
92544
+ });
92545
+
92546
+ // src/execution/story-orchestrator/revalidation-reviews.ts
92547
+ async function dispatchRevalidationPhase(ctx, phase, phases, tracking, isThreeSession) {
92548
+ const reviewConfig = ctx.config?.review ?? ctx.packageView.config.review;
92549
+ if (phase.kind === "semantic-review" && shouldRunReviewsConcurrently(reviewConfig, phases)) {
92550
+ const semantic = phase;
92551
+ const adversarial = phases.find((candidate) => candidate.kind === "adversarial-review");
92552
+ if (adversarial) {
92553
+ await runReviewPair(ctx, [semantic, adversarial], tracking, isThreeSession);
92554
+ return ["semantic-review", "adversarial-review"];
92555
+ }
92556
+ }
92557
+ await runPhase(ctx, phase.slot, tracking.phaseCosts, tracking.phaseOutputs, isThreeSession);
92558
+ return [phase.kind];
92559
+ }
92560
+ function phasesForRevalidation(ctx, phases) {
92561
+ const reviewConfig = ctx.config?.review ?? ctx.packageView.config.review;
92562
+ return shouldRunReviewsConcurrently(reviewConfig, phases) ? phases.filter((phase) => phase.kind !== "adversarial-review") : phases;
92563
+ }
92564
+ function collectPairedReviewFindings(options) {
92565
+ if (options.dispatched.length < 2)
92566
+ return false;
92567
+ const phase = options.phases.find((item) => item.kind === "adversarial-review");
92568
+ if (!phase)
92569
+ return false;
92570
+ const output = options.phaseOutputs[phase.slot.op.name];
92571
+ options.findings.push(...extractPhaseFindings(output));
92572
+ return !phasePassed(phase.slot.op.name, output, options.storyId);
92573
+ }
92574
+ function isRevalidationFailure(options) {
92575
+ return !phasePassed(options.phase.slot.op.name, options.output, options.storyId) && !options.quarantinedOnly || options.pairedReviewFailed;
92576
+ }
92577
+ var init_revalidation_reviews = __esm(() => {
92578
+ init_phase_eval();
92579
+ init_review_pair();
92580
+ init_run_phase();
92581
+ });
92582
+
92511
92583
  // src/execution/story-orchestrator/rectification.ts
92512
92584
  function shouldSkipPhaseForRectification(input) {
92513
92585
  const { phase, state, phaseOutputs, nbfPath } = input;
@@ -92663,6 +92735,7 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs, overrides)
92663
92735
  const extra = overrides?.extraRevalidationKinds ? validationPhases.filter((p) => overrides.extraRevalidationKinds?.includes(p.kind) && !selected.some((s) => s.kind === p.kind)) : [];
92664
92736
  const selectedWithExtra = [...selected, ...extra];
92665
92737
  const phases = lite ? orderGateLast(selectedWithExtra) : selectedWithExtra;
92738
+ const dispatchPhases = phasesForRevalidation(ctx, phases);
92666
92739
  getSafeLogger()?.debug("story-orchestrator", "rectification validate scope", {
92667
92740
  storyId: ctx.storyId,
92668
92741
  mode: opts?.mode ?? "full",
@@ -92671,8 +92744,8 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs, overrides)
92671
92744
  });
92672
92745
  const findings = [];
92673
92746
  let shortCircuited = false;
92674
- for (const phase of phases) {
92675
- await runPhase(ctx, phase.slot, phaseCosts, phaseOutputs, overrides?.isThreeSession);
92747
+ for (const phase of dispatchPhases) {
92748
+ const dispatched = await dispatchRevalidationPhase(ctx, phase, phases, { phaseCosts, phaseOutputs }, overrides?.isThreeSession);
92676
92749
  if (shouldSkipPhaseForRectification({ phase, state, phaseOutputs, nbfPath })) {
92677
92750
  const carvedOutFindings = selectRegressedGateFindings(extractPhaseFindings(phaseOutputs[phase.slot.op.name]), overrides?.gateBaselineKeys ?? new Set, ctx.runtime.quarantineMemo);
92678
92751
  if (carvedOutFindings.length > 0) {
@@ -92699,8 +92772,15 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs, overrides)
92699
92772
  const phaseFindings = extractPhaseFindings(output);
92700
92773
  const blockingFindings = nbfPath ? phaseFindings.filter((finding) => !isQuarantinedFlake(finding, nbfFlakeTriage?.memo ?? ctx.runtime.quarantineMemo)) : phaseFindings;
92701
92774
  findings.push(...blockingFindings);
92775
+ const pairedReviewFailed = collectPairedReviewFindings({
92776
+ dispatched,
92777
+ phases,
92778
+ phaseOutputs,
92779
+ findings,
92780
+ storyId: ctx.storyId
92781
+ });
92702
92782
  const quarantinedOnly = nbfPath && isQuarantinedOnlyGateFailure(phase, phaseFindings, blockingFindings);
92703
- if (!phasePassed(phase.slot.op.name, output, ctx.storyId) && !quarantinedOnly) {
92783
+ if (isRevalidationFailure({ phase, output, storyId: ctx.storyId, quarantinedOnly, pairedReviewFailed })) {
92704
92784
  getSafeLogger()?.warn("story-orchestrator", "Short-circuiting revalidation on phase failure", {
92705
92785
  storyId: ctx.storyId,
92706
92786
  phase: phase.slot.op.name
@@ -92784,6 +92864,7 @@ var init_rectification = __esm(() => {
92784
92864
  init_nbf_flake_triage();
92785
92865
  init_no_progress_bail();
92786
92866
  init_phase_eval();
92867
+ init_revalidation_reviews();
92787
92868
  init_run_phase();
92788
92869
  init_types12();
92789
92870
  });
@@ -92837,14 +92918,76 @@ async function recordGreenCheckpoint(plan, phaseName) {
92837
92918
  }
92838
92919
  }
92839
92920
  }
92921
+ async function runCanonicalReviewPair(options) {
92922
+ const { plan, tracking, semantic, adversarial, indices, total } = options;
92923
+ const { ctx, isThreeSession } = plan;
92924
+ const reviews = [semantic, adversarial];
92925
+ try {
92926
+ await runReviewPair(ctx, reviews, tracking, isThreeSession, { indices, total });
92927
+ } catch (error48) {
92928
+ for (const review of reviews) {
92929
+ const name = review.slot.op.name;
92930
+ if (name in tracking.phaseOutputs && phasePassed(name, tracking.phaseOutputs[name], ctx.storyId)) {
92931
+ await recordGreenCheckpoint(plan, name);
92932
+ }
92933
+ }
92934
+ throw error48;
92935
+ }
92936
+ let shortCircuitPhase;
92937
+ for (const review of reviews) {
92938
+ const name = review.slot.op.name;
92939
+ if (phasePassed(name, tracking.phaseOutputs[name], ctx.storyId)) {
92940
+ await recordGreenCheckpoint(plan, name);
92941
+ } else {
92942
+ shortCircuitPhase = name;
92943
+ if (name !== "semantic-review") {
92944
+ getSafeLogger()?.warn("story-orchestrator", "Short-circuiting on phase failure", {
92945
+ storyId: ctx.storyId,
92946
+ phase: name
92947
+ });
92948
+ break;
92949
+ }
92950
+ getSafeLogger()?.warn("story-orchestrator", "semantic-review failed \u2014 continuing to adversarial-review for a second opinion", { storyId: ctx.storyId, phase: name });
92951
+ }
92952
+ }
92953
+ return shortCircuitPhase;
92954
+ }
92840
92955
  async function runCanonicalLoop(plan, tracking, orderedPhases) {
92841
92956
  const { ctx, isThreeSession } = plan;
92842
92957
  const { phaseCosts, phaseOutputs } = tracking;
92843
92958
  const logger = getSafeLogger();
92844
92959
  let shortCircuitPhase;
92960
+ const skippedPhaseIndexes = new Set;
92961
+ const runnablePhases = orderedPhases.filter((phase, index) => {
92962
+ const name = phase.slot.op.name;
92963
+ const alreadyPassed = name in phaseOutputs && phasePassed(name, phaseOutputs[name], ctx.storyId);
92964
+ if (alreadyPassed)
92965
+ skippedPhaseIndexes.add(index);
92966
+ return !alreadyPassed;
92967
+ });
92968
+ const reviewConfig = ctx.config?.review ?? ctx.packageView.config.review;
92969
+ const pairReviews = shouldRunReviewsConcurrently(reviewConfig, runnablePhases);
92970
+ const phasesByKind = new Map(orderedPhases.map((phase, index) => [phase.kind, { phase, index }]));
92971
+ let pairedAdversarialIndex;
92845
92972
  for (const [phaseIndex, phase] of orderedPhases.entries()) {
92846
92973
  const name = phase.slot.op.name;
92847
- if (name in phaseOutputs && phasePassed(name, phaseOutputs[name], ctx.storyId)) {
92974
+ if (phaseIndex === pairedAdversarialIndex)
92975
+ continue;
92976
+ if (skippedPhaseIndexes.has(phaseIndex))
92977
+ continue;
92978
+ const adversarial = phasesByKind.get("adversarial-review");
92979
+ if (pairReviews && phase.kind === "semantic-review" && adversarial && adversarial.index > phaseIndex) {
92980
+ pairedAdversarialIndex = adversarial.index;
92981
+ shortCircuitPhase = await runCanonicalReviewPair({
92982
+ plan,
92983
+ tracking,
92984
+ semantic: phase,
92985
+ adversarial: adversarial.phase,
92986
+ indices: [phaseIndex + 1, adversarial.index + 1],
92987
+ total: orderedPhases.length
92988
+ });
92989
+ if (shortCircuitPhase === "adversarial-review")
92990
+ break;
92848
92991
  continue;
92849
92992
  }
92850
92993
  try {
@@ -93005,6 +93148,7 @@ var init_execution_plan_phases = __esm(() => {
93005
93148
  init_phase_eval();
93006
93149
  init_phase_state();
93007
93150
  init_rectification();
93151
+ init_review_pair();
93008
93152
  init_run_phase();
93009
93153
  });
93010
93154
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.83.1",
3
+ "version": "0.83.2",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {