@nathapp/nax 0.75.5 → 0.75.6

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 +201 -101
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -17377,7 +17377,11 @@ var init_schemas_review = __esm(() => {
17377
17377
  maxRequotes: 5
17378
17378
  }),
17379
17379
  excludePatterns: exports_external.array(exports_external.string()).optional(),
17380
- demandInspectionTrail: exports_external.boolean().default(true)
17380
+ demandInspectionTrail: exports_external.boolean().default(true),
17381
+ recurrenceDemotion: exports_external.object({
17382
+ enabled: exports_external.boolean().default(false),
17383
+ maxBlockingRounds: exports_external.number().int().min(1).default(2)
17384
+ }).default({ enabled: false, maxBlockingRounds: 2 })
17381
17385
  });
17382
17386
  AdversarialReviewConfigSchema = exports_external.object({
17383
17387
  model: ConfiguredModelSchema.default("balanced"),
@@ -17641,6 +17645,7 @@ var init_schemas3 = __esm(() => {
17641
17645
  rules: [],
17642
17646
  timeoutMs: 600000,
17643
17647
  demandInspectionTrail: true,
17648
+ recurrenceDemotion: { enabled: false, maxBlockingRounds: 2 },
17644
17649
  substantiation: {
17645
17650
  requote: true,
17646
17651
  maxRequotes: 5
@@ -21657,7 +21662,7 @@ class AcpAgentAdapter {
21657
21662
  }
21658
21663
  }
21659
21664
  }
21660
- var MAX_AGENT_OUTPUT_CHARS = 5000, INTERACTION_TIMEOUT_MS, AGENT_REGISTRY, DEFAULT_ENTRY, ACP_ADAPTER_NAMES;
21665
+ var INTERACTION_TIMEOUT_MS, AGENT_REGISTRY, DEFAULT_ENTRY, ACP_ADAPTER_NAMES;
21661
21666
  var init_adapter = __esm(() => {
21662
21667
  init_errors();
21663
21668
  init_logger2();
@@ -22947,11 +22952,38 @@ var init_compose = __esm(() => {
22947
22952
 
22948
22953
  // src/review/truncation.ts
22949
22954
  function looksLikeTruncatedJson(raw) {
22950
- return raw.trimEnd().length >= MAX_AGENT_OUTPUT_CHARS - 100;
22955
+ const text = raw.trimEnd();
22956
+ if (text.length === 0)
22957
+ return false;
22958
+ let depth = 0;
22959
+ let inString = false;
22960
+ let escaped = false;
22961
+ let opened = false;
22962
+ for (const ch of text) {
22963
+ if (escaped) {
22964
+ escaped = false;
22965
+ continue;
22966
+ }
22967
+ if (inString) {
22968
+ if (ch === "\\")
22969
+ escaped = true;
22970
+ else if (ch === '"')
22971
+ inString = false;
22972
+ continue;
22973
+ }
22974
+ if (ch === '"') {
22975
+ inString = true;
22976
+ continue;
22977
+ }
22978
+ if (ch === "{" || ch === "[") {
22979
+ depth++;
22980
+ opened = true;
22981
+ } else if (ch === "}" || ch === "]") {
22982
+ depth--;
22983
+ }
22984
+ }
22985
+ return opened && (inString || depth > 0);
22951
22986
  }
22952
- var init_truncation = __esm(() => {
22953
- init_adapter();
22954
- });
22955
22987
 
22956
22988
  // src/utils/llm-json.ts
22957
22989
  function extractJsonFromMarkdown(text) {
@@ -23094,9 +23126,9 @@ function makeParseRetryStrategy(opts) {
23094
23126
  }
23095
23127
  };
23096
23128
  }
23129
+ var UNPARSED_PREVIEW_BYTES = 600;
23097
23130
  var init_parse_retry = __esm(() => {
23098
23131
  init_logger2();
23099
- init_truncation();
23100
23132
  init_types4();
23101
23133
  });
23102
23134
 
@@ -23114,7 +23146,7 @@ function makeTieredParseRetryStrategy(opts) {
23114
23146
  if (attempt >= opts.maxAttempts - 1) {
23115
23147
  return { retry: false, fallback: opts.exhaustedFallback(inspection, ctx.lastOutput) };
23116
23148
  }
23117
- const isTruncated = ctx.lastOutput.trimEnd().length >= MAX_AGENT_OUTPUT_CHARS - 100;
23149
+ const isTruncated = looksLikeTruncatedJson(ctx.lastOutput);
23118
23150
  const logger = opts._logger ?? getSafeLogger();
23119
23151
  logger?.warn(opts.reviewerKind, `Parse retry \u2014 ${inspection.kind ?? "unknown"}`, {
23120
23152
  storyId: ctx.storyId,
@@ -23128,7 +23160,6 @@ function makeTieredParseRetryStrategy(opts) {
23128
23160
  }
23129
23161
  var init_tiered_parse_retry = __esm(() => {
23130
23162
  init_logger2();
23131
- init_adapter();
23132
23163
  init_types4();
23133
23164
  });
23134
23165
 
@@ -36189,6 +36220,89 @@ var init_finding_filters = __esm(() => {
36189
36220
  init_ac_quote_validator();
36190
36221
  });
36191
36222
 
36223
+ // src/review/recurrence-demotion.ts
36224
+ function normalizeIssueText(s) {
36225
+ return s.replace(/`/g, "").replace(/\s+/g, " ").trim().toLowerCase().slice(0, MAX_ISSUE_PREFIX);
36226
+ }
36227
+ function normalizeFingerprintPath(file3) {
36228
+ return (file3 ?? "").replace(/\\/g, "/").replace(/^(?:\.{1,2}\/)+/, "");
36229
+ }
36230
+ function fingerprintFor(file3, category, text, acIndex) {
36231
+ const normFile = normalizeFingerprintPath(file3);
36232
+ if (typeof acIndex === "number" && Number.isInteger(acIndex) && acIndex >= 1) {
36233
+ return `${normFile}|ac${acIndex}`;
36234
+ }
36235
+ return `${normFile}|${category ?? ""}|${normalizeIssueText(text).slice(0, FP_ISSUE_PREFIX)}`;
36236
+ }
36237
+ function lookupPriorAppearance(priorCounts, finding) {
36238
+ const acKey = finding.acIndex === undefined ? undefined : priorCounts.get(fingerprintFor(finding.file, finding.category, finding.issue, finding.acIndex));
36239
+ const proseKey = priorCounts.get(fingerprintFor(finding.file, finding.category, finding.issue));
36240
+ if (!acKey)
36241
+ return proseKey;
36242
+ if (!proseKey)
36243
+ return acKey;
36244
+ return acKey.count >= proseKey.count ? acKey : proseKey;
36245
+ }
36246
+ function countPriorAppearances(priorIterations, source = "adversarial-review") {
36247
+ const counts = new Map;
36248
+ for (const it of priorIterations) {
36249
+ const seenThisIter = new Map;
36250
+ for (const f of it.findingsAfter ?? []) {
36251
+ if (f.source !== source)
36252
+ continue;
36253
+ const acIndex = typeof f.meta?.acIndex === "number" ? f.meta.acIndex : undefined;
36254
+ seenThisIter.set(fingerprintFor(f.file, f.category, f.message), f.severity);
36255
+ if (acIndex !== undefined) {
36256
+ seenThisIter.set(fingerprintFor(f.file, f.category, f.message, acIndex), f.severity);
36257
+ }
36258
+ }
36259
+ for (const [fp, sev] of seenThisIter) {
36260
+ const cur = counts.get(fp);
36261
+ counts.set(fp, { count: (cur?.count ?? 0) + 1, lastSeverity: sev });
36262
+ }
36263
+ }
36264
+ return counts;
36265
+ }
36266
+ function tagCoverageGap(findings) {
36267
+ return findings.map((f) => ({ ...f, meta: { ...f.meta ?? {}, coverageGap: true } }));
36268
+ }
36269
+ function classifyRecurrence(accepted, priorIterations, cfg, testFileMatch, threshold, source = "adversarial-review") {
36270
+ const blocking = [];
36271
+ const advisory = [];
36272
+ const demoted = [];
36273
+ if (!cfg.enabled) {
36274
+ for (const f of accepted)
36275
+ (isBlockingSeverity(f.severity, threshold) ? blocking : advisory).push(f);
36276
+ return { blocking, advisory, demoted };
36277
+ }
36278
+ const priorCounts = countPriorAppearances(priorIterations, source);
36279
+ for (const f of accepted) {
36280
+ if (f.category === "test-gap" && testFileMatch(f.file) && isBlockingSeverity(f.severity, threshold)) {
36281
+ blocking.push(f);
36282
+ continue;
36283
+ }
36284
+ if (!isBlockingSeverity(f.severity, threshold)) {
36285
+ advisory.push(f);
36286
+ continue;
36287
+ }
36288
+ const prior = lookupPriorAppearance(priorCounts, f);
36289
+ const n = (prior?.count ?? 0) + 1;
36290
+ const prevWasBlocking = prior !== undefined && isBlockingSeverity(prior.lastSeverity, threshold);
36291
+ if (n >= cfg.maxBlockingRounds + 1) {
36292
+ demoted.push(f);
36293
+ } else if (n === 1 || prevWasBlocking) {
36294
+ blocking.push(f);
36295
+ } else {
36296
+ advisory.push(f);
36297
+ }
36298
+ }
36299
+ return { blocking, advisory, demoted };
36300
+ }
36301
+ var MAX_ISSUE_PREFIX = 160, FP_ISSUE_PREFIX = 48;
36302
+ var init_recurrence_demotion = __esm(() => {
36303
+ init_adversarial_helpers();
36304
+ });
36305
+
36192
36306
  // src/review/requote-response.ts
36193
36307
  function parseRequoteResponse(output) {
36194
36308
  const parsed = tryParseLLMJson(output);
@@ -36236,6 +36350,17 @@ function isRecord(value) {
36236
36350
  }
36237
36351
  var init_requote_response = () => {};
36238
36352
 
36353
+ // src/operations/_review-fallback.ts
36354
+ function reviewExhaustedFallback(lastOutput, failOpen) {
36355
+ const unparsedPreview = previewOutput(lastOutput, UNPARSED_PREVIEW_BYTES);
36356
+ if (!/"passed"\s*:\s*false/.test(lastOutput))
36357
+ return { ...failOpen, unparsedPreview };
36358
+ return { ...failOpen, passed: false, failOpen: false, looksLikeFail: true, unparsedPreview };
36359
+ }
36360
+ var init__review_fallback = __esm(() => {
36361
+ init_retry();
36362
+ });
36363
+
36239
36364
  // src/operations/semantic-review.ts
36240
36365
  function withRepromptMarker(output, info) {
36241
36366
  const parsed = tryParseLLMJson(output);
@@ -36454,7 +36579,9 @@ var init_semantic_review = __esm(() => {
36454
36579
  init_logger2();
36455
36580
  init_prompts();
36456
36581
  init_finding_filters();
36582
+ init_recurrence_demotion();
36457
36583
  init_requote_response();
36584
+ init__review_fallback();
36458
36585
  FAIL_OPEN = {
36459
36586
  passed: true,
36460
36587
  findings: [],
@@ -36478,7 +36605,8 @@ var init_semantic_review = __esm(() => {
36478
36605
  invalid: () => ReviewPromptBuilder.jsonRetry(),
36479
36606
  truncated: () => ReviewPromptBuilder.jsonRetryCondensed({ blockingThreshold: input.blockingThreshold })
36480
36607
  },
36481
- exhaustedFallback: (lastOutput) => /"passed"\s*:\s*false/.test(lastOutput) ? { passed: false, findings: [], normalizedFindings: [], acDropped: [], looksLikeFail: true } : FAIL_OPEN,
36608
+ exhaustedFallback: (lastOutput) => reviewExhaustedFallback(lastOutput, FAIL_OPEN),
36609
+ outputPreviewBytes: UNPARSED_PREVIEW_BYTES,
36482
36610
  logContext: { blockingThreshold: input.blockingThreshold ?? "error" }
36483
36611
  }),
36484
36612
  hopBody: semanticReviewHopBody,
@@ -36510,6 +36638,7 @@ var init_semantic_review = __esm(() => {
36510
36638
  repromptEvent
36511
36639
  };
36512
36640
  }
36641
+ const unparsedPreview = previewOutput(output, UNPARSED_PREVIEW_BYTES);
36513
36642
  if (/"passed"\s*:\s*false/.test(output)) {
36514
36643
  return {
36515
36644
  passed: false,
@@ -36517,10 +36646,11 @@ var init_semantic_review = __esm(() => {
36517
36646
  normalizedFindings: [],
36518
36647
  acDropped: [],
36519
36648
  looksLikeFail: true,
36649
+ unparsedPreview,
36520
36650
  repromptEvent
36521
36651
  };
36522
36652
  }
36523
- return FAIL_OPEN;
36653
+ return { ...FAIL_OPEN, unparsedPreview };
36524
36654
  },
36525
36655
  async verify(parsed, input, _verifyCtx) {
36526
36656
  if (parsed.failOpen || parsed.looksLikeFail)
@@ -36532,84 +36662,30 @@ var init_semantic_review = __esm(() => {
36532
36662
  const sanitized = sanitizeRefModeFindings(findings, input.mode, threshold);
36533
36663
  const substantiated = await substantiateSemanticEvidence(sanitized, input.mode, input.workdir, input.story.id, threshold, input.repoRoot);
36534
36664
  const { accepted, dropped } = filterByAcGroundingMinimal(substantiated, input.story.acceptanceCriteria);
36535
- const blocking = accepted.filter((f) => isBlockingSeverity(f.severity, threshold));
36665
+ const isTestFile3 = semanticTestFileMatch(input);
36666
+ const recurrenceCfg = input.semanticConfig.recurrenceDemotion ?? { enabled: false, maxBlockingRounds: 2 };
36667
+ const {
36668
+ blocking,
36669
+ advisory: subThreshold,
36670
+ demoted
36671
+ } = classifyRecurrence(accepted, input.priorSemanticIterations ?? [], recurrenceCfg, isTestFile3, threshold, "semantic-review");
36672
+ const advisoryFindings = [
36673
+ ...toReviewFindings(subThreshold.filter((f) => isBlockingSeverity(f.severity, threshold)), { isTestFile: isTestFile3 }),
36674
+ ...tagCoverageGap(toReviewFindings(demoted, { isTestFile: isTestFile3 }))
36675
+ ];
36536
36676
  const passed = blocking.length === 0 && (parsed.passed || accepted.length > 0);
36537
36677
  return {
36538
36678
  ...parsed,
36539
36679
  passed,
36540
36680
  findings: accepted,
36541
- normalizedFindings: toReviewFindings(blocking, { isTestFile: semanticTestFileMatch(input) }),
36681
+ normalizedFindings: toReviewFindings(blocking, { isTestFile: isTestFile3 }),
36682
+ advisoryFindings,
36542
36683
  acDropped: dropped
36543
36684
  };
36544
36685
  }
36545
36686
  };
36546
36687
  });
36547
36688
 
36548
- // src/review/recurrence-demotion.ts
36549
- function normalizeIssueText(s) {
36550
- return s.replace(/`/g, "").replace(/\s+/g, " ").trim().toLowerCase().slice(0, MAX_ISSUE_PREFIX);
36551
- }
36552
- function fingerprintFor(file3, category, text) {
36553
- const normFile = (file3 ?? "").replace(/\\/g, "/");
36554
- return `${normFile}|${category ?? ""}|${normalizeIssueText(text).slice(0, FP_ISSUE_PREFIX)}`;
36555
- }
36556
- function countPriorAppearances(priorIterations) {
36557
- const counts = new Map;
36558
- for (const it of priorIterations) {
36559
- const seenThisIter = new Map;
36560
- for (const f of it.findingsAfter ?? []) {
36561
- if (f.source !== "adversarial-review")
36562
- continue;
36563
- const fp = fingerprintFor(f.file, f.category, f.message);
36564
- seenThisIter.set(fp, f.severity);
36565
- }
36566
- for (const [fp, sev] of seenThisIter) {
36567
- const cur = counts.get(fp);
36568
- counts.set(fp, { count: (cur?.count ?? 0) + 1, lastSeverity: sev });
36569
- }
36570
- }
36571
- return counts;
36572
- }
36573
- function tagCoverageGap(findings) {
36574
- return findings.map((f) => ({ ...f, meta: { ...f.meta ?? {}, coverageGap: true } }));
36575
- }
36576
- function classifyRecurrence(accepted, priorIterations, cfg, testFileMatch, threshold) {
36577
- const blocking = [];
36578
- const advisory = [];
36579
- const demoted = [];
36580
- if (!cfg.enabled) {
36581
- for (const f of accepted)
36582
- (isBlockingSeverity(f.severity, threshold) ? blocking : advisory).push(f);
36583
- return { blocking, advisory, demoted };
36584
- }
36585
- const priorCounts = countPriorAppearances(priorIterations);
36586
- for (const f of accepted) {
36587
- if (f.category === "test-gap" && testFileMatch(f.file) && isBlockingSeverity(f.severity, threshold)) {
36588
- blocking.push(f);
36589
- continue;
36590
- }
36591
- if (!isBlockingSeverity(f.severity, threshold)) {
36592
- advisory.push(f);
36593
- continue;
36594
- }
36595
- const prior = priorCounts.get(fingerprintFor(f.file, f.category, f.issue));
36596
- const n = (prior?.count ?? 0) + 1;
36597
- const prevWasBlocking = prior !== undefined && isBlockingSeverity(prior.lastSeverity, threshold);
36598
- if (n >= cfg.maxBlockingRounds + 1) {
36599
- demoted.push(f);
36600
- } else if (n === 1 || prevWasBlocking) {
36601
- blocking.push(f);
36602
- } else {
36603
- advisory.push(f);
36604
- }
36605
- }
36606
- return { blocking, advisory, demoted };
36607
- }
36608
- var MAX_ISSUE_PREFIX = 160, FP_ISSUE_PREFIX = 48;
36609
- var init_recurrence_demotion = __esm(() => {
36610
- init_adversarial_helpers();
36611
- });
36612
-
36613
36689
  // src/operations/adversarial-review.ts
36614
36690
  function withRepromptMarker2(output, info) {
36615
36691
  const parsed = tryParseLLMJson(output);
@@ -36794,7 +36870,8 @@ var FAIL_OPEN2, ADVERSARIAL_REQUOTE_RECOVERED_EVENT = "review.adversarial.findin
36794
36870
  invalid: () => ReviewPromptBuilder.jsonRetry(),
36795
36871
  truncated: () => ReviewPromptBuilder.jsonRetryCondensed({ blockingThreshold: input.blockingThreshold })
36796
36872
  },
36797
- exhaustedFallback: (lastOutput) => /"passed"\s*:\s*false/.test(lastOutput) ? { passed: false, findings: [], normalizedFindings: [], acDropped: [], looksLikeFail: true } : FAIL_OPEN2,
36873
+ exhaustedFallback: (lastOutput) => reviewExhaustedFallback(lastOutput, FAIL_OPEN2),
36874
+ outputPreviewBytes: UNPARSED_PREVIEW_BYTES,
36798
36875
  logContext: { blockingThreshold: input.blockingThreshold ?? "error" }
36799
36876
  }), adversarialReviewOp;
36800
36877
  var init_adversarial_review = __esm(() => {
@@ -36806,6 +36883,7 @@ var init_adversarial_review = __esm(() => {
36806
36883
  init_finding_filters();
36807
36884
  init_recurrence_demotion();
36808
36885
  init_requote_response();
36886
+ init__review_fallback();
36809
36887
  FAIL_OPEN2 = {
36810
36888
  passed: true,
36811
36889
  findings: [],
@@ -38726,7 +38804,6 @@ var init_ground = __esm(() => {
38726
38804
  init_errors();
38727
38805
  init_logger2();
38728
38806
  init_prompts();
38729
- init_truncation();
38730
38807
  groundOp = {
38731
38808
  kind: "run",
38732
38809
  name: "ground",
@@ -42163,11 +42240,11 @@ var init_findings = __esm(() => {
42163
42240
  init_cycle();
42164
42241
  });
42165
42242
 
42166
- // src/review/adversarial-iteration-store.ts
42167
- function getAdversarialIterations(store, storyId) {
42243
+ // src/review/review-iteration-store.ts
42244
+ function getReviewIterations(store, storyId) {
42168
42245
  return store.get(storyId) ?? [];
42169
42246
  }
42170
- function recordAdversarialIteration(store, storyId, roundFindings) {
42247
+ function recordReviewIteration(store, storyId, roundFindings) {
42171
42248
  const prior = store.get(storyId) ?? [];
42172
42249
  const findingsBefore = prior.length > 0 ? prior[prior.length - 1].findingsAfter : [];
42173
42250
  const findingsAfter = [...roundFindings];
@@ -42183,7 +42260,7 @@ function recordAdversarialIteration(store, storyId, roundFindings) {
42183
42260
  };
42184
42261
  store.set(storyId, [...prior, iteration]);
42185
42262
  }
42186
- var init_adversarial_iteration_store = __esm(() => {
42263
+ var init_review_iteration_store = __esm(() => {
42187
42264
  init_findings();
42188
42265
  });
42189
42266
 
@@ -42626,7 +42703,7 @@ var package_default;
42626
42703
  var init_package = __esm(() => {
42627
42704
  package_default = {
42628
42705
  name: "@nathapp/nax",
42629
- version: "0.75.5",
42706
+ version: "0.75.6",
42630
42707
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
42631
42708
  type: "module",
42632
42709
  bin: {
@@ -42730,8 +42807,8 @@ var init_version = __esm(() => {
42730
42807
  NAX_VERSION = package_default.version;
42731
42808
  NAX_COMMIT = (() => {
42732
42809
  try {
42733
- if (/^[0-9a-f]{6,10}$/.test("c8f74c5f"))
42734
- return "c8f74c5f";
42810
+ if (/^[0-9a-f]{6,10}$/.test("064f9083"))
42811
+ return "064f9083";
42735
42812
  } catch {}
42736
42813
  try {
42737
42814
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -42778,6 +42855,8 @@ function toPersistedEntry(entry, epochMs) {
42778
42855
  blockingThreshold: entry.blockingThreshold ?? "error",
42779
42856
  result: entry.result,
42780
42857
  advisoryFindings: entry.advisoryFindings ?? null,
42858
+ acDropped: entry.acDropped ?? null,
42859
+ ...entry.parsed ? {} : { unparsedPreview: entry.unparsedPreview ?? null },
42781
42860
  diffAvailable: entry.diffAvailable ?? null,
42782
42861
  adversarialDropAnalysis: entry.adversarialDropAnalysis ?? null,
42783
42862
  adversarialAcceptAnalysis: entry.adversarialAcceptAnalysis ?? null
@@ -44749,7 +44828,7 @@ var init_review = __esm(() => {
44749
44828
  init_category_fix_target();
44750
44829
  init_finding_filters();
44751
44830
  init_ac_quote_validator();
44752
- init_adversarial_iteration_store();
44831
+ init_review_iteration_store();
44753
44832
  init_ac_structural_counterfactual();
44754
44833
  init_adversarial();
44755
44834
  init_semantic_evidence();
@@ -48172,6 +48251,8 @@ function attachReviewAuditSubscriber(bus, auditor, runId) {
48172
48251
  blockingThreshold: event.blockingThreshold,
48173
48252
  result: event.result,
48174
48253
  advisoryFindings: event.advisoryFindings,
48254
+ acDropped: event.acDropped ? [...event.acDropped] : undefined,
48255
+ unparsedPreview: event.unparsedPreview,
48175
48256
  diffAvailable: event.diffAvailable,
48176
48257
  adversarialDropAnalysis: event.adversarialDropAnalysis,
48177
48258
  adversarialAcceptAnalysis: event.adversarialAcceptAnalysis
@@ -49863,6 +49944,7 @@ function createRuntime(config2, workdir, opts) {
49863
49944
  const logger = getLogger();
49864
49945
  const quarantineMemo = createQuarantineMemo();
49865
49946
  const adversarialIterations = new Map;
49947
+ const semanticIterations = new Map;
49866
49948
  const rectificationOscillations = new Map;
49867
49949
  let closed = false;
49868
49950
  return {
@@ -49886,6 +49968,7 @@ function createRuntime(config2, workdir, opts) {
49886
49968
  logger,
49887
49969
  quarantineMemo,
49888
49970
  adversarialIterations,
49971
+ semanticIterations,
49889
49972
  rectificationOscillations,
49890
49973
  get signal() {
49891
49974
  return controller.signal;
@@ -59246,11 +59329,12 @@ function toReviewDecisionPayload(opName, output) {
59246
59329
  const reviewer = opName === "semantic-review" ? "semantic" : opName === "adversarial-review" ? "adversarial" : null;
59247
59330
  if (!reviewer)
59248
59331
  return null;
59332
+ const unparsedPreview = typeof record2.unparsedPreview === "string" ? record2.unparsedPreview : undefined;
59249
59333
  if (record2.failOpen === true) {
59250
- return { reviewer, parsed: false, passed: true, failOpen: true, result: null };
59334
+ return { reviewer, parsed: false, passed: true, failOpen: true, result: null, unparsedPreview };
59251
59335
  }
59252
59336
  if (record2.looksLikeFail === true) {
59253
- return { reviewer, parsed: false, passed: false, looksLikeFail: true, result: null };
59337
+ return { reviewer, parsed: false, passed: false, looksLikeFail: true, result: null, unparsedPreview };
59254
59338
  }
59255
59339
  if (typeof record2.passed !== "boolean" || !Array.isArray(record2.findings)) {
59256
59340
  return null;
@@ -59272,7 +59356,8 @@ function toReviewDecisionPayload(opName, output) {
59272
59356
  parsed: true,
59273
59357
  passed: record2.passed,
59274
59358
  result: { passed: record2.passed, findings: record2.findings },
59275
- acDropped
59359
+ acDropped,
59360
+ ...Array.isArray(record2.advisoryFindings) ? { advisoryFindings: record2.advisoryFindings } : {}
59276
59361
  };
59277
59362
  }
59278
59363
  function emitReviewDecision(ctx, opName, output) {
@@ -59293,7 +59378,10 @@ function emitReviewDecision(ctx, opName, output) {
59293
59378
  looksLikeFail: payload.parsed ? undefined : payload.looksLikeFail,
59294
59379
  failOpen: payload.parsed ? false : payload.failOpen,
59295
59380
  passed: payload.passed,
59296
- result: payload.result
59381
+ result: payload.result,
59382
+ advisoryFindings: payload.parsed ? payload.advisoryFindings : undefined,
59383
+ acDropped: payload.parsed ? payload.acDropped : undefined,
59384
+ unparsedPreview: payload.parsed ? undefined : payload.unparsedPreview
59297
59385
  });
59298
59386
  }
59299
59387
  function logUnifiedReviewPhaseStart(storyId, opName) {
@@ -59430,13 +59518,19 @@ async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = fa
59430
59518
  dispatchInput = await refreshReviewInputForDispatch(opName, dispatchInput);
59431
59519
  let advIterationBefore = 0;
59432
59520
  if (opName === "adversarial-review" && ctx.storyId) {
59433
- const priorIterations = getAdversarialIterations(ctx.runtime.adversarialIterations, ctx.storyId);
59521
+ const priorIterations = getReviewIterations(ctx.runtime.adversarialIterations, ctx.storyId);
59434
59522
  advIterationBefore = priorIterations.length;
59435
59523
  dispatchInput = {
59436
59524
  ...dispatchInput,
59437
59525
  priorAdversarialIterations: priorIterations
59438
59526
  };
59439
59527
  }
59528
+ if (opName === "semantic-review" && ctx.storyId) {
59529
+ dispatchInput = {
59530
+ ...dispatchInput,
59531
+ priorSemanticIterations: getReviewIterations(ctx.runtime.semanticIterations, ctx.storyId)
59532
+ };
59533
+ }
59440
59534
  if (isTddPhase) {
59441
59535
  logger?.info("tdd", `-> Session: ${opName}`, { storyId: ctx.storyId, role: opName, ...progressData });
59442
59536
  } else if (isThreeSession && opName === "full-suite-gate") {
@@ -59458,11 +59552,18 @@ async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = fa
59458
59552
  emitReviewDecision(ctx, opName, output);
59459
59553
  if (opName === "adversarial-review" && ctx.storyId) {
59460
59554
  const advOut = output;
59461
- recordAdversarialIteration(ctx.runtime.adversarialIterations, ctx.storyId, [
59555
+ recordReviewIteration(ctx.runtime.adversarialIterations, ctx.storyId, [
59462
59556
  ...advOut.normalizedFindings ?? [],
59463
59557
  ...advOut.advisoryFindings ?? []
59464
59558
  ]);
59465
59559
  }
59560
+ if (opName === "semantic-review" && ctx.storyId) {
59561
+ const semOut = output;
59562
+ recordReviewIteration(ctx.runtime.semanticIterations, ctx.storyId, [
59563
+ ...semOut.normalizedFindings ?? [],
59564
+ ...semOut.advisoryFindings ?? []
59565
+ ]);
59566
+ }
59466
59567
  logUnifiedReviewPhaseResult(ctx.storyId, opName, output);
59467
59568
  logDeterministicPhaseOutcome(ctx.storyId, opName, output, Date.now() - phaseStartedAt, isTddPhase, slot.op.stage, progressData);
59468
59569
  outcome = derivePhaseOutcome(output);
@@ -60224,6 +60325,7 @@ var init_story_orchestrator = __esm(() => {
60224
60325
  init_rectification();
60225
60326
  init_nbf_flake_triage();
60226
60327
  init_run_phase();
60328
+ init_review_decision();
60227
60329
  init_types9();
60228
60330
  });
60229
60331
 
@@ -60573,7 +60675,6 @@ async function assemblePlanInputsFromCtx(ctx) {
60573
60675
  diff: prepared.diff,
60574
60676
  excludePatterns: prepared.excludePatterns,
60575
60677
  featureCtxBlock: buildFeatureCtxBlock(ctx, "reviewer-semantic"),
60576
- priorSemanticIterations: ctx.priorSemanticIterations,
60577
60678
  resolvedTestPatterns,
60578
60679
  blockingThreshold: ctx.config.review.blockingThreshold,
60579
60680
  _refresh: {
@@ -60611,7 +60712,6 @@ async function assemblePlanInputsFromCtx(ctx) {
60611
60712
  testGlobs: prepared.testGlobs,
60612
60713
  refExcludePatterns: prepared.refExcludePatterns,
60613
60714
  featureCtxBlock: buildFeatureCtxBlock(ctx, "reviewer-adversarial"),
60614
- priorAdversarialIterations: ctx.priorAdversarialIterations,
60615
60715
  resolvedTestPatterns,
60616
60716
  blockingThreshold: ctx.config.review.blockingThreshold,
60617
60717
  _refresh: {
@@ -62113,7 +62213,7 @@ async function fanOutReporters(reporters, hook, invoke) {
62113
62213
  }
62114
62214
  }
62115
62215
  }
62116
- function wireReporters(bus, pluginRegistry, runId, startTime) {
62216
+ function wireReporters(bus, pluginRegistry, runId, startTime, projectKey) {
62117
62217
  const logger = getSafeLogger();
62118
62218
  const safe = (name, fn) => {
62119
62219
  return fn().catch((err) => logger?.warn("reporters-subscriber", `Reporter "${name}" error`, { error: String(err) })).catch(() => {});
@@ -62163,7 +62263,8 @@ function wireReporters(bus, pluginRegistry, runId, startTime) {
62163
62263
  runId,
62164
62264
  feature: ev.feature,
62165
62265
  totalStories: ev.totalStories,
62166
- startTime: new Date(startTime).toISOString()
62266
+ startTime: new Date(startTime).toISOString(),
62267
+ project: projectKey
62167
62268
  });
62168
62269
  } catch (err) {
62169
62270
  logger?.warn("plugins", `Reporter '${r.name}' onRunStart failed`, { error: err });
@@ -71317,8 +71418,6 @@ function releaseHeavyPipelineContext(ctx) {
71317
71418
  ctx.constitution = undefined;
71318
71419
  ctx.acceptanceFailures = undefined;
71319
71420
  ctx.autofixPriorIterations = undefined;
71320
- ctx.priorSemanticIterations = undefined;
71321
- ctx.priorAdversarialIterations = undefined;
71322
71421
  ctx.reviewFindings = undefined;
71323
71422
  ctx.selfVerification = undefined;
71324
71423
  ctx.tddIsolations = undefined;
@@ -71977,7 +72076,7 @@ async function executeUnified(ctx, initialPrd) {
71977
72076
  _prevRunUnsubscribers = [];
71978
72077
  const thisRunUnsubscribers = [
71979
72078
  wireHooks(pipelineEventBus, ctx.hooks, ctx.workdir, ctx.feature),
71980
- wireReporters(pipelineEventBus, ctx.pluginRegistry, ctx.runId, ctx.startTime),
72079
+ wireReporters(pipelineEventBus, ctx.pluginRegistry, ctx.runId, ctx.startTime, ctx.runtime.projectKey),
71981
72080
  wireInteraction(pipelineEventBus, ctx.interactionChain, ctx.config),
71982
72081
  wireEventsWriter(pipelineEventBus, ctx.feature, ctx.runId, ctx.workdir),
71983
72082
  wireRegistry(pipelineEventBus, ctx.feature, ctx.runId, ctx.workdir, ctx.runtime.outputDir)
@@ -74160,6 +74259,7 @@ var exports_execution = {};
74160
74259
  __export(exports_execution, {
74161
74260
  writeExitSummary: () => writeExitSummary,
74162
74261
  withIncreasingFailuresBail: () => withIncreasingFailuresBail,
74262
+ toReviewDecisionPayload: () => toReviewDecisionPayload,
74163
74263
  synthesizeBackfillMetric: () => synthesizeBackfillMetric,
74164
74264
  stopHeartbeat: () => stopHeartbeat,
74165
74265
  startHeartbeat: () => startHeartbeat2,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.75.5",
3
+ "version": "0.75.6",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {