@nathapp/nax 0.65.4 → 0.65.5

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 +111 -57
  2. package/package.json +5 -2
package/dist/nax.js CHANGED
@@ -29424,53 +29424,71 @@ var init_debate_builder = __esm(() => {
29424
29424
  function buildPriorIterationsBlock(iterations) {
29425
29425
  if (iterations.length === 0)
29426
29426
  return "";
29427
- const rows = iterations.map((iter) => {
29428
- const strategies = iter.fixesApplied.map((fa) => fa.strategyName).join(", ") || "-";
29429
- const files = iter.fixesApplied.flatMap((fa) => fa.targetFiles).join(", ") || "-";
29430
- const outcome = iter.outcome;
29431
- const findingSummary = formatFindingSummary(iter.findingsBefore, iter.findingsAfter);
29432
- return `| ${iter.iterationNum} | ${strategies} | ${files} | ${outcome} | ${findingSummary} |`;
29433
- });
29434
- const header = "| # | Strategies run | Files touched | Outcome | Findings before \u2192 after |";
29435
- const separator = "|---|----------------|---------------|---------|--------------------------|";
29436
- const table = [header, separator, ...rows].join(`
29427
+ const sections2 = iterations.map((iter) => renderIteration(iter));
29428
+ const { displaySections, visibleIterations } = applyTokenGuard(sections2, iterations);
29429
+ const verdictTemplate = renderVerdictTemplate(visibleIterations);
29430
+ return [
29431
+ "## Prior Iterations \u2014 verdict required before new analysis",
29432
+ "",
29433
+ ...displaySections,
29434
+ "",
29435
+ verdictTemplate,
29436
+ ""
29437
+ ].join(`
29437
29438
  `);
29438
- const hasUnchanged = iterations.some((i) => i.outcome === "unchanged");
29439
- const unchangedNote = hasUnchanged ? `
29440
- When outcome is "unchanged", the prior hypothesis is FALSIFIED \u2014 the change did not affect what was tested. Choose a different category before producing a new verdict. Do NOT repeat fixes listed above.` : "";
29441
- return `## Prior Iterations \u2014 verdict required before new analysis
29442
-
29443
- ${table}${unchangedNote}
29439
+ }
29440
+ function applyTokenGuard(sections2, iterations) {
29441
+ if (sections2.join(`
29444
29442
 
29445
- `;
29443
+ `).length <= MAX_BLOCK_CHARS || sections2.length <= 2) {
29444
+ return { displaySections: sections2, visibleIterations: iterations };
29445
+ }
29446
+ const n = sections2.length;
29447
+ const collapsed = iterations.slice(0, n - 2).map((iter) => `### Round ${iter.iterationNum} \u2014 outcome: ${iter.outcome} (${iter.findingsAfter.length} findings, omitted for brevity)`);
29448
+ const verbatim = sections2.slice(n - 2);
29449
+ return { displaySections: [...collapsed, ...verbatim], visibleIterations: iterations.slice(n - 2) };
29446
29450
  }
29447
- function formatFindingSummary(before, after) {
29448
- const beforeStr = before.length === 0 ? "0" : formatFindingCount(before);
29449
- const afterStr = after.length === 0 ? "0" : formatFindingCount(after);
29450
- return `${beforeStr} \u2192 ${afterStr}`;
29451
+ function renderIteration(iter) {
29452
+ const header = `### Round ${iter.iterationNum} \u2014 outcome: ${iter.outcome} (${iter.findingsBefore.length} \u2192 ${iter.findingsAfter.length})`;
29453
+ if (iter.findingsAfter.length === 0) {
29454
+ return [header, "_All prior findings cleared._"].join(`
29455
+ `);
29456
+ }
29457
+ const lines = iter.findingsAfter.map((f, i) => renderFinding(f, i + 1));
29458
+ return [header, "Findings flagged previously:", ...lines].join(`
29459
+ `);
29451
29460
  }
29452
- function formatFindingCount(findings) {
29453
- const count = findings.length;
29454
- const topCategory = mostFrequentCategory(findings);
29455
- return topCategory !== null ? `${count} [${topCategory}]` : `${count}`;
29461
+ function renderFinding(f, n) {
29462
+ const message = truncate(f.message ?? "", 240);
29463
+ const suggestion = truncate(f.suggestion ?? "", 200);
29464
+ const loc = f.file ? f.line != null ? `${f.file}:${f.line}` : f.file : "(workdir-global)";
29465
+ const tag = `[${f.severity} / ${f.category}]`;
29466
+ const ac = typeof f.meta?.acQuote === "string" ? f.meta.acQuote : undefined;
29467
+ const acLine = ac ? `
29468
+ acQuote: "${truncate(ac, 160)}"` : "";
29469
+ return `${n}. ${tag} ${loc}
29470
+ Message: ${message}
29471
+ Suggestion: ${suggestion}${acLine}`;
29472
+ }
29473
+ function renderVerdictTemplate(iterations) {
29474
+ const total = iterations.reduce((sum, it) => sum + it.findingsAfter.length, 0);
29475
+ const hasUnchanged = iterations.some((i) => i.outcome === "unchanged");
29476
+ const unchangedNote = hasUnchanged ? `
29477
+
29478
+ When outcome is "unchanged", the prior hypothesis is FALSIFIED \u2014 the change did not affect what was tested. Choose a different category before producing a new verdict. Do NOT repeat fixes listed above.` : "";
29479
+ return [
29480
+ `**Required:** before adding any new finding, classify each of the ${total} prior finding(s) above as one of:`,
29481
+ "- `addressed` \u2014 the current diff resolves it (cite the diff line that fixes it in your `message` field)",
29482
+ "- `still-blocking` \u2014 the implementer did not fix it; re-flag it with the IDENTICAL `file`, `line`, `category`, and substantively the same `message` wording",
29483
+ `- \`never-an-issue\` \u2014 your prior judgment was wrong; explain why in \`message\` and emit severity \`"info"\``,
29484
+ `Then surface any genuinely new findings.${unchangedNote}`
29485
+ ].join(`
29486
+ `);
29456
29487
  }
29457
- function mostFrequentCategory(findings) {
29458
- if (findings.length === 0)
29459
- return null;
29460
- const freq = new Map;
29461
- for (const f of findings) {
29462
- freq.set(f.category, (freq.get(f.category) ?? 0) + 1);
29463
- }
29464
- let top = null;
29465
- let topCount = 0;
29466
- for (const [cat, cnt] of freq) {
29467
- if (cnt > topCount) {
29468
- topCount = cnt;
29469
- top = cat;
29470
- }
29471
- }
29472
- return top;
29488
+ function truncate(s, max) {
29489
+ return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
29473
29490
  }
29491
+ var MAX_BLOCK_CHARS = 6000;
29474
29492
 
29475
29493
  // src/prompts/builders/review-builder.ts
29476
29494
  class ReviewPromptBuilder {
@@ -47468,6 +47486,16 @@ function buildFailureReason(checks3) {
47468
47486
  }
47469
47487
 
47470
47488
  class ReviewOrchestrator {
47489
+ priorAdversarialByStory = new Map;
47490
+ priorSemanticByStory = new Map;
47491
+ clearStory(storyId) {
47492
+ this.priorAdversarialByStory.delete(storyId);
47493
+ this.priorSemanticByStory.delete(storyId);
47494
+ }
47495
+ reset() {
47496
+ this.priorAdversarialByStory.clear();
47497
+ this.priorSemanticByStory.clear();
47498
+ }
47471
47499
  async review(opts) {
47472
47500
  const {
47473
47501
  reviewConfig,
@@ -47795,6 +47823,8 @@ class ReviewOrchestrator {
47795
47823
  assembleForStage(ctx, "review-adversarial")
47796
47824
  ]);
47797
47825
  const contextBundles = semanticBundle || adversarialBundle ? { semantic: semanticBundle ?? undefined, adversarial: adversarialBundle ?? undefined } : undefined;
47826
+ const priorAdversarialIterations = this.priorAdversarialByStory.get(ctx.story.id) ?? [];
47827
+ const priorSemanticIterations = this.priorSemanticByStory.get(ctx.story.id) ?? [];
47798
47828
  const result = await this.review({
47799
47829
  reviewConfig: ctx.config.review,
47800
47830
  workdir: ctx.workdir,
@@ -47816,23 +47846,24 @@ class ReviewOrchestrator {
47816
47846
  featureName: ctx.prd.feature,
47817
47847
  resolverSession,
47818
47848
  priorFailures: ctx.story.priorFailures,
47819
- priorSemanticIterations: ctx.priorSemanticIterations,
47849
+ priorSemanticIterations,
47820
47850
  featureContextMarkdown: ctx.featureContextMarkdown,
47821
47851
  contextBundles,
47822
47852
  projectDir: ctx.projectDir,
47823
47853
  env: ctx.worktreeDependencyContext?.env,
47824
47854
  naxIgnoreIndex: ctx.naxIgnoreIndex,
47825
47855
  runtime: ctx.runtime,
47826
- priorAdversarialIterations: ctx.priorAdversarialIterations
47856
+ priorAdversarialIterations
47827
47857
  });
47858
+ const logger = getSafeLogger();
47828
47859
  const advCheck = result.builtIn.checks?.find((c) => c.check === "adversarial");
47829
47860
  if (advCheck) {
47830
47861
  if (!advCheck.success && !advCheck.skipped) {
47831
- const prior = ctx.priorAdversarialIterations ?? [];
47862
+ const prior = this.priorAdversarialByStory.get(ctx.story.id) ?? [];
47832
47863
  const findingsBefore = prior.length > 0 ? prior[prior.length - 1].findingsAfter ?? [] : [];
47833
47864
  const findingsAfter = advCheck.findings ?? [];
47834
47865
  const now = new Date().toISOString();
47835
- const newIteration = {
47866
+ const next = {
47836
47867
  iterationNum: prior.length + 1,
47837
47868
  findingsBefore,
47838
47869
  fixesApplied: [],
@@ -47841,21 +47872,27 @@ class ReviewOrchestrator {
47841
47872
  startedAt: now,
47842
47873
  finishedAt: now
47843
47874
  };
47844
- ctx.priorAdversarialIterations = [...prior, newIteration];
47875
+ this.priorAdversarialByStory.set(ctx.story.id, [...prior, next]);
47876
+ logger?.debug("review", "Adversarial iteration recorded", {
47877
+ storyId: ctx.story.id,
47878
+ iterationNum: next.iterationNum,
47879
+ outcome: next.outcome,
47880
+ findingsCount: findingsAfter.length
47881
+ });
47845
47882
  } else if (advCheck.success && !advCheck.skipped) {
47846
- ctx.priorAdversarialIterations = undefined;
47883
+ this.priorAdversarialByStory.delete(ctx.story.id);
47847
47884
  }
47848
47885
  } else if (retrySkipChecks?.has("adversarial")) {
47849
- ctx.priorAdversarialIterations = undefined;
47886
+ this.priorAdversarialByStory.delete(ctx.story.id);
47850
47887
  }
47851
47888
  const semCheck = result.builtIn.checks?.find((c) => c.check === "semantic");
47852
47889
  if (semCheck) {
47853
47890
  if (!semCheck.success && !semCheck.skipped) {
47854
- const prior = ctx.priorSemanticIterations ?? [];
47891
+ const prior = this.priorSemanticByStory.get(ctx.story.id) ?? [];
47855
47892
  const findingsBefore = prior.length > 0 ? prior[prior.length - 1].findingsAfter ?? [] : [];
47856
47893
  const findingsAfter = semCheck.findings ?? [];
47857
47894
  const now = new Date().toISOString();
47858
- const newIteration = {
47895
+ const next = {
47859
47896
  iterationNum: prior.length + 1,
47860
47897
  findingsBefore,
47861
47898
  fixesApplied: [],
@@ -47864,12 +47901,18 @@ class ReviewOrchestrator {
47864
47901
  startedAt: now,
47865
47902
  finishedAt: now
47866
47903
  };
47867
- ctx.priorSemanticIterations = [...prior, newIteration];
47904
+ this.priorSemanticByStory.set(ctx.story.id, [...prior, next]);
47905
+ logger?.debug("review", "Semantic iteration recorded", {
47906
+ storyId: ctx.story.id,
47907
+ iterationNum: next.iterationNum,
47908
+ outcome: next.outcome,
47909
+ findingsCount: findingsAfter.length
47910
+ });
47868
47911
  } else if (semCheck.success && !semCheck.skipped) {
47869
- ctx.priorSemanticIterations = undefined;
47912
+ this.priorSemanticByStory.delete(ctx.story.id);
47870
47913
  }
47871
47914
  } else if (retrySkipChecks?.has("semantic")) {
47872
- ctx.priorSemanticIterations = undefined;
47915
+ this.priorSemanticByStory.delete(ctx.story.id);
47873
47916
  }
47874
47917
  return result;
47875
47918
  }
@@ -48561,6 +48604,7 @@ var init_completion = __esm(() => {
48561
48604
  init_logger2();
48562
48605
  init_metrics();
48563
48606
  init_prd();
48607
+ init_orchestrator2();
48564
48608
  init_event_bus();
48565
48609
  completionStage = {
48566
48610
  name: "completion",
@@ -48594,6 +48638,7 @@ var init_completion = __esm(() => {
48594
48638
  }
48595
48639
  for (const completedStory of ctx.stories) {
48596
48640
  markStoryPassed(ctx.prd, completedStory.id);
48641
+ reviewOrchestrator.clearStory(completedStory.id);
48597
48642
  const costPerStory = sessionCost / ctx.stories.length;
48598
48643
  logger.info("completion", "Story passed", {
48599
48644
  storyId: completedStory.id,
@@ -55138,7 +55183,7 @@ var package_default;
55138
55183
  var init_package = __esm(() => {
55139
55184
  package_default = {
55140
55185
  name: "@nathapp/nax",
55141
- version: "0.65.4",
55186
+ version: "0.65.5",
55142
55187
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
55143
55188
  type: "module",
55144
55189
  bin: {
@@ -55149,10 +55194,13 @@ var init_package = __esm(() => {
55149
55194
  dev: "bun run bin/nax.ts",
55150
55195
  build: 'bun build bin/nax.ts --outdir dist --target bun --define "GIT_COMMIT=\\"$(git rev-parse --short HEAD)\\""',
55151
55196
  typecheck: "bun x tsc --noEmit",
55152
- lint: "bun x biome check src/ bin/ && bun run check:no-real-global-nax",
55197
+ lint: "bun x biome check src/ bin/ && bun run check:no-real-global-nax && bun run check:alias-internals && bun run check:deep-relatives",
55153
55198
  "lint:json": "bun x biome check src/ bin/ --reporter json",
55154
55199
  "lint:fix": "bun x biome check --write src/ bin/",
55155
55200
  "check:no-real-global-nax": "bun run scripts/check-no-real-global-nax.ts",
55201
+ "check:alias-internals": "bun run scripts/check-alias-internals.ts",
55202
+ "check:deep-relatives": "bun run scripts/check-deep-relatives.ts",
55203
+ "check:deep-relatives:update": "bun run scripts/check-deep-relatives.ts --update-baseline",
55156
55204
  release: "bun scripts/release.ts",
55157
55205
  test: "bun run scripts/run-tests.ts",
55158
55206
  "test:bail": "bun run scripts/run-tests.ts --bail",
@@ -55224,8 +55272,8 @@ var init_version = __esm(() => {
55224
55272
  NAX_VERSION = package_default.version;
55225
55273
  NAX_COMMIT = (() => {
55226
55274
  try {
55227
- if (/^[0-9a-f]{6,10}$/.test("006c297f"))
55228
- return "006c297f";
55275
+ if (/^[0-9a-f]{6,10}$/.test("a329264b"))
55276
+ return "a329264b";
55229
55277
  } catch {}
55230
55278
  try {
55231
55279
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -58380,6 +58428,7 @@ async function handlePipelineFailure(ctx, pipelineResult) {
58380
58428
  break;
58381
58429
  case "fail":
58382
58430
  markStoryFailed(prd, ctx.story.id, pipelineResult.context.tddFailureCategory, pipelineResult.stoppedAtStage, ctx.statusWriter);
58431
+ reviewOrchestrator.clearStory(ctx.story.id);
58383
58432
  await savePRD(prd, ctx.prdPath);
58384
58433
  prdDirty = true;
58385
58434
  logger?.error("pipeline", "Story failed", { storyId: ctx.story.id, reason: pipelineResult.reason });
@@ -58442,6 +58491,7 @@ var init_pipeline_result_handler = __esm(() => {
58442
58491
  init_logger2();
58443
58492
  init_event_bus();
58444
58493
  init_prd();
58494
+ init_orchestrator2();
58445
58495
  init_bun_deps();
58446
58496
  init_git();
58447
58497
  init_manager3();
@@ -58515,6 +58565,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
58515
58565
  });
58516
58566
  } catch (error48) {
58517
58567
  markStoryFailed(prd, story.id, "dependency-prep", "worktree-dependencies", ctx.statusWriter);
58568
+ reviewOrchestrator.clearStory(story.id);
58518
58569
  await savePRD(prd, ctx.prdPath);
58519
58570
  try {
58520
58571
  await _iterationRunnerDeps.worktreeManager.remove(ctx.workdir, story.id);
@@ -58648,6 +58699,7 @@ var init_iteration_runner = __esm(() => {
58648
58699
  init_runner3();
58649
58700
  init_stages();
58650
58701
  init_prd();
58702
+ init_orchestrator2();
58651
58703
  init_git();
58652
58704
  init_dependencies();
58653
58705
  init_manager3();
@@ -94915,6 +94967,7 @@ init_test_path();
94915
94967
  init_hooks();
94916
94968
  init_logger2();
94917
94969
  init_prd();
94970
+ init_orchestrator2();
94918
94971
  init_git();
94919
94972
  init_crash_recovery();
94920
94973
  init_story_context();
@@ -95038,6 +95091,7 @@ async function runCompletionPhase(options) {
95038
95091
  await writeExitSummary(options.logFilePath, options.totalCost, options.iterations, options.storiesCompleted, durationMs);
95039
95092
  logger?.debug("execution", "Completion phase \u2014 auto-committing dirty files");
95040
95093
  await autoCommitIfDirty(options.workdir, "run.complete", "run-summary", options.feature);
95094
+ reviewOrchestrator.reset();
95041
95095
  await options.runtime?.close();
95042
95096
  logger?.debug("execution", "Completion phase done \u2014 returning to runner");
95043
95097
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.65.4",
3
+ "version": "0.65.5",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,10 +11,13 @@
11
11
  "dev": "bun run bin/nax.ts",
12
12
  "build": "bun build bin/nax.ts --outdir dist --target bun --define \"GIT_COMMIT=\\\"$(git rev-parse --short HEAD)\\\"\"",
13
13
  "typecheck": "bun x tsc --noEmit",
14
- "lint": "bun x biome check src/ bin/ && bun run check:no-real-global-nax",
14
+ "lint": "bun x biome check src/ bin/ && bun run check:no-real-global-nax && bun run check:alias-internals && bun run check:deep-relatives",
15
15
  "lint:json": "bun x biome check src/ bin/ --reporter json",
16
16
  "lint:fix": "bun x biome check --write src/ bin/",
17
17
  "check:no-real-global-nax": "bun run scripts/check-no-real-global-nax.ts",
18
+ "check:alias-internals": "bun run scripts/check-alias-internals.ts",
19
+ "check:deep-relatives": "bun run scripts/check-deep-relatives.ts",
20
+ "check:deep-relatives:update": "bun run scripts/check-deep-relatives.ts --update-baseline",
18
21
  "release": "bun scripts/release.ts",
19
22
  "test": "bun run scripts/run-tests.ts",
20
23
  "test:bail": "bun run scripts/run-tests.ts --bail",