@nathapp/nax 0.67.19 → 0.68.1

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 +256 -367
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -16763,7 +16763,6 @@ var init_schemas_debate = __esm(() => {
16763
16763
  exports_external.object({ kind: exports_external.literal("majority-fail-closed") }),
16764
16764
  exports_external.object({ kind: exports_external.literal("majority-fail-open") }),
16765
16765
  exports_external.object({ kind: exports_external.literal("judge") }),
16766
- exports_external.object({ kind: exports_external.literal("dialogue-verdict") }),
16767
16766
  exports_external.object({
16768
16767
  kind: exports_external.literal("verifier-pick"),
16769
16768
  patch: exports_external.object({
@@ -17187,7 +17186,7 @@ var init_schemas_infra = __esm(() => {
17187
17186
  });
17188
17187
 
17189
17188
  // src/config/schemas-review.ts
17190
- var SemanticReviewConfigSchema, ReviewDialogueConfigSchema, AdversarialReviewConfigSchema, ReviewConfigSchema;
17189
+ var SemanticReviewConfigSchema, AdversarialReviewConfigSchema, ReviewConfigSchema;
17191
17190
  var init_schemas_review = __esm(() => {
17192
17191
  init_zod();
17193
17192
  init_schemas_model();
@@ -17206,11 +17205,6 @@ var init_schemas_review = __esm(() => {
17206
17205
  }),
17207
17206
  excludePatterns: exports_external.array(exports_external.string()).optional()
17208
17207
  });
17209
- ReviewDialogueConfigSchema = exports_external.object({
17210
- enabled: exports_external.boolean().default(false),
17211
- maxClarificationsPerAttempt: exports_external.number().int().min(0).max(10).default(2),
17212
- maxDialogueMessages: exports_external.number().int().min(5).max(100).default(20)
17213
- });
17214
17208
  AdversarialReviewConfigSchema = exports_external.object({
17215
17209
  model: ConfiguredModelSchema.default("balanced"),
17216
17210
  diffMode: exports_external.enum(["embedded", "ref"]).default("ref"),
@@ -17242,6 +17236,7 @@ var init_schemas_review = __esm(() => {
17242
17236
  }),
17243
17237
  audit: exports_external.object({ enabled: exports_external.boolean().default(false) }).default({ enabled: false }),
17244
17238
  blockingThreshold: exports_external.enum(["error", "warning", "info"]).default("error"),
17239
+ pluginMode: exports_external.enum(["observational", "gating"]).default("observational"),
17245
17240
  semantic: SemanticReviewConfigSchema.optional(),
17246
17241
  adversarial: AdversarialReviewConfigSchema.optional()
17247
17242
  });
@@ -17426,6 +17421,7 @@ var init_schemas3 = __esm(() => {
17426
17421
  commands: {},
17427
17422
  audit: { enabled: false },
17428
17423
  blockingThreshold: "error",
17424
+ pluginMode: "observational",
17429
17425
  semantic: {
17430
17426
  model: "balanced",
17431
17427
  diffMode: "ref",
@@ -18748,10 +18744,10 @@ function _applyLegacyReviewExecutionShim(conf, warn = (msg) => {
18748
18744
  const review = result.review ?? conf.review;
18749
18745
  if (review && typeof review === "object") {
18750
18746
  let newReview = review;
18751
- const LEGACY_PLUGIN_MODE = "pluginMode";
18752
- if (LEGACY_PLUGIN_MODE in review) {
18753
- warn("review.pluginMode is a legacy field that has been removed. Remove it from your config.");
18754
- const { [LEGACY_PLUGIN_MODE]: _pm, ...rest } = review;
18747
+ const LEGACY_PLUGIN_MODE_VALUE = "per-story";
18748
+ if ("pluginMode" in review && review.pluginMode === LEGACY_PLUGIN_MODE_VALUE) {
18749
+ warn('review.pluginMode: "per-story" is a legacy value that has been removed. Remove it from your config (or set to "observational"/"gating").');
18750
+ const { pluginMode: _pm, ...rest } = review;
18755
18751
  newReview = rest;
18756
18752
  }
18757
18753
  const dialogue = newReview.dialogue;
@@ -21975,6 +21971,10 @@ function tryParseLLMJson(text) {
21975
21971
  }
21976
21972
 
21977
21973
  // src/agents/retry/parse-retry.ts
21974
+ function previewOutput(output, maxBytes) {
21975
+ const collapsed = output.replace(/\s+/g, " ").trim();
21976
+ return collapsed.length > maxBytes ? `${collapsed.slice(0, maxBytes)}\u2026` : collapsed;
21977
+ }
21978
21978
  function makeParseRetryStrategy(opts) {
21979
21979
  const parse5 = opts.parse ?? tryParseLLMJson;
21980
21980
  const checkTruncated = opts.looksTruncated ?? looksLikeTruncatedJson;
@@ -22007,16 +22007,19 @@ function makeParseRetryStrategy(opts) {
22007
22007
  const isTruncated = checkTruncated(ctx.lastOutput);
22008
22008
  const nextPrompt = isTruncated ? opts.prompts.truncated() : opts.prompts.invalid();
22009
22009
  const logger = opts._logger ?? getSafeLogger();
22010
+ const preview = opts.outputPreviewBytes && opts.outputPreviewBytes > 0 ? { outputPreview: previewOutput(ctx.lastOutput, opts.outputPreviewBytes) } : {};
22010
22011
  if (isTruncated) {
22011
22012
  logger?.warn(opts.reviewerKind, "JSON parse retry \u2014 likely truncated", {
22012
22013
  storyId: ctx.storyId,
22013
22014
  originalByteSize: ctx.lastOutput.length,
22015
+ ...preview,
22014
22016
  ...opts.logContext
22015
22017
  });
22016
22018
  } else {
22017
22019
  logger?.warn(opts.reviewerKind, "JSON parse retry \u2014 invalid shape", {
22018
22020
  storyId: ctx.storyId,
22019
22021
  originalByteSize: ctx.lastOutput.length,
22022
+ ...preview,
22020
22023
  ...opts.logContext
22021
22024
  });
22022
22025
  }
@@ -30228,9 +30231,6 @@ function resolvePersonas(debaters, stage, autoPersona) {
30228
30231
  return { ...d, persona: assigned };
30229
30232
  });
30230
30233
  }
30231
- function buildDebaterLabel(debater) {
30232
- return debater.persona ? `${debater.agent} (${debater.persona})` : debater.agent;
30233
- }
30234
30234
  var PERSONA_FRAGMENTS, PLAN_ROTATION, REVIEW_ROTATION;
30235
30235
  var init_personas = __esm(() => {
30236
30236
  PERSONA_FRAGMENTS = {
@@ -32482,33 +32482,6 @@ function llmFindingToReviewFinding(f, opts = {}) {
32482
32482
  function llmFindingsToReviewFindings(findings, opts = {}) {
32483
32483
  return findings.map((f) => llmFindingToReviewFinding(f, opts));
32484
32484
  }
32485
- function findingToReviewFinding(f, opts = {}) {
32486
- const narrowed = narrowSeverity(f.severity);
32487
- const ruleId = f.rule?.trim() ? f.rule.trim() : deriveRuleId(f.category, f.message);
32488
- const result = {
32489
- ruleId,
32490
- severity: narrowed,
32491
- file: f.file ?? "",
32492
- line: f.line ?? 0,
32493
- message: f.message
32494
- };
32495
- if (f.category)
32496
- result.category = f.category;
32497
- const effectiveSource = opts.source ?? f.source;
32498
- if (effectiveSource)
32499
- result.source = effectiveSource;
32500
- const meta3 = { ...f.meta ?? {} };
32501
- if (f.suggestion)
32502
- meta3.suggestion = f.suggestion;
32503
- if (f.severity !== narrowed)
32504
- meta3.originalSeverity = f.severity;
32505
- if (Object.keys(meta3).length > 0)
32506
- result.meta = meta3;
32507
- return result;
32508
- }
32509
- function findingsToReviewFindings(findings, opts = {}) {
32510
- return findings.map((f) => findingToReviewFinding(f, opts));
32511
- }
32512
32485
  var SEVERITY_MAP, RULE_ID_SLUG_TOKENS = 6;
32513
32486
  var init_finding_projection = __esm(() => {
32514
32487
  SEVERITY_MAP = {
@@ -35641,7 +35614,9 @@ var init_autofix_implementer = __esm(() => {
35641
35614
  session: { role: "implementer", lifetime: "warm" },
35642
35615
  config: autofixConfigSelector,
35643
35616
  build(input, _ctx) {
35644
- const prompt = RectifierPromptBuilder.reviewRectification(input.failedChecks, input.story, {
35617
+ const verifierFindings = input.findings?.filter((f) => f.source === "tdd-verifier");
35618
+ const useVerifierContext = verifierFindings !== undefined && verifierFindings.length > 0 && verifierFindings.length === input.findings?.length;
35619
+ const prompt = useVerifierContext ? RectifierPromptBuilder.verifierContext(verifierFindings) : RectifierPromptBuilder.reviewRectification(input.failedChecks, input.story, {
35645
35620
  blockingThreshold: input.blockingThreshold
35646
35621
  });
35647
35622
  return {
@@ -35869,6 +35844,29 @@ var init_debate_stateful = __esm(() => {
35869
35844
  };
35870
35845
  });
35871
35846
 
35847
+ // src/debate/selectors/judge.ts
35848
+ var RESOLVER_FALLBACK_AGENT = "synthesis", RESOLVER_FALLBACK_MODEL = "fast", judgeSelector = async (ctx) => {
35849
+ const resolverAgent = ctx.stageConfig.resolver.agent ?? RESOLVER_FALLBACK_AGENT;
35850
+ const resolverModel = ctx.stageConfig.resolver.model ?? RESOLVER_FALLBACK_MODEL;
35851
+ const proposals = ctx.proposals.map((p) => p.output);
35852
+ const output = await callOp(ctx.callContext, judgeOp, {
35853
+ proposals,
35854
+ critiques: ctx.critiques,
35855
+ debaters: ctx.debaters,
35856
+ resolverAgent,
35857
+ resolverModel,
35858
+ timeoutSeconds: ctx.stageConfig.timeoutSeconds
35859
+ });
35860
+ return {
35861
+ outcome: output.trim() ? "passed" : "failed",
35862
+ output
35863
+ };
35864
+ };
35865
+ var init_judge = __esm(() => {
35866
+ init_operations();
35867
+ init_operations();
35868
+ });
35869
+
35872
35870
  // src/debate/selectors/majority.ts
35873
35871
  function stripMarkdownFence(text) {
35874
35872
  const match = text.match(/^```(?:json)?\s*\n?([\s\S]*?)\n?```\s*$/);
@@ -35910,125 +35908,6 @@ var majorityFailClosedSelector = async (ctx) => {
35910
35908
  return { outcome: computeMajority(proposalOutputs, true) };
35911
35909
  };
35912
35910
 
35913
- // src/debate/resolvers.ts
35914
- function majorityResolver(proposals, failOpen) {
35915
- return computeMajority(proposals, failOpen);
35916
- }
35917
- var init_resolvers = __esm(() => {
35918
- init_prompts();
35919
- });
35920
-
35921
- // src/debate/selectors/pick.ts
35922
- function pickSelectorKind(stageConfig, ctx) {
35923
- if (stageConfig.selector) {
35924
- return stageConfig.selector.kind;
35925
- }
35926
- if (ctx.reviewerSession && ctx.resolverContextInput) {
35927
- return "dialogue-verdict";
35928
- }
35929
- return pickBaseSelectorKind(stageConfig);
35930
- }
35931
- function pickBaseSelectorKind(stageConfig) {
35932
- switch (stageConfig.resolver.type) {
35933
- case "synthesis":
35934
- return "synthesis";
35935
- case "majority-fail-closed":
35936
- return "majority-fail-closed";
35937
- case "majority-fail-open":
35938
- return "majority-fail-open";
35939
- case "custom":
35940
- return "judge";
35941
- }
35942
- return "synthesis";
35943
- }
35944
-
35945
- // src/debate/selectors/dialogue-verdict.ts
35946
- var dialogueVerdictSelector = async (ctx) => {
35947
- if (ctx.reviewerSession && ctx.resolverContextInput) {
35948
- try {
35949
- const debateCtx = {
35950
- resolverType: ctx.stageConfig.resolver.type
35951
- };
35952
- if (ctx.stageConfig.resolver.type === "majority-fail-closed" || ctx.stageConfig.resolver.type === "majority-fail-open") {
35953
- const failOpen = ctx.stageConfig.resolver.type === "majority-fail-open";
35954
- const rawOutcome = majorityResolver(ctx.proposals.map((p) => p.output), failOpen);
35955
- let passCount = 0;
35956
- let failCount = 0;
35957
- for (const proposal of ctx.proposals) {
35958
- const parsed = tryParseLLMJson(proposal.output);
35959
- if (parsed !== null && typeof parsed.passed === "boolean" && parsed.passed) {
35960
- passCount++;
35961
- } else if (failOpen) {
35962
- passCount++;
35963
- } else {
35964
- failCount++;
35965
- }
35966
- }
35967
- debateCtx.majorityVote = { passed: rawOutcome === "passed", passCount, failCount };
35968
- }
35969
- const story = {
35970
- id: ctx.resolverContextInput.story.id,
35971
- title: ctx.resolverContextInput.story.title,
35972
- description: "",
35973
- acceptanceCriteria: ctx.resolverContextInput.story.acceptanceCriteria
35974
- };
35975
- const rcRecord = ctx.resolverContextInput;
35976
- const diffContext = ctx.resolverContextInput.diffMode === "ref" ? {
35977
- mode: "ref",
35978
- storyGitRef: rcRecord.storyGitRef ?? "",
35979
- stat: rcRecord.stat ?? undefined,
35980
- productionExcludePatterns: rcRecord.productionExcludePatterns
35981
- } : { mode: "embedded", diff: rcRecord.diff ?? "" };
35982
- const labeledProposals = ctx.labeledProposals ?? ctx.proposals.map((p) => ({
35983
- debater: p.debater.agent,
35984
- output: p.output
35985
- }));
35986
- let dialogueResult;
35987
- if (ctx.resolverContextInput.isReReview) {
35988
- dialogueResult = await ctx.reviewerSession.reReviewDebate(labeledProposals, ctx.critiques, diffContext, debateCtx);
35989
- } else {
35990
- dialogueResult = await ctx.reviewerSession.resolveDebate(labeledProposals, ctx.critiques, diffContext, story, ctx.resolverContextInput.semanticConfig, debateCtx);
35991
- }
35992
- const outcome = dialogueResult.checkResult.success ? "passed" : "failed";
35993
- return {
35994
- outcome,
35995
- findings: dialogueResult.checkResult.findings,
35996
- dialogueResult
35997
- };
35998
- } catch {}
35999
- }
36000
- const baseKind = pickBaseSelectorKind(ctx.stageConfig);
36001
- const baseSelector = resolveSelector(baseKind);
36002
- return baseSelector(ctx);
36003
- };
36004
- var init_dialogue_verdict = __esm(() => {
36005
- init_resolvers();
36006
- init_registry2();
36007
- });
36008
-
36009
- // src/debate/selectors/judge.ts
36010
- var RESOLVER_FALLBACK_AGENT = "synthesis", RESOLVER_FALLBACK_MODEL = "fast", judgeSelector = async (ctx) => {
36011
- const resolverAgent = ctx.stageConfig.resolver.agent ?? RESOLVER_FALLBACK_AGENT;
36012
- const resolverModel = ctx.stageConfig.resolver.model ?? RESOLVER_FALLBACK_MODEL;
36013
- const proposals = ctx.proposals.map((p) => p.output);
36014
- const output = await callOp(ctx.callContext, judgeOp, {
36015
- proposals,
36016
- critiques: ctx.critiques,
36017
- debaters: ctx.debaters,
36018
- resolverAgent,
36019
- resolverModel,
36020
- timeoutSeconds: ctx.stageConfig.timeoutSeconds
36021
- });
36022
- return {
36023
- outcome: output.trim() ? "passed" : "failed",
36024
- output
36025
- };
36026
- };
36027
- var init_judge = __esm(() => {
36028
- init_operations();
36029
- init_operations();
36030
- });
36031
-
36032
35911
  // src/debate/selectors/synthesis.ts
36033
35912
  var RESOLVER_FALLBACK_AGENT2 = "synthesis", RESOLVER_FALLBACK_MODEL2 = "fast", synthesisSelector = async (ctx) => {
36034
35913
  const resolverAgent = ctx.stageConfig.resolver.agent ?? RESOLVER_FALLBACK_AGENT2;
@@ -36205,7 +36084,6 @@ function registerSelector(kind, strategy) {
36205
36084
  var STRATEGIES;
36206
36085
  var init_registry2 = __esm(() => {
36207
36086
  init_errors();
36208
- init_dialogue_verdict();
36209
36087
  init_judge();
36210
36088
  init_synthesis();
36211
36089
  init_verifier_pick();
@@ -36214,16 +36092,34 @@ var init_registry2 = __esm(() => {
36214
36092
  registerSelector("majority-fail-closed", majorityFailClosedSelector);
36215
36093
  registerSelector("majority-fail-open", majorityFailOpenSelector);
36216
36094
  registerSelector("judge", judgeSelector);
36217
- registerSelector("dialogue-verdict", dialogueVerdictSelector);
36218
36095
  registerSelector("verifier-pick", (ctx) => verifierPickSelector(ctx));
36219
36096
  });
36220
36097
 
36098
+ // src/debate/selectors/pick.ts
36099
+ function pickSelectorKind(stageConfig) {
36100
+ if (stageConfig.selector) {
36101
+ return stageConfig.selector.kind;
36102
+ }
36103
+ return pickBaseSelectorKind(stageConfig);
36104
+ }
36105
+ function pickBaseSelectorKind(stageConfig) {
36106
+ switch (stageConfig.resolver.type) {
36107
+ case "synthesis":
36108
+ return "synthesis";
36109
+ case "majority-fail-closed":
36110
+ return "majority-fail-closed";
36111
+ case "majority-fail-open":
36112
+ return "majority-fail-open";
36113
+ case "custom":
36114
+ return "judge";
36115
+ }
36116
+ }
36117
+
36221
36118
  // src/debate/selectors/index.ts
36222
36119
  var init_selectors2 = __esm(() => {
36223
36120
  init_registry2();
36224
36121
  init_synthesis();
36225
36122
  init_judge();
36226
- init_dialogue_verdict();
36227
36123
  init_verifier_pick();
36228
36124
  });
36229
36125
 
@@ -36251,14 +36147,10 @@ function buildResolverCallContext(provided, agentManager, storyId, workdir, feat
36251
36147
  ...sessionOverride !== undefined ? { sessionOverride } : {}
36252
36148
  };
36253
36149
  }
36254
- async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, config2, callContext, storyId, timeoutMs, workdir, featureName, reviewerSession, resolverContext, promptSuffix, debaters, agentManager) {
36150
+ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, config2, callContext, storyId, timeoutMs, workdir, featureName, promptSuffix, debaters, agentManager) {
36255
36151
  const logger = _debateSessionDeps.getSafeLogger();
36256
- if (reviewerSession && !resolverContext) {
36257
- logger?.warn("debate", "ReviewerSession provided but resolverContext is undefined \u2014 falling back to stateless resolver", { storyId });
36258
- }
36259
- const resolverContextInput = resolverContext ? (({ labeledProposals: _lp, ...rest }) => rest)(resolverContext) : undefined;
36260
- const kind = pickSelectorKind(stageConfig, { reviewerSession, resolverContextInput });
36261
- if ((kind === "majority-fail-closed" || kind === "majority-fail-open") && workdir !== undefined && !reviewerSession) {
36152
+ const kind = pickSelectorKind(stageConfig);
36153
+ if ((kind === "majority-fail-closed" || kind === "majority-fail-open") && workdir !== undefined) {
36262
36154
  logger?.warn("debate", "majority resolver does not support implementer session resumption \u2014 switch to synthesis or custom resolver for context-aware semantic review");
36263
36155
  }
36264
36156
  const proposalList = debaters ? debaters.map((debater, i) => ({
@@ -36285,48 +36177,20 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
36285
36177
  stageConfig,
36286
36178
  config: effectiveConfig,
36287
36179
  proposals: proposalList,
36288
- labeledProposals: resolverContext?.labeledProposals,
36289
36180
  critiques: critiqueOutputs,
36290
36181
  workdir: workdir ?? "",
36291
36182
  featureName: featureName ?? "",
36292
36183
  timeoutMs,
36293
36184
  agentManager: effectiveAgentManager,
36294
- reviewerSession,
36295
- resolverContextInput,
36296
36185
  promptSuffix,
36297
36186
  debaters: debaters ?? [],
36298
36187
  callContext: effectiveCallContext
36299
36188
  };
36300
- const resolverTypeMappedKind = pickBaseSelectorKind(stageConfig);
36301
- if (kind === "dialogue-verdict") {
36302
- try {
36303
- const result2 = await resolveSelector(kind)(selectorCtx);
36304
- return {
36305
- outcome: result2.outcome,
36306
- output: result2.output,
36307
- findings: result2.findings,
36308
- dialogueResult: result2.dialogueResult
36309
- };
36310
- } catch (err) {
36311
- logger?.warn("debate", "dialogue-verdict selector failed, falling back to stateless", {
36312
- storyId,
36313
- error: err instanceof Error ? err.message : String(err)
36314
- });
36315
- const fallbackResult = await resolveSelector(resolverTypeMappedKind)(selectorCtx);
36316
- return {
36317
- outcome: fallbackResult.outcome,
36318
- output: fallbackResult.output,
36319
- findings: fallbackResult.findings,
36320
- dialogueResult: fallbackResult.dialogueResult
36321
- };
36322
- }
36323
- }
36324
36189
  const result = await resolveSelector(kind)(selectorCtx);
36325
36190
  return {
36326
36191
  outcome: result.outcome,
36327
36192
  output: result.output,
36328
- findings: result.findings,
36329
- dialogueResult: result.dialogueResult
36193
+ findings: result.findings
36330
36194
  };
36331
36195
  }
36332
36196
  var RESOLVER_FALLBACK_AGENT3 = "synthesis", _debateSessionDeps;
@@ -36966,6 +36830,46 @@ var init_verdict = __esm(() => {
36966
36830
  });
36967
36831
 
36968
36832
  // src/operations/verify.ts
36833
+ function buildVerifierFindings(verdict, categorization) {
36834
+ if (categorization.success)
36835
+ return [];
36836
+ switch (categorization.failureCategory) {
36837
+ case "verifier-rejected": {
36838
+ const files = verdict.testModifications.files;
36839
+ return [
36840
+ {
36841
+ source: "tdd-verifier",
36842
+ severity: "error",
36843
+ category: "illegitimate-test-edits",
36844
+ fixTarget: "test",
36845
+ message: files.length > 0 ? `Implementer edited test files illegitimately: ${files.join(", ")}` : "Implementer made illegitimate test modifications",
36846
+ meta: {
36847
+ reasoning: verdict.testModifications.reasoning,
36848
+ files
36849
+ }
36850
+ }
36851
+ ];
36852
+ }
36853
+ case "tests-failing": {
36854
+ return [
36855
+ {
36856
+ source: "tdd-verifier",
36857
+ severity: "error",
36858
+ category: "tests-failed",
36859
+ fixTarget: "source",
36860
+ message: `${verdict.tests.failCount} story-scoped test(s) failed (verifier)`,
36861
+ meta: {
36862
+ passCount: verdict.tests.passCount,
36863
+ failCount: verdict.tests.failCount,
36864
+ reasoning: verdict.reasoning
36865
+ }
36866
+ }
36867
+ ];
36868
+ }
36869
+ default:
36870
+ return [];
36871
+ }
36872
+ }
36969
36873
  function parseVerdictFromStdout(output, _input, _ctx) {
36970
36874
  if (!output || !output.trim()) {
36971
36875
  throw new ParseValidationError("verifier produced no stdout");
@@ -36985,6 +36889,7 @@ function parseVerdictFromStdout(output, _input, _ctx) {
36985
36889
  estimatedCostUsd: 0,
36986
36890
  durationMs: 0,
36987
36891
  output,
36892
+ normalizedFindings: buildVerifierFindings(verdict, categorization),
36988
36893
  ...categorization.failureCategory && { failureCategory: categorization.failureCategory },
36989
36894
  ...categorization.reviewReason && { reviewReason: categorization.reviewReason }
36990
36895
  };
@@ -36999,6 +36904,7 @@ var verifierOp;
36999
36904
  var init_verify = __esm(() => {
37000
36905
  init_retry();
37001
36906
  init_config();
36907
+ init_logger2();
37002
36908
  init_tdd_builder();
37003
36909
  init_isolation();
37004
36910
  init_verdict();
@@ -37017,6 +36923,7 @@ var init_verify = __esm(() => {
37017
36923
  },
37018
36924
  reviewerKind: "verifier",
37019
36925
  maxAttempts: 2,
36926
+ outputPreviewBytes: 600,
37020
36927
  prompts: {
37021
36928
  invalid: () => TddPromptBuilder.verdictRetry(),
37022
36929
  truncated: () => TddPromptBuilder.verdictRetryCondensed()
@@ -37045,29 +36952,45 @@ var init_verify = __esm(() => {
37045
36952
  },
37046
36953
  async recover(input, verifyCtx) {
37047
36954
  const packageDir = verifyCtx.packageView.packageDir;
36955
+ const logger = getSafeLogger();
36956
+ const storyId = input.story.id;
37048
36957
  try {
37049
36958
  const verdict = await readVerdict(packageDir);
37050
36959
  if (verdict) {
37051
36960
  const testsAllPassing = verdict.tests.allPassing === true;
37052
36961
  const categorization = categorizeVerdict(verdict, testsAllPassing);
37053
36962
  const isolation = await runVerifierIsolation(input.beforeRef, verifyCtx);
36963
+ const normalizedFindings = buildVerifierFindings(verdict, categorization);
36964
+ logger?.warn("verifier", "Recovered verdict from disk after unparseable stdout", {
36965
+ storyId,
36966
+ packageDir,
36967
+ success: categorization.success,
36968
+ findingsCount: normalizedFindings.length,
36969
+ ...categorization.failureCategory && { failureCategory: categorization.failureCategory }
36970
+ });
37054
36971
  return {
37055
36972
  success: categorization.success,
37056
36973
  filesChanged: [],
37057
36974
  estimatedCostUsd: 0,
37058
36975
  durationMs: 0,
37059
36976
  output: "",
36977
+ normalizedFindings,
37060
36978
  ...categorization.failureCategory && { failureCategory: categorization.failureCategory },
37061
36979
  ...categorization.reviewReason && { reviewReason: categorization.reviewReason },
37062
36980
  ...isolation && { isolation }
37063
36981
  };
37064
36982
  }
36983
+ logger?.error("verifier", "No usable verdict \u2014 unparseable stdout and no verdict file on disk (fail-closed)", {
36984
+ storyId,
36985
+ packageDir
36986
+ });
37065
36987
  return {
37066
36988
  success: false,
37067
36989
  filesChanged: [],
37068
36990
  estimatedCostUsd: 0,
37069
36991
  durationMs: 0,
37070
36992
  output: "",
36993
+ normalizedFindings: [],
37071
36994
  reviewReason: "verifier produced unparseable verdict in stdout after retries and no usable verdict file on disk"
37072
36995
  };
37073
36996
  } finally {
@@ -38081,7 +38004,8 @@ var init__finding_to_check = __esm(() => {
38081
38004
  "semantic-review": "semantic",
38082
38005
  "adversarial-review": "adversarial",
38083
38006
  lint: "lint",
38084
- typecheck: "typecheck"
38007
+ typecheck: "typecheck",
38008
+ "tdd-verifier": "test"
38085
38009
  };
38086
38010
  });
38087
38011
 
@@ -38093,7 +38017,8 @@ function makeAutofixImplementerStrategy(story, config2, sink) {
38093
38017
  fixOp: implementerRectifyOp,
38094
38018
  buildInput: (findings, _prior, _cycleCtx) => ({
38095
38019
  failedChecks: findingsToFailedChecks(findings),
38096
- story
38020
+ story,
38021
+ findings
38097
38022
  }),
38098
38023
  extractApplied: (output) => {
38099
38024
  for (const decl of output.testEditDeclarations) {
@@ -38116,7 +38041,7 @@ var IMPLEMENTER_SOURCES;
38116
38041
  var init_autofix_implementer_strategy = __esm(() => {
38117
38042
  init__finding_to_check();
38118
38043
  init_autofix_implementer();
38119
- IMPLEMENTER_SOURCES = new Set(["lint", "typecheck", "semantic-review"]);
38044
+ IMPLEMENTER_SOURCES = new Set(["lint", "typecheck", "semantic-review", "tdd-verifier"]);
38120
38045
  });
38121
38046
 
38122
38047
  // src/operations/autofix-test-writer-strategy.ts
@@ -38859,6 +38784,9 @@ var init_operations = __esm(() => {
38859
38784
  });
38860
38785
 
38861
38786
  // src/findings/cycle.ts
38787
+ function normalizeValidateResult(r) {
38788
+ return Array.isArray(r) ? { findings: r, shortCircuited: false } : r;
38789
+ }
38862
38790
  function classifySingleSource(before, after) {
38863
38791
  const beforeKeys = new Set(before.map(findingKey));
38864
38792
  const afterKeys = new Set(after.map(findingKey));
@@ -39055,11 +38983,12 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
39055
38983
  });
39056
38984
  if (allExhausted) {
39057
38985
  let liteFindingsAfter;
38986
+ let liteShortCircuited = false;
39058
38987
  try {
39059
- liteFindingsAfter = await cycle.validate(ctx, {
39060
- mode: "lite",
39061
- strategiesRun: group.map((s) => s.name)
39062
- });
38988
+ const liteRaw = await cycle.validate(ctx, { mode: "lite", strategiesRun: group.map((s) => s.name) });
38989
+ const liteResult = normalizeValidateResult(liteRaw);
38990
+ liteFindingsAfter = liteResult.findings;
38991
+ liteShortCircuited = liteResult.shortCircuited ?? false;
39063
38992
  } catch (err) {
39064
38993
  const finishedAt3 = now();
39065
38994
  cycle.iterations.push({
@@ -39097,7 +39026,7 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
39097
39026
  finishedAt: finishedAt2
39098
39027
  });
39099
39028
  cycle.findings = liteFindingsAfter;
39100
- if (liteFindingsAfter.length === 0) {
39029
+ if (liteFindingsAfter.length === 0 && !liteShortCircuited) {
39101
39030
  logger?.info("findings.cycle", "cycle exited \u2014 resolved after terminal lite validate", {
39102
39031
  storyId,
39103
39032
  packageDir,
@@ -39111,6 +39040,21 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
39111
39040
  costUsd: totalCostUsd
39112
39041
  };
39113
39042
  }
39043
+ if (liteShortCircuited) {
39044
+ logger?.info("findings.cycle", "cycle exited \u2014 validate short-circuited", {
39045
+ storyId,
39046
+ packageDir,
39047
+ cycleName,
39048
+ reason: "validate-short-circuit",
39049
+ liteFindingsAfterCount: liteFindingsAfter.length
39050
+ });
39051
+ return {
39052
+ iterations: cycle.iterations,
39053
+ finalFindings: liteFindingsAfter,
39054
+ exitReason: "validate-short-circuit",
39055
+ costUsd: totalCostUsd
39056
+ };
39057
+ }
39114
39058
  logger?.info("findings.cycle", "cycle exited \u2014 strategy attempt cap reached (lite validate)", {
39115
39059
  storyId,
39116
39060
  packageDir,
@@ -39131,7 +39075,8 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
39131
39075
  let validatorAttempt = 0;
39132
39076
  for (;; ) {
39133
39077
  try {
39134
- findingsAfter = await cycle.validate(ctx, { mode: "full", strategiesRun: group.map((s) => s.name) });
39078
+ const fullRaw = await cycle.validate(ctx, { mode: "full", strategiesRun: group.map((s) => s.name) });
39079
+ findingsAfter = normalizeValidateResult(fullRaw).findings;
39135
39080
  break;
39136
39081
  } catch (err) {
39137
39082
  if (validatorAttempt >= cycle.config.validatorRetries) {
@@ -39775,7 +39720,6 @@ async function runSemanticDebate(opts) {
39775
39720
  agentManager,
39776
39721
  featureName,
39777
39722
  story,
39778
- resolverSession,
39779
39723
  diffMode,
39780
39724
  diff,
39781
39725
  stat,
@@ -39793,10 +39737,9 @@ async function runSemanticDebate(opts) {
39793
39737
  ...configuredStageConfig,
39794
39738
  sessionMode: "one-shot",
39795
39739
  mode: "panel",
39796
- selector: { kind: "dialogue-verdict" },
39740
+ selector: { kind: pickBaseSelectorKind(configuredStageConfig) },
39797
39741
  postDebateVerifier: { kind: "review-grounding-filter" }
39798
39742
  };
39799
- const isReReview = resolverSession !== undefined && resolverSession.history.length > 0;
39800
39743
  const semanticAgentName = agentManager && typeof agentManager.getDefault === "function" ? agentManager.getDefault() : "claude";
39801
39744
  const semanticCallCtx = {
39802
39745
  runtime,
@@ -39813,89 +39756,10 @@ async function runSemanticDebate(opts) {
39813
39756
  config: naxConfig,
39814
39757
  workdir,
39815
39758
  featureName,
39816
- timeoutSeconds: naxConfig.execution?.sessionTimeoutSeconds,
39817
- reviewerSession: resolverSession,
39818
- resolverContextInput: resolverSession ? {
39819
- diffMode,
39820
- ...diffMode === "ref" ? { storyGitRef: effectiveRef, stat, productionExcludePatterns } : { diff },
39821
- story: { id: story.id, title: story.title, acceptanceCriteria: story.acceptanceCriteria },
39822
- semanticConfig,
39823
- blockingThreshold,
39824
- resolverType: reviewStageConfig.resolver.type,
39825
- isReReview
39826
- } : undefined
39759
+ timeoutSeconds: naxConfig.execution?.sessionTimeoutSeconds
39827
39760
  });
39828
- const historyLenBefore = resolverSession?.history.length ?? 0;
39829
39761
  const debateResult = await debateRunner.run(prompt);
39830
39762
  const debateCost = debateResult.totalCostUsd ?? 0;
39831
- const sessionUsed = resolverSession && resolverSession.history.length > historyLenBefore;
39832
- if (sessionUsed) {
39833
- const durationMs2 = Date.now() - startTime;
39834
- try {
39835
- const verdict = resolverSession.getVerdict();
39836
- const findings = verdict.findings ?? [];
39837
- if (!verdict.passed && findings.length > 0) {
39838
- logger?.warn("review", `Semantic review failed (debate+dialogue): ${findings.length} findings`, {
39839
- storyId: story.id,
39840
- durationMs: durationMs2
39841
- });
39842
- recordSemanticDebateAudit({
39843
- runtime,
39844
- workdir,
39845
- storyId: story.id,
39846
- featureName,
39847
- parsed: true,
39848
- passed: false,
39849
- blockingThreshold,
39850
- result: {
39851
- passed: false,
39852
- findings: findingsToReviewFindings(findings, { source: "semantic-debate-review" })
39853
- }
39854
- });
39855
- return {
39856
- check: "semantic",
39857
- success: false,
39858
- command: "",
39859
- exitCode: 1,
39860
- output: `Semantic review failed:
39861
-
39862
- ${findings.map((f) => `${f.rule ?? "semantic"}: ${f.message}`).join(`
39863
- `)}`,
39864
- durationMs: durationMs2,
39865
- findings,
39866
- cost: debateCost
39867
- };
39868
- }
39869
- const label = verdict.passed ? "Semantic review passed (debate+dialogue)" : "Semantic review passed (debate+dialogue, all findings non-blocking)";
39870
- logger?.info("review", label, { storyId: story.id, durationMs: durationMs2 });
39871
- recordSemanticDebateAudit({
39872
- runtime,
39873
- workdir,
39874
- storyId: story.id,
39875
- featureName,
39876
- parsed: true,
39877
- passed: true,
39878
- blockingThreshold,
39879
- result: {
39880
- passed: true,
39881
- findings: findingsToReviewFindings(findings, { source: "semantic-debate-review" })
39882
- }
39883
- });
39884
- return {
39885
- check: "semantic",
39886
- success: true,
39887
- command: "",
39888
- exitCode: 0,
39889
- output: label,
39890
- durationMs: durationMs2,
39891
- cost: debateCost
39892
- };
39893
- } catch {
39894
- logger?.warn("review", "getVerdict() failed after debate+dialogue \u2014 falling back to stateless verdict", {
39895
- storyId: story.id
39896
- });
39897
- }
39898
- }
39899
39763
  const resolverPassed = debateResult.outcome === "passed";
39900
39764
  const allFindings = [];
39901
39765
  for (const p of debateResult.proposals) {
@@ -40015,6 +39879,7 @@ ${formatFindings2(debateBlocking)}`,
40015
39879
  };
40016
39880
  }
40017
39881
  var init_semantic_debate = __esm(() => {
39882
+ init_debate();
40018
39883
  init_logger2();
40019
39884
  init_ac_quote_validator();
40020
39885
  init_finding_projection();
@@ -40049,7 +39914,6 @@ async function runSemanticReview(opts) {
40049
39914
  agentManager,
40050
39915
  naxConfig,
40051
39916
  featureName,
40052
- resolverSession,
40053
39917
  priorSemanticIterations,
40054
39918
  blockingThreshold,
40055
39919
  featureContextMarkdown,
@@ -40183,7 +40047,6 @@ async function runSemanticReview(opts) {
40183
40047
  agentManager: effectiveAgentManager,
40184
40048
  featureName,
40185
40049
  story,
40186
- resolverSession,
40187
40050
  diffMode,
40188
40051
  diff,
40189
40052
  stat,
@@ -40550,7 +40413,6 @@ async function runReview(opts) {
40550
40413
  naxConfig,
40551
40414
  retrySkipChecks,
40552
40415
  featureName,
40553
- resolverSession,
40554
40416
  priorFailures,
40555
40417
  priorSemanticIterations,
40556
40418
  featureContextMarkdown,
@@ -40630,7 +40492,6 @@ async function runReview(opts) {
40630
40492
  agentManager,
40631
40493
  naxConfig,
40632
40494
  featureName,
40633
- resolverSession,
40634
40495
  priorSemanticIterations,
40635
40496
  blockingThreshold: config2.blockingThreshold,
40636
40497
  featureContextMarkdown,
@@ -41568,6 +41429,26 @@ Tests are failing. Fix the source so all tests pass \u2014 not just the ones lis
41568
41429
  parts.push(escapeHatchFor(opts.story));
41569
41430
  return parts.join("");
41570
41431
  }
41432
+ static verifierContext(findings) {
41433
+ if (findings.length === 0) {
41434
+ return "The verifier found no issues.";
41435
+ }
41436
+ const lines = [
41437
+ `Fix the following ${findings.length} verifier finding${findings.length === 1 ? "" : "s"}:
41438
+ `
41439
+ ];
41440
+ for (const f of findings) {
41441
+ const reasoning = f.meta?.reasoning ?? "";
41442
+ const detail = reasoning ? ` Reasoning: ${reasoning}
41443
+ ` : "";
41444
+ lines.push(`- [${f.category}] ${f.message}
41445
+ ${detail}`);
41446
+ }
41447
+ lines.push(`
41448
+ Fix the implementation to resolve all verifier findings.`);
41449
+ return lines.join(`
41450
+ `);
41451
+ }
41571
41452
  static failingTestContext(findings) {
41572
41453
  if (findings.length === 0) {
41573
41454
  return "The full test suite has failing tests. Fix the implementation to make all tests pass.";
@@ -46051,17 +45932,6 @@ function createDebaterCallContext(ctx, agentName) {
46051
45932
  }
46052
45933
  };
46053
45934
  }
46054
- function buildResolverContext(successfulProposals, resolverContextInput) {
46055
- if (!resolverContextInput)
46056
- return;
46057
- return {
46058
- ...resolverContextInput,
46059
- labeledProposals: successfulProposals.map((proposal) => ({
46060
- debater: buildDebaterLabel(proposal.debater),
46061
- output: proposal.output
46062
- }))
46063
- };
46064
- }
46065
45935
  async function runZeroSuccessFallback(ctx, prompt, firstDebater) {
46066
45936
  if (!firstDebater)
46067
45937
  return null;
@@ -46090,7 +45960,6 @@ var init_runner_stateful_helpers = __esm(() => {
46090
45960
  init_call();
46091
45961
  init_debate_stateful();
46092
45962
  init_prompts();
46093
- init_personas();
46094
45963
  DEFAULT_ABORT_SIGNAL = new AbortController().signal;
46095
45964
  });
46096
45965
 
@@ -46212,13 +46081,7 @@ async function runHybrid(ctx, prompt) {
46212
46081
  }
46213
46082
  }
46214
46083
  const proposalOutputs = successfulResults.map((p) => p.output);
46215
- const resolveResult = await resolveOutcome(proposalOutputs, rebuttals.map((r) => r.output), ctx.stageConfig, ctx.config, { ...ctx.callContext, scopeId: ctx.resolverCallContext?.scopeId ?? ctx.callContext.scopeId }, ctx.storyId, ctx.timeoutSeconds * 1000, ctx.workdir, ctx.featureName, ctx.reviewerSession, ctx.resolverContextInput ? {
46216
- ...ctx.resolverContextInput,
46217
- labeledProposals: successfulResults.map((p) => ({
46218
- debater: buildDebaterLabel(p.debater),
46219
- output: p.output
46220
- }))
46221
- } : undefined, undefined, successfulResults.map((p) => p.debater), agentManager);
46084
+ const resolveResult = await resolveOutcome(proposalOutputs, rebuttals.map((r) => r.output), ctx.stageConfig, ctx.config, { ...ctx.callContext, scopeId: ctx.resolverCallContext?.scopeId ?? ctx.callContext.scopeId }, ctx.storyId, ctx.timeoutSeconds * 1000, ctx.workdir, ctx.featureName, undefined, successfulResults.map((p) => p.debater), agentManager);
46222
46085
  return {
46223
46086
  storyId: ctx.storyId,
46224
46087
  stage: ctx.stage,
@@ -46647,9 +46510,7 @@ async function scoreAndDispatchVerifierPick(resolved, rebuttalSettled, ctx, opts
46647
46510
  timeoutMs: (ctx.stageConfig.timeoutSeconds ?? 600) * 1000,
46648
46511
  agentManager,
46649
46512
  debaters: resolved.map((e) => e.debater),
46650
- callContext: ctx.callContext,
46651
- ...ctx.reviewerSession ? { reviewerSession: ctx.reviewerSession } : {},
46652
- ...ctx.resolverContextInput ? { resolverContextInput: ctx.resolverContextInput } : {}
46513
+ callContext: ctx.callContext
46653
46514
  };
46654
46515
  const manifest = extractManifestFromContext(scoringCtx);
46655
46516
  const scored = await Promise.all(rebutProposals.map(async (p) => ({ proposal: p, score: await computeScore(p, manifest) })));
@@ -46728,9 +46589,7 @@ async function finalizePlanRun(ctx, opts, successful, rebuttalList, outputPaths,
46728
46589
  timeoutMs: (ctx.stageConfig.timeoutSeconds ?? 600) * 1000,
46729
46590
  agentManager,
46730
46591
  debaters: finalizedProposals.map((p) => p.debater),
46731
- callContext: ctx.callContext,
46732
- ...ctx.reviewerSession ? { reviewerSession: ctx.reviewerSession } : {},
46733
- ...ctx.resolverContextInput ? { resolverContextInput: ctx.resolverContextInput } : {}
46592
+ callContext: ctx.callContext
46734
46593
  };
46735
46594
  const manifest = extractManifestFromContext(selectorCtx);
46736
46595
  const scored = await Promise.all(selectorCtx.proposals.map(async (proposal) => ({ proposal, score: await computeScore(proposal, manifest) })));
@@ -46744,7 +46603,7 @@ async function finalizePlanRun(ctx, opts, successful, rebuttalList, outputPaths,
46744
46603
  const outcome = selectorKind === "verifier-pick" ? {
46745
46604
  outcome: "passed",
46746
46605
  output: selectionSummary.winnerOutput ?? finalizedProposals[0]?.output
46747
- } : await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.callContext, ctx.storyId, resolverTimeoutMs, opts.workdir, opts.feature, ctx.reviewerSession, buildResolverContext(finalizedProposals.map((p) => ({ debater: p.debater, agentName: p.agentName, output: p.output, cost: 0 })), ctx.resolverContextInput), buildPlanSynthesisSuffix(opts.specContent), finalizedProposals.map((p) => p.debater), agentManager);
46606
+ } : await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.callContext, ctx.storyId, resolverTimeoutMs, opts.workdir, opts.feature, buildPlanSynthesisSuffix(opts.specContent), finalizedProposals.map((p) => p.debater), agentManager);
46748
46607
  let finalOutcome = outcome.outcome;
46749
46608
  let winningOutput = outcome.output ?? finalizedProposals[0]?.output;
46750
46609
  winningOutput = await readWinnerOutput(selectionSummary.winnerOutputPath ?? outputPaths[0], winningOutput);
@@ -46785,7 +46644,6 @@ async function finalizePlanRun(ctx, opts, successful, rebuttalList, outputPaths,
46785
46644
  var _runPlanDeps, FILE_OUTPUT_INSTRUCTION = "Write the complete PRD JSON to this file path and then reply with a short confirmation:";
46786
46645
  var init_runner_plan_helpers = __esm(() => {
46787
46646
  init_pre_phase();
46788
- init_runner_stateful_helpers();
46789
46647
  init_verifier_pick();
46790
46648
  init_session_helpers();
46791
46649
  init_verifiers();
@@ -47041,9 +46899,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
47041
46899
  stage: ctx.stage,
47042
46900
  stageConfig: ctx.stageConfig,
47043
46901
  config: ctx.config,
47044
- callContext: ctx.callContext,
47045
- reviewerSession: ctx.reviewerSession,
47046
- resolverContextInput: ctx.resolverContextInput
46902
+ callContext: ctx.callContext
47047
46903
  }, { workdir: opts.workdir, feature: opts.feature, specContent: opts.specContent }, successful, rebuttalList, outputPaths, totalCostUsd, agentManager, selectorKind, includeHybridRebuttals);
47048
46904
  }
47049
46905
  var DEFAULT_MAX_CONCURRENT_DEBATERS = 2;
@@ -47177,7 +47033,7 @@ async function runStateful(ctx, prompt) {
47177
47033
  }).then((result) => ({ ...result, resolvedIndex: proposal.resolvedIndex }))), concurrencyLimit)).flatMap((result) => result.status === "fulfilled" && result.value.success ? [{ debater: resolved[result.value.resolvedIndex].debater, round: 1, output: result.value.rebut }] : []) : [];
47178
47034
  throwIfAborted2();
47179
47035
  const proposalOutputs = successfulProposals.map((proposal) => proposal.output);
47180
- const outcome = await resolveOutcome(proposalOutputs, rebuttals.map((rebuttal) => rebuttal.output), ctx.stageConfig, ctx.config, { ...ctx.callContext, scopeId: ctx.resolverCallContext?.scopeId ?? ctx.callContext.scopeId }, ctx.storyId, ctx.timeoutSeconds * 1000, ctx.workdir, ctx.featureName, ctx.reviewerSession, buildResolverContext(successfulProposals, ctx.resolverContextInput), undefined, successfulProposals.map((proposal) => proposal.debater), ctx.agentManager);
47036
+ const outcome = await resolveOutcome(proposalOutputs, rebuttals.map((rebuttal) => rebuttal.output), ctx.stageConfig, ctx.config, { ...ctx.callContext, scopeId: ctx.resolverCallContext?.scopeId ?? ctx.callContext.scopeId }, ctx.storyId, ctx.timeoutSeconds * 1000, ctx.workdir, ctx.featureName, undefined, successfulProposals.map((proposal) => proposal.debater), ctx.agentManager);
47181
47037
  logger?.info("debate", "debate:result", {
47182
47038
  storyId: ctx.storyId,
47183
47039
  stage: ctx.stage,
@@ -47220,8 +47076,6 @@ class DebateRunner {
47220
47076
  featureName;
47221
47077
  timeoutSeconds;
47222
47078
  sessionManager;
47223
- reviewerSession;
47224
- resolverContextInput;
47225
47079
  constructor(opts) {
47226
47080
  this.ctx = opts.ctx;
47227
47081
  this.stage = opts.stage;
@@ -47231,8 +47085,6 @@ class DebateRunner {
47231
47085
  this.featureName = opts.featureName ?? opts.stage;
47232
47086
  this.timeoutSeconds = opts.timeoutSeconds ?? opts.stageConfig.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS;
47233
47087
  this.sessionManager = opts.sessionManager ?? opts.ctx.runtime?.sessionManager;
47234
- this.reviewerSession = opts.reviewerSession;
47235
- this.resolverContextInput = opts.resolverContextInput;
47236
47088
  }
47237
47089
  async run(prompt) {
47238
47090
  const sessionMode = this.stageConfig.sessionMode ?? "one-shot";
@@ -47426,14 +47278,7 @@ ${prompt}`;
47426
47278
  critiqueOutputs = critiqueSettled.filter((r) => r.status === "fulfilled").map((r) => r.value);
47427
47279
  }
47428
47280
  const proposalOutputs = successful.map((p) => p.output);
47429
- const fullResolverContext = this.resolverContextInput ? {
47430
- ...this.resolverContextInput,
47431
- labeledProposals: successful.map((s) => ({
47432
- debater: buildDebaterLabel(s.debater),
47433
- output: s.output
47434
- }))
47435
- } : undefined;
47436
- const selectorOutcome = await resolveOutcome(proposalOutputs, critiqueOutputs, this.stageConfig, this.config, { ...this.ctx, scopeId: resolverScope.scopeId }, this.ctx.storyId ?? "", this.timeoutSeconds * 1000, this.workdir, this.featureName, this.reviewerSession, fullResolverContext, undefined, successful.map((s) => s.debater), agentManager);
47281
+ const selectorOutcome = await resolveOutcome(proposalOutputs, critiqueOutputs, this.stageConfig, this.config, { ...this.ctx, scopeId: resolverScope.scopeId }, this.ctx.storyId ?? "", this.timeoutSeconds * 1000, this.workdir, this.featureName, undefined, successful.map((s) => s.debater), agentManager);
47437
47282
  let finalOutcome = selectorOutcome.outcome;
47438
47283
  if (config2.postDebateVerifier) {
47439
47284
  const verifierCtx = {
@@ -47443,8 +47288,8 @@ ${prompt}`;
47443
47288
  selectorResult: selectorOutcome,
47444
47289
  workdir: this.workdir,
47445
47290
  ctx: { ...this.ctx, scopeId: verifierScope.scopeId },
47446
- acceptanceCriteria: this.resolverContextInput?.story.acceptanceCriteria,
47447
- blockingThreshold: this.resolverContextInput?.blockingThreshold
47291
+ acceptanceCriteria: undefined,
47292
+ blockingThreshold: undefined
47448
47293
  };
47449
47294
  const verifierResult = await resolvePostDebateVerifier(config2.postDebateVerifier.kind)(verifierCtx);
47450
47295
  finalOutcome = verifierResult.outcome;
@@ -47481,9 +47326,7 @@ ${prompt}`;
47481
47326
  agentManager: this.ctx.runtime.agentManager,
47482
47327
  sessionManager: this.sessionManager ?? this.ctx.runtime.sessionManager,
47483
47328
  runtime: this.ctx.runtime,
47484
- abortSignal: this.ctx.runtime.signal,
47485
- reviewerSession: this.reviewerSession,
47486
- resolverContextInput: this.resolverContextInput
47329
+ abortSignal: this.ctx.runtime.signal
47487
47330
  };
47488
47331
  }
47489
47332
  toPlanCtx() {
@@ -47517,6 +47360,11 @@ var init_runner3 = __esm(() => {
47517
47360
  init_verifiers();
47518
47361
  });
47519
47362
 
47363
+ // src/debate/resolvers.ts
47364
+ var init_resolvers = __esm(() => {
47365
+ init_prompts();
47366
+ });
47367
+
47520
47368
  // src/debate/index.ts
47521
47369
  var init_debate = __esm(() => {
47522
47370
  init_runner3();
@@ -53044,7 +52892,7 @@ function extractPhaseFindings(output) {
53044
52892
  return [];
53045
52893
  }
53046
52894
  const record2 = output;
53047
- const rawArray = Array.isArray(record2.normalizedFindings) ? record2.normalizedFindings : Array.isArray(record2.findings) ? record2.findings : [];
52895
+ const rawArray = Array.isArray(record2.normalizedFindings) && record2.normalizedFindings.length > 0 ? record2.normalizedFindings : Array.isArray(record2.findings) ? record2.findings : [];
53048
52896
  const findings = rawArray.filter(isFinding);
53049
52897
  const success2 = "success" in record2 ? record2.success === true : ("passed" in record2) ? record2.passed === true : findings.length === 0;
53050
52898
  return success2 ? [] : findings;
@@ -53156,23 +53004,34 @@ function logUnifiedReviewPhaseStart(storyId, opName) {
53156
53004
  logger?.info("review", "Running adversarial check", { storyId });
53157
53005
  }
53158
53006
  }
53159
- function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage) {
53160
- if (isTddPhase)
53161
- return;
53162
- if (opName === "semantic-review" || opName === "adversarial-review")
53163
- return;
53007
+ function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
53164
53008
  if (output === null || output === undefined || typeof output !== "object")
53165
- return;
53166
- const logger = getSafeLogger();
53009
+ return null;
53167
53010
  const r = output;
53168
53011
  const success2 = r.success === true || r.passed === true;
53169
- const findingsCount = Array.isArray(r.findings) ? r.findings.length : undefined;
53012
+ const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
53170
53013
  const status = typeof r.status === "string" ? r.status : undefined;
53171
53014
  const data = { storyId, phase: opName, durationMs };
53172
53015
  if (findingsCount !== undefined)
53173
53016
  data.findingsCount = findingsCount;
53174
53017
  if (status !== undefined)
53175
53018
  data.status = status;
53019
+ if (typeof r.failureCategory === "string")
53020
+ data.failureCategory = r.failureCategory;
53021
+ if (typeof r.reviewReason === "string")
53022
+ data.reviewReason = r.reviewReason;
53023
+ return { success: success2, data };
53024
+ }
53025
+ function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage) {
53026
+ if (isTddPhase)
53027
+ return;
53028
+ if (opName === "semantic-review" || opName === "adversarial-review")
53029
+ return;
53030
+ const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
53031
+ if (!built)
53032
+ return;
53033
+ const { success: success2, data } = built;
53034
+ const logger = getSafeLogger();
53176
53035
  const message = formatPhaseResultMessage(opName, success2, stage);
53177
53036
  if (stage === "rectification") {
53178
53037
  logger?.info("story-orchestrator", message, data);
@@ -53334,7 +53193,7 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53334
53193
  config: { maxAttemptsTotal: rectification.maxAttempts, validatorRetries: 1 },
53335
53194
  validate: async (_validateCtx, opts) => {
53336
53195
  if (ctx.runtime.signal?.aborted)
53337
- return [];
53196
+ return { findings: [], shortCircuited: false };
53338
53197
  const lite = (opts?.mode ?? "full") === "lite";
53339
53198
  const phases = phasesToRevalidate(opts?.strategiesRun, validationPhases);
53340
53199
  getSafeLogger()?.debug("story-orchestrator", "rectification validate scope", {
@@ -53344,6 +53203,7 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53344
53203
  phasesSelected: phases.map((p) => p.kind)
53345
53204
  });
53346
53205
  const findings = [];
53206
+ let shortCircuited = false;
53347
53207
  for (const phase of phases) {
53348
53208
  if (lite && phase.kind === "full-suite-gate") {
53349
53209
  continue;
@@ -53358,10 +53218,12 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53358
53218
  storyId: ctx.storyId,
53359
53219
  phase: phase.slot.op.name
53360
53220
  });
53221
+ shortCircuited = true;
53361
53222
  break;
53362
53223
  }
53363
53224
  }
53364
- return rectification.postValidate ? await rectification.postValidate(findings, _validateCtx) : findings;
53225
+ const validated = rectification.postValidate ? await rectification.postValidate(findings, _validateCtx) : findings;
53226
+ return { findings: validated, shortCircuited };
53365
53227
  }
53366
53228
  };
53367
53229
  const cycleResult = await _storyOrchestratorDeps.runFixCycle(cycle, ctx, "story-orchestrator-rectification", { callOp: wrappedCallOp });
@@ -53393,6 +53255,9 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53393
53255
  if (EXHAUSTED_EXIT_REASONS.has(cycleResult.exitReason) && cycleResult.finalFindings.length > 0) {
53394
53256
  return { rectificationExhausted: true, unfixedFindings: cycleResult.finalFindings };
53395
53257
  }
53258
+ if (cycleResult.exitReason === "validate-short-circuit") {
53259
+ return { liteScopeIncomplete: true };
53260
+ }
53396
53261
  return {};
53397
53262
  }
53398
53263
 
@@ -53437,7 +53302,7 @@ class ExecutionPlan {
53437
53302
  }
53438
53303
  }
53439
53304
  const rectResult = await runRectification(this.ctx, this.state, phaseCosts, phaseOutputs);
53440
- if (this.state.rectification && !rectResult.rectificationExhausted) {
53305
+ if (this.state.rectification && (!rectResult.rectificationExhausted || rectResult.liteScopeIncomplete)) {
53441
53306
  let resumeRectifyUsed = false;
53442
53307
  for (const phase of collectOrderedPhases(this.state)) {
53443
53308
  const name = phase.slot.op.name;
@@ -53601,7 +53466,8 @@ var init_story_orchestrator = __esm(() => {
53601
53466
  "max-attempts-per-strategy",
53602
53467
  "bail-when",
53603
53468
  "no-strategy",
53604
- "agent-gave-up"
53469
+ "agent-gave-up",
53470
+ "validate-short-circuit"
53605
53471
  ]);
53606
53472
  TDD_OP_NAMES = new Set(["test-writer", "implementer", "verifier"]);
53607
53473
  STRICT_VERDICT_PHASE_NAMES = new Set([
@@ -58017,7 +57883,7 @@ var package_default;
58017
57883
  var init_package = __esm(() => {
58018
57884
  package_default = {
58019
57885
  name: "@nathapp/nax",
58020
- version: "0.67.19",
57886
+ version: "0.68.1",
58021
57887
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
58022
57888
  type: "module",
58023
57889
  bin: {
@@ -58112,8 +57978,8 @@ var init_version = __esm(() => {
58112
57978
  NAX_VERSION = package_default.version;
58113
57979
  NAX_COMMIT = (() => {
58114
57980
  try {
58115
- if (/^[0-9a-f]{6,10}$/.test("e80ba4d6"))
58116
- return "e80ba4d6";
57981
+ if (/^[0-9a-f]{6,10}$/.test("5d69e4e4"))
57982
+ return "5d69e4e4";
58117
57983
  } catch {}
58118
57984
  try {
58119
57985
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -59462,6 +59328,18 @@ async function handleRunCompletion(options) {
59462
59328
  }
59463
59329
  }
59464
59330
  }
59331
+ let pluginGateFailed = false;
59332
+ const deferredReview = options.deferredReview;
59333
+ if (deferredReview?.anyFailed) {
59334
+ const failedReviewers = deferredReview.reviewerResults.filter((r) => !r.passed).map((r) => r.name);
59335
+ pluginGateFailed = config2.review.pluginMode === "gating";
59336
+ logger?.warn("review", "Deferred plugin reviewer(s) reported failures", {
59337
+ storyId: runId,
59338
+ failedReviewers,
59339
+ pluginMode: config2.review.pluginMode,
59340
+ gating: pluginGateFailed
59341
+ });
59342
+ }
59465
59343
  const aggSnap = options.runtime.costAggregator.snapshot();
59466
59344
  const aggregatorTotal = aggSnap.totalCostUsd;
59467
59345
  const reportedTotal = Math.max(totalCost, aggregatorTotal);
@@ -59610,7 +59488,8 @@ async function handleRunCompletion(options) {
59610
59488
  failed: finalCounts.failed,
59611
59489
  skipped: finalCounts.skipped,
59612
59490
  pending: finalCounts.pending
59613
- }
59491
+ },
59492
+ pluginGateFailed
59614
59493
  };
59615
59494
  }
59616
59495
  var _runCompletionDeps;
@@ -98175,12 +98054,13 @@ async function runCompletionPhase(options) {
98175
98054
  skipRegression: regressionAlreadyPassed,
98176
98055
  sessionManager: options.sessionManager,
98177
98056
  pluginProviderCache: options.pluginProviderCache,
98057
+ deferredReview: options.deferredReview,
98178
98058
  runtime: options.runtime,
98179
98059
  abortSignal: options.abortSignal
98180
98060
  });
98181
- const { durationMs, runCompletedAt, finalCounts, reportedTotal } = completionResult;
98061
+ const { durationMs, runCompletedAt, finalCounts, reportedTotal, pluginGateFailed } = completionResult;
98182
98062
  if (options.featureDir) {
98183
- const finalStatus = isComplete(options.prd) ? "completed" : "failed";
98063
+ const finalStatus = isComplete(options.prd) && !pluginGateFailed ? "completed" : "failed";
98184
98064
  options.statusWriter.setRunStatus(finalStatus);
98185
98065
  await options.statusWriter.writeFeatureStatus(options.featureDir, reportedTotal, options.iterations);
98186
98066
  }
@@ -98210,7 +98090,8 @@ async function runCompletionPhase(options) {
98210
98090
  return {
98211
98091
  durationMs,
98212
98092
  runCompletedAt,
98213
- acceptancePassed
98093
+ acceptancePassed,
98094
+ pluginGateFailed
98214
98095
  };
98215
98096
  }
98216
98097
 
@@ -98302,7 +98183,14 @@ async function runExecutionPhase(options, prd, pluginRegistry) {
98302
98183
  storiesCompleted,
98303
98184
  totalCost
98304
98185
  });
98305
- return { prd, iterations, storiesCompleted, totalCost, allStoryMetrics };
98186
+ return {
98187
+ prd,
98188
+ iterations,
98189
+ storiesCompleted,
98190
+ totalCost,
98191
+ allStoryMetrics,
98192
+ deferredReview: unifiedResult.deferredReview
98193
+ };
98306
98194
  }
98307
98195
 
98308
98196
  // src/execution/runner-setup.ts
@@ -98466,13 +98354,14 @@ async function run(options) {
98466
98354
  sessionManager,
98467
98355
  agentManager,
98468
98356
  pluginProviderCache,
98357
+ deferredReview: executionResult.deferredReview,
98469
98358
  runtime,
98470
98359
  abortSignal: shutdownController.signal
98471
98360
  });
98472
- const { durationMs, acceptancePassed } = completionResult;
98361
+ const { durationMs, acceptancePassed, pluginGateFailed } = completionResult;
98473
98362
  runCompleted = true;
98474
98363
  return {
98475
- success: isComplete(prd) && acceptancePassed,
98364
+ success: isComplete(prd) && acceptancePassed && !pluginGateFailed,
98476
98365
  iterations,
98477
98366
  storiesCompleted,
98478
98367
  totalCost,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.67.19",
3
+ "version": "0.68.1",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {