@nathapp/nax 0.67.0 → 0.67.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/nax.js +74 -15
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -35108,6 +35108,24 @@ async function getChangedFiles(workdir, fromRef = "HEAD") {
35108
35108
  return output.trim().split(`
35109
35109
  `).filter(Boolean);
35110
35110
  }
35111
+ async function getAddedLinesPerFile(workdir, fromRef = "HEAD") {
35112
+ const proc = _isolationDeps.spawn(["git", "diff", "--numstat", fromRef], {
35113
+ cwd: workdir,
35114
+ stdout: "pipe",
35115
+ stderr: "pipe"
35116
+ });
35117
+ const output = await Bun.readableStreamToText(proc.stdout);
35118
+ await proc.exited;
35119
+ const result = new Map;
35120
+ for (const line of output.trim().split(`
35121
+ `).filter(Boolean)) {
35122
+ const [addedStr, _deletedStr, path4] = line.split("\t");
35123
+ const added = Number.parseInt(addedStr ?? "", 10);
35124
+ if (path4 && Number.isFinite(added))
35125
+ result.set(path4, added);
35126
+ }
35127
+ return result;
35128
+ }
35111
35129
  function matchesAllowedPath(filePath, allowedPaths) {
35112
35130
  return allowedPaths.some((pattern) => {
35113
35131
  const regexPattern = pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\//g, "\\/");
@@ -35115,17 +35133,25 @@ function matchesAllowedPath(filePath, allowedPaths) {
35115
35133
  return regex.test(filePath);
35116
35134
  });
35117
35135
  }
35118
- async function verifyTestWriterIsolation(workdir, beforeRef, allowedPaths = ["src/index.ts", "src/**/index.ts"], testFilePatterns = DEFAULT_TEST_FILE_PATTERNS) {
35136
+ async function verifyTestWriterIsolation(workdir, beforeRef, allowedPaths = ["src/index.ts", "src/**/index.ts"], testFilePatterns = DEFAULT_TEST_FILE_PATTERNS, mode = "strict") {
35119
35137
  const changed = await getChangedFiles(workdir, beforeRef);
35120
35138
  const sourceFiles = changed.filter((f) => isSourceFile(f) && !isTestFileByPatterns(f, testFilePatterns));
35139
+ const addedLines = mode === "lite" && sourceFiles.length > 0 ? await getAddedLinesPerFile(workdir, beforeRef) : null;
35121
35140
  const softViolations = [];
35122
35141
  const violations = [];
35123
35142
  for (const file3 of sourceFiles) {
35124
35143
  if (matchesAllowedPath(file3, allowedPaths)) {
35125
35144
  softViolations.push(file3);
35126
- } else {
35127
- violations.push(file3);
35145
+ continue;
35146
+ }
35147
+ if (addedLines) {
35148
+ const added = addedLines.get(file3) ?? Number.POSITIVE_INFINITY;
35149
+ if (added <= LITE_STUB_ADDED_LINES_CEILING) {
35150
+ softViolations.push(file3);
35151
+ continue;
35152
+ }
35128
35153
  }
35154
+ violations.push(file3);
35129
35155
  }
35130
35156
  return {
35131
35157
  passed: violations.length === 0,
@@ -35151,7 +35177,7 @@ async function verifyImplementerIsolation(workdir, beforeRef, testFilePatterns =
35151
35177
  description: "Implementer should not modify test files"
35152
35178
  };
35153
35179
  }
35154
- var _isolationDeps, SRC_PATTERNS;
35180
+ var _isolationDeps, SRC_PATTERNS, LITE_STUB_ADDED_LINES_CEILING = 20;
35155
35181
  var init_isolation = __esm(() => {
35156
35182
  init_test_runners();
35157
35183
  init_bun_deps();
@@ -35235,7 +35261,7 @@ var init_write_test = __esm(() => {
35235
35261
  return parsed;
35236
35262
  const allowedPaths = ctx.config.tdd?.testWriterAllowedPaths ?? ["src/index.ts", "src/**/index.ts"];
35237
35263
  const testFilePatterns = typeof ctx.packageView.config.execution?.smartTestRunner === "object" && ctx.packageView.config.execution.smartTestRunner !== null ? ctx.packageView.config.execution.smartTestRunner.testFilePatterns : undefined;
35238
- const isolation = await verifyTestWriterIsolation(ctx.packageView.packageDir, input.beforeRef, allowedPaths, testFilePatterns);
35264
+ const isolation = await verifyTestWriterIsolation(ctx.packageView.packageDir, input.beforeRef, allowedPaths, testFilePatterns, input.lite ? "lite" : "strict");
35239
35265
  return { ...parsed, isolation };
35240
35266
  }
35241
35267
  };
@@ -37189,10 +37215,32 @@ async function runRectificationLoop(opts) {
37189
37215
  }
37190
37216
  }
37191
37217
  });
37192
- const initialFailure = {
37193
- testOutput,
37194
- testSummary
37195
- };
37218
+ let initialFailure = { testOutput, testSummary };
37219
+ if (rectificationConfig.abortOnIncreasingFailures) {
37220
+ const preCheck = await _rectificationDeps.runVerification({
37221
+ workdir,
37222
+ expectedFiles: getExpectedFiles(story),
37223
+ command: testCommand,
37224
+ timeoutSeconds,
37225
+ forceExit: config2.quality.forceExit,
37226
+ detectOpenHandles: config2.quality.detectOpenHandles,
37227
+ detectOpenHandlesRetries: config2.quality.detectOpenHandlesRetries,
37228
+ timeoutRetryCount: 0,
37229
+ gracePeriodMs: config2.quality.gracePeriodMs,
37230
+ drainTimeoutMs: config2.quality.drainTimeoutMs,
37231
+ shell: config2.quality.shell,
37232
+ stripEnvVars: config2.quality.stripEnvVars
37233
+ });
37234
+ if (preCheck.output) {
37235
+ const preCheckSummary = parseTestOutput(preCheck.output);
37236
+ initialFailure = { testOutput: preCheck.output, testSummary: preCheckSummary };
37237
+ } else {
37238
+ logger?.warn("rectification", "pre-check returned no output \u2014 abort baseline may be scope-mismatched", {
37239
+ storyId: story.id,
37240
+ preCheckStatus: preCheck.status
37241
+ });
37242
+ }
37243
+ }
37196
37244
  const outcome = await runRetryLoop({
37197
37245
  stage: "rectification",
37198
37246
  storyId: story.id,
@@ -54346,10 +54394,13 @@ class ExecutionPlan {
54346
54394
  const phaseOutputs = {};
54347
54395
  const startedAt = Date.now();
54348
54396
  const logger = getSafeLogger();
54349
- const shortCircuitExempt = this.state.rectification ? new Set([
54397
+ const verifierPresent = this.state.verifier !== undefined;
54398
+ const rectificationExempt = this.state.rectification ? [
54350
54399
  ...this.state.fullSuiteGate ? [this.state.fullSuiteGate.slot.op.name] : [],
54351
54400
  ...this.state.verifier ? [this.state.verifier.slot.op.name] : []
54352
- ]) : new Set;
54401
+ ] : [];
54402
+ const verifierExempt = verifierPresent && this.state.fullSuiteGate ? [this.state.fullSuiteGate.slot.op.name] : [];
54403
+ const shortCircuitExempt = new Set([...rectificationExempt, ...verifierExempt]);
54353
54404
  for (const phase of collectOrderedPhases(this.state)) {
54354
54405
  try {
54355
54406
  await runPhase(this.ctx, phase.slot, phaseCosts, phaseOutputs, this.isThreeSession);
@@ -54577,7 +54628,8 @@ async function assemblePlanInputsFromCtx(ctx) {
54577
54628
  story,
54578
54629
  promptMarkdown: testWriterPrompt,
54579
54630
  featureContextMarkdown: ctx.featureContextMarkdown,
54580
- constitution: ctx.constitution?.content
54631
+ constitution: ctx.constitution?.content,
54632
+ lite: isLite
54581
54633
  } : undefined;
54582
54634
  const greenfieldGateInput = _isTdd && _isFreshRun && resolvedTestPatterns ? { story, workdir: ctx.workdir, resolvedTestPatterns } : undefined;
54583
54635
  const implementerInput = {
@@ -54778,6 +54830,13 @@ function deriveTddFailureCategory(phaseOutputs) {
54778
54830
  }
54779
54831
  return "tests-failing";
54780
54832
  }
54833
+ const verifierPassed = verifierOutput?.success === true;
54834
+ if (!verifierPassed) {
54835
+ const gateOutput = phaseOutputs[fullSuiteGateOp.name];
54836
+ if (gateOutput && (gateOutput.success === false || gateOutput.passed === false)) {
54837
+ return "tests-failing";
54838
+ }
54839
+ }
54781
54840
  const implOutput = phaseOutputs[implementerOp.name];
54782
54841
  if (implOutput?.success === false) {
54783
54842
  return "session-failure";
@@ -59160,7 +59219,7 @@ var package_default;
59160
59219
  var init_package = __esm(() => {
59161
59220
  package_default = {
59162
59221
  name: "@nathapp/nax",
59163
- version: "0.67.0",
59222
+ version: "0.67.2",
59164
59223
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
59165
59224
  type: "module",
59166
59225
  bin: {
@@ -59255,8 +59314,8 @@ var init_version = __esm(() => {
59255
59314
  NAX_VERSION = package_default.version;
59256
59315
  NAX_COMMIT = (() => {
59257
59316
  try {
59258
- if (/^[0-9a-f]{6,10}$/.test("ffa2f392"))
59259
- return "ffa2f392";
59317
+ if (/^[0-9a-f]{6,10}$/.test("d2b13ea6"))
59318
+ return "d2b13ea6";
59260
59319
  } catch {}
59261
59320
  try {
59262
59321
  const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.67.0",
3
+ "version": "0.67.2",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {