@nathapp/nax 0.67.18 → 0.67.19

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 +87 -13
  2. package/package.json +1 -1
package/dist/nax.js CHANGED
@@ -31242,7 +31242,7 @@ ${STEP2}${frameworkLine}
31242
31242
  ${STEP3_HEADER}
31243
31243
  ${STEP3_SHARED_RULES}
31244
31244
  - **File output (REQUIRED)**: Write the acceptance test file DIRECTLY to the path shown below. Do NOT output the test code in your response. After writing the file, reply with a brief confirmation.
31245
- - **Path anchor (CRITICAL)**: Write the test file to this exact path: \`${p.targetTestFilePath}\`. Import from package sources using relative paths like \`../../../src/...\` (3 levels up from \`.nax/features/<name>/\` to the package root).
31245
+ - **Path anchor (CRITICAL \u2014 do NOT deviate)**: Write the test file to this exact path: \`${p.targetTestFilePath}\`. This path is intentional and computed by the orchestrator \u2014 do not change it based on what you observe in the project. In particular: if you see a \`.nax/features/\` directory at the repo root, that is for stories scoped to the repo root. When a story belongs to a specific package (e.g. \`packages/core\`), its acceptance test lives inside that package's \`.nax/features/\` directory so the test runner can resolve the package's imports correctly. The package root is 3 levels above the test file (\`../../../\` relative to the test file).
31246
31246
  - **Process cwd**: When spawning child processes to invoke a CLI or binary, set the working directory to the **package root** (\`join(import.meta.dir, "../../..")\`) as your default \u2014 unless your Step 2 exploration reveals the CLI uses a different working directory convention (e.g. reads config from \`~/.config/\`, or resolves paths relative to a flag value). Always check how the CLI resolves file paths before assuming.${implSection}`;
31247
31247
  }
31248
31248
  buildGeneratorFromSpecPrompt(p) {
@@ -33716,6 +33716,35 @@ function acFailureToFinding(acId, output) {
33716
33716
  fixTarget: "source"
33717
33717
  };
33718
33718
  }
33719
+ function executionFailureToFinding(params) {
33720
+ const tail = tailLines(params.output, 40);
33721
+ const exitStr = params.exitCode !== undefined ? ` (exit ${params.exitCode})` : "";
33722
+ const message = `Test runner exited non-zero without structured failures${exitStr}. Command: \`${params.command}\`
33723
+
33724
+ --- runner output (last 40 lines) ---
33725
+ ${tail}`;
33726
+ return {
33727
+ source: "test-runner",
33728
+ severity: "error",
33729
+ category: "execution-failed",
33730
+ message,
33731
+ fixTarget: "source",
33732
+ meta: {
33733
+ command: params.command,
33734
+ exitCode: params.exitCode,
33735
+ packageDir: params.packageDir,
33736
+ cwd: params.cwd
33737
+ }
33738
+ };
33739
+ }
33740
+ function tailLines(s, n) {
33741
+ if (!s)
33742
+ return "(no output)";
33743
+ const lines = s.split(`
33744
+ `);
33745
+ return lines.slice(Math.max(0, lines.length - n)).join(`
33746
+ `);
33747
+ }
33719
33748
  function acSentinelToFinding(sentinel, _output) {
33720
33749
  if (sentinel === "AC-HOOK") {
33721
33750
  return {
@@ -37792,7 +37821,9 @@ async function runVerificationCore(options) {
37792
37821
  success: options.acceptOnTimeout ?? false,
37793
37822
  countsTowardEscalation: false,
37794
37823
  error: execution.error,
37795
- output: execution.output
37824
+ output: execution.output,
37825
+ exitCode: execution.exitCode,
37826
+ command: finalCommand
37796
37827
  };
37797
37828
  }
37798
37829
  const exitCode = execution.exitCode ?? 1;
@@ -37806,7 +37837,9 @@ async function runVerificationCore(options) {
37806
37837
  error: analysis.error,
37807
37838
  output: execution.output,
37808
37839
  passCount: analysis.passCount,
37809
- failCount: analysis.failCount
37840
+ failCount: analysis.failCount,
37841
+ exitCode,
37842
+ command: finalCommand
37810
37843
  };
37811
37844
  }
37812
37845
  return {
@@ -37815,10 +37848,19 @@ async function runVerificationCore(options) {
37815
37848
  countsTowardEscalation: true,
37816
37849
  output: execution.output,
37817
37850
  passCount: analysis.passCount,
37818
- failCount: analysis.failCount
37851
+ failCount: analysis.failCount,
37852
+ exitCode,
37853
+ command: finalCommand
37819
37854
  };
37820
37855
  }
37821
- return { status: "SUCCESS", success: true, countsTowardEscalation: true, output: execution.output };
37856
+ return {
37857
+ status: "SUCCESS",
37858
+ success: true,
37859
+ countsTowardEscalation: true,
37860
+ output: execution.output,
37861
+ exitCode,
37862
+ command: finalCommand
37863
+ };
37822
37864
  }
37823
37865
  async function fullSuite(options) {
37824
37866
  return runVerificationCore(options);
@@ -37895,7 +37937,9 @@ var init_full_suite_gate = __esm(() => {
37895
37937
  failed: parsedSummary.failed ?? 0,
37896
37938
  output: result.output ?? "",
37897
37939
  parsedSummary,
37898
- timedOut: result.status === "TIMEOUT"
37940
+ timedOut: result.status === "TIMEOUT",
37941
+ exitCode: result.exitCode,
37942
+ command: result.command ?? gateCtx.testCmd
37899
37943
  };
37900
37944
  }
37901
37945
  };
@@ -37922,6 +37966,13 @@ var init_full_suite_gate = __esm(() => {
37922
37966
  };
37923
37967
  }
37924
37968
  const gateCtx = await deps.resolveGateContext(input, ctx);
37969
+ logger.info("verify[regression]", "Running full-suite gate", {
37970
+ storyId: input.story.id,
37971
+ packageDir: input.story.workdir,
37972
+ cwd: input.workdir,
37973
+ command: gateCtx.testCmd,
37974
+ timeoutSeconds: gateCtx.fullSuiteTimeout
37975
+ });
37925
37976
  const testResult = await deps.runTests(input, gateCtx);
37926
37977
  if (testResult.passed) {
37927
37978
  return { success: true, passed: true, status: "passed", estimatedCostUsd: 0, attempts: 0, findings: [] };
@@ -37955,13 +38006,27 @@ var init_full_suite_gate = __esm(() => {
37955
38006
  }
37956
38007
  const findings = testSummaryToFindings(testResult.parsedSummary);
37957
38008
  if (findings.length === 0) {
38009
+ const cmd = testResult.command ?? gateCtx.testCmd;
38010
+ const synth = executionFailureToFinding({
38011
+ command: cmd,
38012
+ exitCode: testResult.exitCode,
38013
+ output: testResult.output,
38014
+ packageDir: input.story.workdir,
38015
+ cwd: input.workdir
38016
+ });
38017
+ logger.warn("verify[regression]", "Full-suite gate execution-failed \u2014 emitting synth finding", {
38018
+ storyId: input.story.id,
38019
+ command: cmd,
38020
+ exitCode: testResult.exitCode,
38021
+ packageDir: input.story.workdir
38022
+ });
37958
38023
  return {
37959
38024
  success: false,
37960
38025
  passed: false,
37961
38026
  status: "execution-failed",
37962
38027
  estimatedCostUsd: 0,
37963
38028
  attempts: 0,
37964
- findings: []
38029
+ findings: [synth]
37965
38030
  };
37966
38031
  }
37967
38032
  return { success: false, passed: false, status: "failed", estimatedCostUsd: 0, attempts: 0, findings };
@@ -37973,7 +38038,7 @@ var init_full_suite_gate = __esm(() => {
37973
38038
  function makeFullSuiteRectifyStrategy(story, config2) {
37974
38039
  return {
37975
38040
  name: "full-suite-rectify",
37976
- appliesTo: (finding) => finding.source === "test-runner" && finding.category === "failed-test",
38041
+ appliesTo: (finding) => finding.source === "test-runner" && (finding.category === "failed-test" || finding.category === "execution-failed"),
37977
38042
  fixOp: implementerOp,
37978
38043
  buildInput: (findings) => ({
37979
38044
  story,
@@ -38665,6 +38730,15 @@ var init_verify_scoped = __esm(() => {
38665
38730
  command: selection.effectiveCommand
38666
38731
  });
38667
38732
  }
38733
+ const scopedTimeout = ctxConfig.execution?.regressionGate?.timeoutSeconds ?? 600;
38734
+ logger.info("verify[scoped]", "Running scoped tests", {
38735
+ storyId: input.storyId,
38736
+ packageDir: input.packageDir,
38737
+ cwd: input.workdir,
38738
+ command: selection.effectiveCommand,
38739
+ timeoutSeconds: scopedTimeout,
38740
+ isFullSuite: selection.isFullSuite
38741
+ });
38668
38742
  const start = Date.now();
38669
38743
  const result = await deps.regression({
38670
38744
  workdir: input.workdir,
@@ -51491,9 +51565,9 @@ var init_acceptance2 = __esm(() => {
51491
51565
  function logTestOutput(logger, stage, output, opts = {}) {
51492
51566
  if (!logger || !output)
51493
51567
  return;
51494
- const tailLines = opts.tailLines ?? 20;
51568
+ const tailLines2 = opts.tailLines ?? 20;
51495
51569
  const lines = output.split(`
51496
- `).slice(-tailLines).join(`
51570
+ `).slice(-tailLines2).join(`
51497
51571
  `);
51498
51572
  logger.debug(stage, "Test output (tail)", {
51499
51573
  ...opts.storyId !== undefined && { storyId: opts.storyId },
@@ -57943,7 +58017,7 @@ var package_default;
57943
58017
  var init_package = __esm(() => {
57944
58018
  package_default = {
57945
58019
  name: "@nathapp/nax",
57946
- version: "0.67.18",
58020
+ version: "0.67.19",
57947
58021
  description: "AI Coding Agent Orchestrator \u2014 loops until done",
57948
58022
  type: "module",
57949
58023
  bin: {
@@ -58038,8 +58112,8 @@ var init_version = __esm(() => {
58038
58112
  NAX_VERSION = package_default.version;
58039
58113
  NAX_COMMIT = (() => {
58040
58114
  try {
58041
- if (/^[0-9a-f]{6,10}$/.test("cc7adcea"))
58042
- return "cc7adcea";
58115
+ if (/^[0-9a-f]{6,10}$/.test("e80ba4d6"))
58116
+ return "e80ba4d6";
58043
58117
  } catch {}
58044
58118
  try {
58045
58119
  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.18",
3
+ "version": "0.67.19",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {