@nathapp/nax 0.67.19 → 0.68.0

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 +214 -359
  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;
@@ -30228,9 +30224,6 @@ function resolvePersonas(debaters, stage, autoPersona) {
30228
30224
  return { ...d, persona: assigned };
30229
30225
  });
30230
30226
  }
30231
- function buildDebaterLabel(debater) {
30232
- return debater.persona ? `${debater.agent} (${debater.persona})` : debater.agent;
30233
- }
30234
30227
  var PERSONA_FRAGMENTS, PLAN_ROTATION, REVIEW_ROTATION;
30235
30228
  var init_personas = __esm(() => {
30236
30229
  PERSONA_FRAGMENTS = {
@@ -32482,33 +32475,6 @@ function llmFindingToReviewFinding(f, opts = {}) {
32482
32475
  function llmFindingsToReviewFindings(findings, opts = {}) {
32483
32476
  return findings.map((f) => llmFindingToReviewFinding(f, opts));
32484
32477
  }
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
32478
  var SEVERITY_MAP, RULE_ID_SLUG_TOKENS = 6;
32513
32479
  var init_finding_projection = __esm(() => {
32514
32480
  SEVERITY_MAP = {
@@ -35641,7 +35607,9 @@ var init_autofix_implementer = __esm(() => {
35641
35607
  session: { role: "implementer", lifetime: "warm" },
35642
35608
  config: autofixConfigSelector,
35643
35609
  build(input, _ctx) {
35644
- const prompt = RectifierPromptBuilder.reviewRectification(input.failedChecks, input.story, {
35610
+ const verifierFindings = input.findings?.filter((f) => f.source === "tdd-verifier");
35611
+ const useVerifierContext = verifierFindings !== undefined && verifierFindings.length > 0 && verifierFindings.length === input.findings?.length;
35612
+ const prompt = useVerifierContext ? RectifierPromptBuilder.verifierContext(verifierFindings) : RectifierPromptBuilder.reviewRectification(input.failedChecks, input.story, {
35645
35613
  blockingThreshold: input.blockingThreshold
35646
35614
  });
35647
35615
  return {
@@ -35869,6 +35837,29 @@ var init_debate_stateful = __esm(() => {
35869
35837
  };
35870
35838
  });
35871
35839
 
35840
+ // src/debate/selectors/judge.ts
35841
+ var RESOLVER_FALLBACK_AGENT = "synthesis", RESOLVER_FALLBACK_MODEL = "fast", judgeSelector = async (ctx) => {
35842
+ const resolverAgent = ctx.stageConfig.resolver.agent ?? RESOLVER_FALLBACK_AGENT;
35843
+ const resolverModel = ctx.stageConfig.resolver.model ?? RESOLVER_FALLBACK_MODEL;
35844
+ const proposals = ctx.proposals.map((p) => p.output);
35845
+ const output = await callOp(ctx.callContext, judgeOp, {
35846
+ proposals,
35847
+ critiques: ctx.critiques,
35848
+ debaters: ctx.debaters,
35849
+ resolverAgent,
35850
+ resolverModel,
35851
+ timeoutSeconds: ctx.stageConfig.timeoutSeconds
35852
+ });
35853
+ return {
35854
+ outcome: output.trim() ? "passed" : "failed",
35855
+ output
35856
+ };
35857
+ };
35858
+ var init_judge = __esm(() => {
35859
+ init_operations();
35860
+ init_operations();
35861
+ });
35862
+
35872
35863
  // src/debate/selectors/majority.ts
35873
35864
  function stripMarkdownFence(text) {
35874
35865
  const match = text.match(/^```(?:json)?\s*\n?([\s\S]*?)\n?```\s*$/);
@@ -35910,125 +35901,6 @@ var majorityFailClosedSelector = async (ctx) => {
35910
35901
  return { outcome: computeMajority(proposalOutputs, true) };
35911
35902
  };
35912
35903
 
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
35904
  // src/debate/selectors/synthesis.ts
36033
35905
  var RESOLVER_FALLBACK_AGENT2 = "synthesis", RESOLVER_FALLBACK_MODEL2 = "fast", synthesisSelector = async (ctx) => {
36034
35906
  const resolverAgent = ctx.stageConfig.resolver.agent ?? RESOLVER_FALLBACK_AGENT2;
@@ -36205,7 +36077,6 @@ function registerSelector(kind, strategy) {
36205
36077
  var STRATEGIES;
36206
36078
  var init_registry2 = __esm(() => {
36207
36079
  init_errors();
36208
- init_dialogue_verdict();
36209
36080
  init_judge();
36210
36081
  init_synthesis();
36211
36082
  init_verifier_pick();
@@ -36214,16 +36085,34 @@ var init_registry2 = __esm(() => {
36214
36085
  registerSelector("majority-fail-closed", majorityFailClosedSelector);
36215
36086
  registerSelector("majority-fail-open", majorityFailOpenSelector);
36216
36087
  registerSelector("judge", judgeSelector);
36217
- registerSelector("dialogue-verdict", dialogueVerdictSelector);
36218
36088
  registerSelector("verifier-pick", (ctx) => verifierPickSelector(ctx));
36219
36089
  });
36220
36090
 
36091
+ // src/debate/selectors/pick.ts
36092
+ function pickSelectorKind(stageConfig) {
36093
+ if (stageConfig.selector) {
36094
+ return stageConfig.selector.kind;
36095
+ }
36096
+ return pickBaseSelectorKind(stageConfig);
36097
+ }
36098
+ function pickBaseSelectorKind(stageConfig) {
36099
+ switch (stageConfig.resolver.type) {
36100
+ case "synthesis":
36101
+ return "synthesis";
36102
+ case "majority-fail-closed":
36103
+ return "majority-fail-closed";
36104
+ case "majority-fail-open":
36105
+ return "majority-fail-open";
36106
+ case "custom":
36107
+ return "judge";
36108
+ }
36109
+ }
36110
+
36221
36111
  // src/debate/selectors/index.ts
36222
36112
  var init_selectors2 = __esm(() => {
36223
36113
  init_registry2();
36224
36114
  init_synthesis();
36225
36115
  init_judge();
36226
- init_dialogue_verdict();
36227
36116
  init_verifier_pick();
36228
36117
  });
36229
36118
 
@@ -36251,14 +36140,10 @@ function buildResolverCallContext(provided, agentManager, storyId, workdir, feat
36251
36140
  ...sessionOverride !== undefined ? { sessionOverride } : {}
36252
36141
  };
36253
36142
  }
36254
- async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, config2, callContext, storyId, timeoutMs, workdir, featureName, reviewerSession, resolverContext, promptSuffix, debaters, agentManager) {
36143
+ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, config2, callContext, storyId, timeoutMs, workdir, featureName, promptSuffix, debaters, agentManager) {
36255
36144
  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) {
36145
+ const kind = pickSelectorKind(stageConfig);
36146
+ if ((kind === "majority-fail-closed" || kind === "majority-fail-open") && workdir !== undefined) {
36262
36147
  logger?.warn("debate", "majority resolver does not support implementer session resumption \u2014 switch to synthesis or custom resolver for context-aware semantic review");
36263
36148
  }
36264
36149
  const proposalList = debaters ? debaters.map((debater, i) => ({
@@ -36285,48 +36170,20 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
36285
36170
  stageConfig,
36286
36171
  config: effectiveConfig,
36287
36172
  proposals: proposalList,
36288
- labeledProposals: resolverContext?.labeledProposals,
36289
36173
  critiques: critiqueOutputs,
36290
36174
  workdir: workdir ?? "",
36291
36175
  featureName: featureName ?? "",
36292
36176
  timeoutMs,
36293
36177
  agentManager: effectiveAgentManager,
36294
- reviewerSession,
36295
- resolverContextInput,
36296
36178
  promptSuffix,
36297
36179
  debaters: debaters ?? [],
36298
36180
  callContext: effectiveCallContext
36299
36181
  };
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
36182
  const result = await resolveSelector(kind)(selectorCtx);
36325
36183
  return {
36326
36184
  outcome: result.outcome,
36327
36185
  output: result.output,
36328
- findings: result.findings,
36329
- dialogueResult: result.dialogueResult
36186
+ findings: result.findings
36330
36187
  };
36331
36188
  }
36332
36189
  var RESOLVER_FALLBACK_AGENT3 = "synthesis", _debateSessionDeps;
@@ -36966,6 +36823,46 @@ var init_verdict = __esm(() => {
36966
36823
  });
36967
36824
 
36968
36825
  // src/operations/verify.ts
36826
+ function buildVerifierFindings(verdict, categorization) {
36827
+ if (categorization.success)
36828
+ return [];
36829
+ switch (categorization.failureCategory) {
36830
+ case "verifier-rejected": {
36831
+ const files = verdict.testModifications.files;
36832
+ return [
36833
+ {
36834
+ source: "tdd-verifier",
36835
+ severity: "error",
36836
+ category: "illegitimate-test-edits",
36837
+ fixTarget: "test",
36838
+ message: files.length > 0 ? `Implementer edited test files illegitimately: ${files.join(", ")}` : "Implementer made illegitimate test modifications",
36839
+ meta: {
36840
+ reasoning: verdict.testModifications.reasoning,
36841
+ files
36842
+ }
36843
+ }
36844
+ ];
36845
+ }
36846
+ case "tests-failing": {
36847
+ return [
36848
+ {
36849
+ source: "tdd-verifier",
36850
+ severity: "error",
36851
+ category: "tests-failed",
36852
+ fixTarget: "source",
36853
+ message: `${verdict.tests.failCount} story-scoped test(s) failed (verifier)`,
36854
+ meta: {
36855
+ passCount: verdict.tests.passCount,
36856
+ failCount: verdict.tests.failCount,
36857
+ reasoning: verdict.reasoning
36858
+ }
36859
+ }
36860
+ ];
36861
+ }
36862
+ default:
36863
+ return [];
36864
+ }
36865
+ }
36969
36866
  function parseVerdictFromStdout(output, _input, _ctx) {
36970
36867
  if (!output || !output.trim()) {
36971
36868
  throw new ParseValidationError("verifier produced no stdout");
@@ -36985,6 +36882,7 @@ function parseVerdictFromStdout(output, _input, _ctx) {
36985
36882
  estimatedCostUsd: 0,
36986
36883
  durationMs: 0,
36987
36884
  output,
36885
+ normalizedFindings: buildVerifierFindings(verdict, categorization),
36988
36886
  ...categorization.failureCategory && { failureCategory: categorization.failureCategory },
36989
36887
  ...categorization.reviewReason && { reviewReason: categorization.reviewReason }
36990
36888
  };
@@ -37057,6 +36955,7 @@ var init_verify = __esm(() => {
37057
36955
  estimatedCostUsd: 0,
37058
36956
  durationMs: 0,
37059
36957
  output: "",
36958
+ normalizedFindings: buildVerifierFindings(verdict, categorization),
37060
36959
  ...categorization.failureCategory && { failureCategory: categorization.failureCategory },
37061
36960
  ...categorization.reviewReason && { reviewReason: categorization.reviewReason },
37062
36961
  ...isolation && { isolation }
@@ -37068,6 +36967,7 @@ var init_verify = __esm(() => {
37068
36967
  estimatedCostUsd: 0,
37069
36968
  durationMs: 0,
37070
36969
  output: "",
36970
+ normalizedFindings: [],
37071
36971
  reviewReason: "verifier produced unparseable verdict in stdout after retries and no usable verdict file on disk"
37072
36972
  };
37073
36973
  } finally {
@@ -38081,7 +37981,8 @@ var init__finding_to_check = __esm(() => {
38081
37981
  "semantic-review": "semantic",
38082
37982
  "adversarial-review": "adversarial",
38083
37983
  lint: "lint",
38084
- typecheck: "typecheck"
37984
+ typecheck: "typecheck",
37985
+ "tdd-verifier": "test"
38085
37986
  };
38086
37987
  });
38087
37988
 
@@ -38093,7 +37994,8 @@ function makeAutofixImplementerStrategy(story, config2, sink) {
38093
37994
  fixOp: implementerRectifyOp,
38094
37995
  buildInput: (findings, _prior, _cycleCtx) => ({
38095
37996
  failedChecks: findingsToFailedChecks(findings),
38096
- story
37997
+ story,
37998
+ findings
38097
37999
  }),
38098
38000
  extractApplied: (output) => {
38099
38001
  for (const decl of output.testEditDeclarations) {
@@ -38116,7 +38018,7 @@ var IMPLEMENTER_SOURCES;
38116
38018
  var init_autofix_implementer_strategy = __esm(() => {
38117
38019
  init__finding_to_check();
38118
38020
  init_autofix_implementer();
38119
- IMPLEMENTER_SOURCES = new Set(["lint", "typecheck", "semantic-review"]);
38021
+ IMPLEMENTER_SOURCES = new Set(["lint", "typecheck", "semantic-review", "tdd-verifier"]);
38120
38022
  });
38121
38023
 
38122
38024
  // src/operations/autofix-test-writer-strategy.ts
@@ -38859,6 +38761,9 @@ var init_operations = __esm(() => {
38859
38761
  });
38860
38762
 
38861
38763
  // src/findings/cycle.ts
38764
+ function normalizeValidateResult(r) {
38765
+ return Array.isArray(r) ? { findings: r, shortCircuited: false } : r;
38766
+ }
38862
38767
  function classifySingleSource(before, after) {
38863
38768
  const beforeKeys = new Set(before.map(findingKey));
38864
38769
  const afterKeys = new Set(after.map(findingKey));
@@ -39055,11 +38960,12 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
39055
38960
  });
39056
38961
  if (allExhausted) {
39057
38962
  let liteFindingsAfter;
38963
+ let liteShortCircuited = false;
39058
38964
  try {
39059
- liteFindingsAfter = await cycle.validate(ctx, {
39060
- mode: "lite",
39061
- strategiesRun: group.map((s) => s.name)
39062
- });
38965
+ const liteRaw = await cycle.validate(ctx, { mode: "lite", strategiesRun: group.map((s) => s.name) });
38966
+ const liteResult = normalizeValidateResult(liteRaw);
38967
+ liteFindingsAfter = liteResult.findings;
38968
+ liteShortCircuited = liteResult.shortCircuited ?? false;
39063
38969
  } catch (err) {
39064
38970
  const finishedAt3 = now();
39065
38971
  cycle.iterations.push({
@@ -39097,7 +39003,7 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
39097
39003
  finishedAt: finishedAt2
39098
39004
  });
39099
39005
  cycle.findings = liteFindingsAfter;
39100
- if (liteFindingsAfter.length === 0) {
39006
+ if (liteFindingsAfter.length === 0 && !liteShortCircuited) {
39101
39007
  logger?.info("findings.cycle", "cycle exited \u2014 resolved after terminal lite validate", {
39102
39008
  storyId,
39103
39009
  packageDir,
@@ -39111,6 +39017,21 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
39111
39017
  costUsd: totalCostUsd
39112
39018
  };
39113
39019
  }
39020
+ if (liteShortCircuited) {
39021
+ logger?.info("findings.cycle", "cycle exited \u2014 validate short-circuited", {
39022
+ storyId,
39023
+ packageDir,
39024
+ cycleName,
39025
+ reason: "validate-short-circuit",
39026
+ liteFindingsAfterCount: liteFindingsAfter.length
39027
+ });
39028
+ return {
39029
+ iterations: cycle.iterations,
39030
+ finalFindings: liteFindingsAfter,
39031
+ exitReason: "validate-short-circuit",
39032
+ costUsd: totalCostUsd
39033
+ };
39034
+ }
39114
39035
  logger?.info("findings.cycle", "cycle exited \u2014 strategy attempt cap reached (lite validate)", {
39115
39036
  storyId,
39116
39037
  packageDir,
@@ -39131,7 +39052,8 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
39131
39052
  let validatorAttempt = 0;
39132
39053
  for (;; ) {
39133
39054
  try {
39134
- findingsAfter = await cycle.validate(ctx, { mode: "full", strategiesRun: group.map((s) => s.name) });
39055
+ const fullRaw = await cycle.validate(ctx, { mode: "full", strategiesRun: group.map((s) => s.name) });
39056
+ findingsAfter = normalizeValidateResult(fullRaw).findings;
39135
39057
  break;
39136
39058
  } catch (err) {
39137
39059
  if (validatorAttempt >= cycle.config.validatorRetries) {
@@ -39775,7 +39697,6 @@ async function runSemanticDebate(opts) {
39775
39697
  agentManager,
39776
39698
  featureName,
39777
39699
  story,
39778
- resolverSession,
39779
39700
  diffMode,
39780
39701
  diff,
39781
39702
  stat,
@@ -39793,10 +39714,9 @@ async function runSemanticDebate(opts) {
39793
39714
  ...configuredStageConfig,
39794
39715
  sessionMode: "one-shot",
39795
39716
  mode: "panel",
39796
- selector: { kind: "dialogue-verdict" },
39717
+ selector: { kind: pickBaseSelectorKind(configuredStageConfig) },
39797
39718
  postDebateVerifier: { kind: "review-grounding-filter" }
39798
39719
  };
39799
- const isReReview = resolverSession !== undefined && resolverSession.history.length > 0;
39800
39720
  const semanticAgentName = agentManager && typeof agentManager.getDefault === "function" ? agentManager.getDefault() : "claude";
39801
39721
  const semanticCallCtx = {
39802
39722
  runtime,
@@ -39813,89 +39733,10 @@ async function runSemanticDebate(opts) {
39813
39733
  config: naxConfig,
39814
39734
  workdir,
39815
39735
  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
39736
+ timeoutSeconds: naxConfig.execution?.sessionTimeoutSeconds
39827
39737
  });
39828
- const historyLenBefore = resolverSession?.history.length ?? 0;
39829
39738
  const debateResult = await debateRunner.run(prompt);
39830
39739
  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
39740
  const resolverPassed = debateResult.outcome === "passed";
39900
39741
  const allFindings = [];
39901
39742
  for (const p of debateResult.proposals) {
@@ -40015,6 +39856,7 @@ ${formatFindings2(debateBlocking)}`,
40015
39856
  };
40016
39857
  }
40017
39858
  var init_semantic_debate = __esm(() => {
39859
+ init_debate();
40018
39860
  init_logger2();
40019
39861
  init_ac_quote_validator();
40020
39862
  init_finding_projection();
@@ -40049,7 +39891,6 @@ async function runSemanticReview(opts) {
40049
39891
  agentManager,
40050
39892
  naxConfig,
40051
39893
  featureName,
40052
- resolverSession,
40053
39894
  priorSemanticIterations,
40054
39895
  blockingThreshold,
40055
39896
  featureContextMarkdown,
@@ -40183,7 +40024,6 @@ async function runSemanticReview(opts) {
40183
40024
  agentManager: effectiveAgentManager,
40184
40025
  featureName,
40185
40026
  story,
40186
- resolverSession,
40187
40027
  diffMode,
40188
40028
  diff,
40189
40029
  stat,
@@ -40550,7 +40390,6 @@ async function runReview(opts) {
40550
40390
  naxConfig,
40551
40391
  retrySkipChecks,
40552
40392
  featureName,
40553
- resolverSession,
40554
40393
  priorFailures,
40555
40394
  priorSemanticIterations,
40556
40395
  featureContextMarkdown,
@@ -40630,7 +40469,6 @@ async function runReview(opts) {
40630
40469
  agentManager,
40631
40470
  naxConfig,
40632
40471
  featureName,
40633
- resolverSession,
40634
40472
  priorSemanticIterations,
40635
40473
  blockingThreshold: config2.blockingThreshold,
40636
40474
  featureContextMarkdown,
@@ -41568,6 +41406,26 @@ Tests are failing. Fix the source so all tests pass \u2014 not just the ones lis
41568
41406
  parts.push(escapeHatchFor(opts.story));
41569
41407
  return parts.join("");
41570
41408
  }
41409
+ static verifierContext(findings) {
41410
+ if (findings.length === 0) {
41411
+ return "The verifier found no issues.";
41412
+ }
41413
+ const lines = [
41414
+ `Fix the following ${findings.length} verifier finding${findings.length === 1 ? "" : "s"}:
41415
+ `
41416
+ ];
41417
+ for (const f of findings) {
41418
+ const reasoning = f.meta?.reasoning ?? "";
41419
+ const detail = reasoning ? ` Reasoning: ${reasoning}
41420
+ ` : "";
41421
+ lines.push(`- [${f.category}] ${f.message}
41422
+ ${detail}`);
41423
+ }
41424
+ lines.push(`
41425
+ Fix the implementation to resolve all verifier findings.`);
41426
+ return lines.join(`
41427
+ `);
41428
+ }
41571
41429
  static failingTestContext(findings) {
41572
41430
  if (findings.length === 0) {
41573
41431
  return "The full test suite has failing tests. Fix the implementation to make all tests pass.";
@@ -46051,17 +45909,6 @@ function createDebaterCallContext(ctx, agentName) {
46051
45909
  }
46052
45910
  };
46053
45911
  }
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
45912
  async function runZeroSuccessFallback(ctx, prompt, firstDebater) {
46066
45913
  if (!firstDebater)
46067
45914
  return null;
@@ -46090,7 +45937,6 @@ var init_runner_stateful_helpers = __esm(() => {
46090
45937
  init_call();
46091
45938
  init_debate_stateful();
46092
45939
  init_prompts();
46093
- init_personas();
46094
45940
  DEFAULT_ABORT_SIGNAL = new AbortController().signal;
46095
45941
  });
46096
45942
 
@@ -46212,13 +46058,7 @@ async function runHybrid(ctx, prompt) {
46212
46058
  }
46213
46059
  }
46214
46060
  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);
46061
+ 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
46062
  return {
46223
46063
  storyId: ctx.storyId,
46224
46064
  stage: ctx.stage,
@@ -46647,9 +46487,7 @@ async function scoreAndDispatchVerifierPick(resolved, rebuttalSettled, ctx, opts
46647
46487
  timeoutMs: (ctx.stageConfig.timeoutSeconds ?? 600) * 1000,
46648
46488
  agentManager,
46649
46489
  debaters: resolved.map((e) => e.debater),
46650
- callContext: ctx.callContext,
46651
- ...ctx.reviewerSession ? { reviewerSession: ctx.reviewerSession } : {},
46652
- ...ctx.resolverContextInput ? { resolverContextInput: ctx.resolverContextInput } : {}
46490
+ callContext: ctx.callContext
46653
46491
  };
46654
46492
  const manifest = extractManifestFromContext(scoringCtx);
46655
46493
  const scored = await Promise.all(rebutProposals.map(async (p) => ({ proposal: p, score: await computeScore(p, manifest) })));
@@ -46728,9 +46566,7 @@ async function finalizePlanRun(ctx, opts, successful, rebuttalList, outputPaths,
46728
46566
  timeoutMs: (ctx.stageConfig.timeoutSeconds ?? 600) * 1000,
46729
46567
  agentManager,
46730
46568
  debaters: finalizedProposals.map((p) => p.debater),
46731
- callContext: ctx.callContext,
46732
- ...ctx.reviewerSession ? { reviewerSession: ctx.reviewerSession } : {},
46733
- ...ctx.resolverContextInput ? { resolverContextInput: ctx.resolverContextInput } : {}
46569
+ callContext: ctx.callContext
46734
46570
  };
46735
46571
  const manifest = extractManifestFromContext(selectorCtx);
46736
46572
  const scored = await Promise.all(selectorCtx.proposals.map(async (proposal) => ({ proposal, score: await computeScore(proposal, manifest) })));
@@ -46744,7 +46580,7 @@ async function finalizePlanRun(ctx, opts, successful, rebuttalList, outputPaths,
46744
46580
  const outcome = selectorKind === "verifier-pick" ? {
46745
46581
  outcome: "passed",
46746
46582
  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);
46583
+ } : 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
46584
  let finalOutcome = outcome.outcome;
46749
46585
  let winningOutput = outcome.output ?? finalizedProposals[0]?.output;
46750
46586
  winningOutput = await readWinnerOutput(selectionSummary.winnerOutputPath ?? outputPaths[0], winningOutput);
@@ -46785,7 +46621,6 @@ async function finalizePlanRun(ctx, opts, successful, rebuttalList, outputPaths,
46785
46621
  var _runPlanDeps, FILE_OUTPUT_INSTRUCTION = "Write the complete PRD JSON to this file path and then reply with a short confirmation:";
46786
46622
  var init_runner_plan_helpers = __esm(() => {
46787
46623
  init_pre_phase();
46788
- init_runner_stateful_helpers();
46789
46624
  init_verifier_pick();
46790
46625
  init_session_helpers();
46791
46626
  init_verifiers();
@@ -47041,9 +46876,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
47041
46876
  stage: ctx.stage,
47042
46877
  stageConfig: ctx.stageConfig,
47043
46878
  config: ctx.config,
47044
- callContext: ctx.callContext,
47045
- reviewerSession: ctx.reviewerSession,
47046
- resolverContextInput: ctx.resolverContextInput
46879
+ callContext: ctx.callContext
47047
46880
  }, { workdir: opts.workdir, feature: opts.feature, specContent: opts.specContent }, successful, rebuttalList, outputPaths, totalCostUsd, agentManager, selectorKind, includeHybridRebuttals);
47048
46881
  }
47049
46882
  var DEFAULT_MAX_CONCURRENT_DEBATERS = 2;
@@ -47177,7 +47010,7 @@ async function runStateful(ctx, prompt) {
47177
47010
  }).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
47011
  throwIfAborted2();
47179
47012
  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);
47013
+ 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
47014
  logger?.info("debate", "debate:result", {
47182
47015
  storyId: ctx.storyId,
47183
47016
  stage: ctx.stage,
@@ -47220,8 +47053,6 @@ class DebateRunner {
47220
47053
  featureName;
47221
47054
  timeoutSeconds;
47222
47055
  sessionManager;
47223
- reviewerSession;
47224
- resolverContextInput;
47225
47056
  constructor(opts) {
47226
47057
  this.ctx = opts.ctx;
47227
47058
  this.stage = opts.stage;
@@ -47231,8 +47062,6 @@ class DebateRunner {
47231
47062
  this.featureName = opts.featureName ?? opts.stage;
47232
47063
  this.timeoutSeconds = opts.timeoutSeconds ?? opts.stageConfig.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS;
47233
47064
  this.sessionManager = opts.sessionManager ?? opts.ctx.runtime?.sessionManager;
47234
- this.reviewerSession = opts.reviewerSession;
47235
- this.resolverContextInput = opts.resolverContextInput;
47236
47065
  }
47237
47066
  async run(prompt) {
47238
47067
  const sessionMode = this.stageConfig.sessionMode ?? "one-shot";
@@ -47426,14 +47255,7 @@ ${prompt}`;
47426
47255
  critiqueOutputs = critiqueSettled.filter((r) => r.status === "fulfilled").map((r) => r.value);
47427
47256
  }
47428
47257
  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);
47258
+ 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
47259
  let finalOutcome = selectorOutcome.outcome;
47438
47260
  if (config2.postDebateVerifier) {
47439
47261
  const verifierCtx = {
@@ -47443,8 +47265,8 @@ ${prompt}`;
47443
47265
  selectorResult: selectorOutcome,
47444
47266
  workdir: this.workdir,
47445
47267
  ctx: { ...this.ctx, scopeId: verifierScope.scopeId },
47446
- acceptanceCriteria: this.resolverContextInput?.story.acceptanceCriteria,
47447
- blockingThreshold: this.resolverContextInput?.blockingThreshold
47268
+ acceptanceCriteria: undefined,
47269
+ blockingThreshold: undefined
47448
47270
  };
47449
47271
  const verifierResult = await resolvePostDebateVerifier(config2.postDebateVerifier.kind)(verifierCtx);
47450
47272
  finalOutcome = verifierResult.outcome;
@@ -47481,9 +47303,7 @@ ${prompt}`;
47481
47303
  agentManager: this.ctx.runtime.agentManager,
47482
47304
  sessionManager: this.sessionManager ?? this.ctx.runtime.sessionManager,
47483
47305
  runtime: this.ctx.runtime,
47484
- abortSignal: this.ctx.runtime.signal,
47485
- reviewerSession: this.reviewerSession,
47486
- resolverContextInput: this.resolverContextInput
47306
+ abortSignal: this.ctx.runtime.signal
47487
47307
  };
47488
47308
  }
47489
47309
  toPlanCtx() {
@@ -47517,6 +47337,11 @@ var init_runner3 = __esm(() => {
47517
47337
  init_verifiers();
47518
47338
  });
47519
47339
 
47340
+ // src/debate/resolvers.ts
47341
+ var init_resolvers = __esm(() => {
47342
+ init_prompts();
47343
+ });
47344
+
47520
47345
  // src/debate/index.ts
47521
47346
  var init_debate = __esm(() => {
47522
47347
  init_runner3();
@@ -53044,7 +52869,7 @@ function extractPhaseFindings(output) {
53044
52869
  return [];
53045
52870
  }
53046
52871
  const record2 = output;
53047
- const rawArray = Array.isArray(record2.normalizedFindings) ? record2.normalizedFindings : Array.isArray(record2.findings) ? record2.findings : [];
52872
+ const rawArray = Array.isArray(record2.normalizedFindings) && record2.normalizedFindings.length > 0 ? record2.normalizedFindings : Array.isArray(record2.findings) ? record2.findings : [];
53048
52873
  const findings = rawArray.filter(isFinding);
53049
52874
  const success2 = "success" in record2 ? record2.success === true : ("passed" in record2) ? record2.passed === true : findings.length === 0;
53050
52875
  return success2 ? [] : findings;
@@ -53334,7 +53159,7 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53334
53159
  config: { maxAttemptsTotal: rectification.maxAttempts, validatorRetries: 1 },
53335
53160
  validate: async (_validateCtx, opts) => {
53336
53161
  if (ctx.runtime.signal?.aborted)
53337
- return [];
53162
+ return { findings: [], shortCircuited: false };
53338
53163
  const lite = (opts?.mode ?? "full") === "lite";
53339
53164
  const phases = phasesToRevalidate(opts?.strategiesRun, validationPhases);
53340
53165
  getSafeLogger()?.debug("story-orchestrator", "rectification validate scope", {
@@ -53344,6 +53169,7 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53344
53169
  phasesSelected: phases.map((p) => p.kind)
53345
53170
  });
53346
53171
  const findings = [];
53172
+ let shortCircuited = false;
53347
53173
  for (const phase of phases) {
53348
53174
  if (lite && phase.kind === "full-suite-gate") {
53349
53175
  continue;
@@ -53358,10 +53184,12 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53358
53184
  storyId: ctx.storyId,
53359
53185
  phase: phase.slot.op.name
53360
53186
  });
53187
+ shortCircuited = true;
53361
53188
  break;
53362
53189
  }
53363
53190
  }
53364
- return rectification.postValidate ? await rectification.postValidate(findings, _validateCtx) : findings;
53191
+ const validated = rectification.postValidate ? await rectification.postValidate(findings, _validateCtx) : findings;
53192
+ return { findings: validated, shortCircuited };
53365
53193
  }
53366
53194
  };
53367
53195
  const cycleResult = await _storyOrchestratorDeps.runFixCycle(cycle, ctx, "story-orchestrator-rectification", { callOp: wrappedCallOp });
@@ -53393,6 +53221,9 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
53393
53221
  if (EXHAUSTED_EXIT_REASONS.has(cycleResult.exitReason) && cycleResult.finalFindings.length > 0) {
53394
53222
  return { rectificationExhausted: true, unfixedFindings: cycleResult.finalFindings };
53395
53223
  }
53224
+ if (cycleResult.exitReason === "validate-short-circuit") {
53225
+ return { liteScopeIncomplete: true };
53226
+ }
53396
53227
  return {};
53397
53228
  }
53398
53229
 
@@ -53437,7 +53268,7 @@ class ExecutionPlan {
53437
53268
  }
53438
53269
  }
53439
53270
  const rectResult = await runRectification(this.ctx, this.state, phaseCosts, phaseOutputs);
53440
- if (this.state.rectification && !rectResult.rectificationExhausted) {
53271
+ if (this.state.rectification && (!rectResult.rectificationExhausted || rectResult.liteScopeIncomplete)) {
53441
53272
  let resumeRectifyUsed = false;
53442
53273
  for (const phase of collectOrderedPhases(this.state)) {
53443
53274
  const name = phase.slot.op.name;
@@ -53601,7 +53432,8 @@ var init_story_orchestrator = __esm(() => {
53601
53432
  "max-attempts-per-strategy",
53602
53433
  "bail-when",
53603
53434
  "no-strategy",
53604
- "agent-gave-up"
53435
+ "agent-gave-up",
53436
+ "validate-short-circuit"
53605
53437
  ]);
53606
53438
  TDD_OP_NAMES = new Set(["test-writer", "implementer", "verifier"]);
53607
53439
  STRICT_VERDICT_PHASE_NAMES = new Set([
@@ -58017,7 +57849,7 @@ var package_default;
58017
57849
  var init_package = __esm(() => {
58018
57850
  package_default = {
58019
57851
  name: "@nathapp/nax",
58020
- version: "0.67.19",
57852
+ version: "0.68.0",
58021
57853
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
58022
57854
  type: "module",
58023
57855
  bin: {
@@ -58112,8 +57944,8 @@ var init_version = __esm(() => {
58112
57944
  NAX_VERSION = package_default.version;
58113
57945
  NAX_COMMIT = (() => {
58114
57946
  try {
58115
- if (/^[0-9a-f]{6,10}$/.test("e80ba4d6"))
58116
- return "e80ba4d6";
57947
+ if (/^[0-9a-f]{6,10}$/.test("d56db412"))
57948
+ return "d56db412";
58117
57949
  } catch {}
58118
57950
  try {
58119
57951
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
@@ -59462,6 +59294,18 @@ async function handleRunCompletion(options) {
59462
59294
  }
59463
59295
  }
59464
59296
  }
59297
+ let pluginGateFailed = false;
59298
+ const deferredReview = options.deferredReview;
59299
+ if (deferredReview?.anyFailed) {
59300
+ const failedReviewers = deferredReview.reviewerResults.filter((r) => !r.passed).map((r) => r.name);
59301
+ pluginGateFailed = config2.review.pluginMode === "gating";
59302
+ logger?.warn("review", "Deferred plugin reviewer(s) reported failures", {
59303
+ storyId: runId,
59304
+ failedReviewers,
59305
+ pluginMode: config2.review.pluginMode,
59306
+ gating: pluginGateFailed
59307
+ });
59308
+ }
59465
59309
  const aggSnap = options.runtime.costAggregator.snapshot();
59466
59310
  const aggregatorTotal = aggSnap.totalCostUsd;
59467
59311
  const reportedTotal = Math.max(totalCost, aggregatorTotal);
@@ -59610,7 +59454,8 @@ async function handleRunCompletion(options) {
59610
59454
  failed: finalCounts.failed,
59611
59455
  skipped: finalCounts.skipped,
59612
59456
  pending: finalCounts.pending
59613
- }
59457
+ },
59458
+ pluginGateFailed
59614
59459
  };
59615
59460
  }
59616
59461
  var _runCompletionDeps;
@@ -98175,12 +98020,13 @@ async function runCompletionPhase(options) {
98175
98020
  skipRegression: regressionAlreadyPassed,
98176
98021
  sessionManager: options.sessionManager,
98177
98022
  pluginProviderCache: options.pluginProviderCache,
98023
+ deferredReview: options.deferredReview,
98178
98024
  runtime: options.runtime,
98179
98025
  abortSignal: options.abortSignal
98180
98026
  });
98181
- const { durationMs, runCompletedAt, finalCounts, reportedTotal } = completionResult;
98027
+ const { durationMs, runCompletedAt, finalCounts, reportedTotal, pluginGateFailed } = completionResult;
98182
98028
  if (options.featureDir) {
98183
- const finalStatus = isComplete(options.prd) ? "completed" : "failed";
98029
+ const finalStatus = isComplete(options.prd) && !pluginGateFailed ? "completed" : "failed";
98184
98030
  options.statusWriter.setRunStatus(finalStatus);
98185
98031
  await options.statusWriter.writeFeatureStatus(options.featureDir, reportedTotal, options.iterations);
98186
98032
  }
@@ -98210,7 +98056,8 @@ async function runCompletionPhase(options) {
98210
98056
  return {
98211
98057
  durationMs,
98212
98058
  runCompletedAt,
98213
- acceptancePassed
98059
+ acceptancePassed,
98060
+ pluginGateFailed
98214
98061
  };
98215
98062
  }
98216
98063
 
@@ -98302,7 +98149,14 @@ async function runExecutionPhase(options, prd, pluginRegistry) {
98302
98149
  storiesCompleted,
98303
98150
  totalCost
98304
98151
  });
98305
- return { prd, iterations, storiesCompleted, totalCost, allStoryMetrics };
98152
+ return {
98153
+ prd,
98154
+ iterations,
98155
+ storiesCompleted,
98156
+ totalCost,
98157
+ allStoryMetrics,
98158
+ deferredReview: unifiedResult.deferredReview
98159
+ };
98306
98160
  }
98307
98161
 
98308
98162
  // src/execution/runner-setup.ts
@@ -98466,13 +98320,14 @@ async function run(options) {
98466
98320
  sessionManager,
98467
98321
  agentManager,
98468
98322
  pluginProviderCache,
98323
+ deferredReview: executionResult.deferredReview,
98469
98324
  runtime,
98470
98325
  abortSignal: shutdownController.signal
98471
98326
  });
98472
- const { durationMs, acceptancePassed } = completionResult;
98327
+ const { durationMs, acceptancePassed, pluginGateFailed } = completionResult;
98473
98328
  runCompleted = true;
98474
98329
  return {
98475
- success: isComplete(prd) && acceptancePassed,
98330
+ success: isComplete(prd) && acceptancePassed && !pluginGateFailed,
98476
98331
  iterations,
98477
98332
  storiesCompleted,
98478
98333
  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.0",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {