@nathapp/nax 0.67.1 → 0.67.3
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.
- package/dist/nax.js +86 -15
- 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
|
-
|
|
35127
|
-
violations.push(file3);
|
|
35145
|
+
continue;
|
|
35128
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
|
+
}
|
|
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
|
};
|
|
@@ -51779,12 +51805,12 @@ async function assertionSiteDiffCheck(workdir, beforeRef, files) {
|
|
|
51779
51805
|
}
|
|
51780
51806
|
return { violated: false };
|
|
51781
51807
|
}
|
|
51782
|
-
async function runIsolationGuard(workdir, beforeRef, config2, packageDir) {
|
|
51808
|
+
async function runIsolationGuard(workdir, beforeRef, config2, packageDir, mode = "strict") {
|
|
51783
51809
|
if (config2.quality.autofix?.enforceTestWriterIsolation === false) {
|
|
51784
51810
|
return { violated: false, skipped: true };
|
|
51785
51811
|
}
|
|
51786
51812
|
const resolved = await resolveTestFilePatterns(config2, workdir, packageDir);
|
|
51787
|
-
const result = await _guardDeps.verifyTestWriterIsolation(workdir, beforeRef, config2.tdd?.testWriterAllowedPaths, resolved.globs);
|
|
51813
|
+
const result = await _guardDeps.verifyTestWriterIsolation(workdir, beforeRef, config2.tdd?.testWriterAllowedPaths, resolved.globs, mode);
|
|
51788
51814
|
if (!result.passed) {
|
|
51789
51815
|
return { violated: true, files: result.violations ?? [] };
|
|
51790
51816
|
}
|
|
@@ -52129,7 +52155,7 @@ async function runAgentRectificationV2(ctx, _lintFixCmd, _formatFixCmd, _effecti
|
|
|
52129
52155
|
});
|
|
52130
52156
|
return { unresolved: `assertion_weakening:${assertionResult.file}:${assertionResult.line}` };
|
|
52131
52157
|
}
|
|
52132
|
-
const isolationResult = await _autofixCycleGuardDeps.runIsolationGuard(ctx.workdir, beforeRef, ctx.config, ctx.story.workdir || undefined);
|
|
52158
|
+
const isolationResult = await _autofixCycleGuardDeps.runIsolationGuard(ctx.workdir, beforeRef, ctx.config, ctx.story.workdir || undefined, ctx.routing?.testStrategy === "three-session-tdd-lite" ? "lite" : "strict");
|
|
52133
52159
|
if (isolationResult.violated) {
|
|
52134
52160
|
await _autofixCycleGuardDeps.revertDiff(ctx.workdir, isolationResult.files);
|
|
52135
52161
|
logger.info("autofix-cycle", "test-writer isolation guard violated \u2014 reverted", {
|
|
@@ -54185,6 +54211,12 @@ function collectOrderedPhases(state) {
|
|
|
54185
54211
|
return [];
|
|
54186
54212
|
});
|
|
54187
54213
|
}
|
|
54214
|
+
function phaseExplicitlyPassed(output) {
|
|
54215
|
+
if (output === null || output === undefined || typeof output !== "object")
|
|
54216
|
+
return false;
|
|
54217
|
+
const r = output;
|
|
54218
|
+
return r.success === true || r.passed === true;
|
|
54219
|
+
}
|
|
54188
54220
|
function phasePassed(opName, output) {
|
|
54189
54221
|
if (output === null || output === undefined) {
|
|
54190
54222
|
getSafeLogger()?.warn("story-orchestrator", "Phase produced no output \u2014 treating as pass", {
|
|
@@ -54368,10 +54400,13 @@ class ExecutionPlan {
|
|
|
54368
54400
|
const phaseOutputs = {};
|
|
54369
54401
|
const startedAt = Date.now();
|
|
54370
54402
|
const logger = getSafeLogger();
|
|
54371
|
-
const
|
|
54403
|
+
const verifierPresent = this.state.verifier !== undefined;
|
|
54404
|
+
const rectificationExempt = this.state.rectification ? [
|
|
54372
54405
|
...this.state.fullSuiteGate ? [this.state.fullSuiteGate.slot.op.name] : [],
|
|
54373
54406
|
...this.state.verifier ? [this.state.verifier.slot.op.name] : []
|
|
54374
|
-
]
|
|
54407
|
+
] : [];
|
|
54408
|
+
const verifierExempt = verifierPresent && this.state.fullSuiteGate ? [this.state.fullSuiteGate.slot.op.name] : [];
|
|
54409
|
+
const shortCircuitExempt = new Set([...rectificationExempt, ...verifierExempt]);
|
|
54375
54410
|
for (const phase of collectOrderedPhases(this.state)) {
|
|
54376
54411
|
try {
|
|
54377
54412
|
await runPhase(this.ctx, phase.slot, phaseCosts, phaseOutputs, this.isThreeSession);
|
|
@@ -54390,7 +54425,17 @@ class ExecutionPlan {
|
|
|
54390
54425
|
}
|
|
54391
54426
|
}
|
|
54392
54427
|
await runRectification(this.ctx, this.state, phaseCosts, phaseOutputs);
|
|
54393
|
-
const
|
|
54428
|
+
const verifierName = this.state.verifier?.slot.op.name;
|
|
54429
|
+
const gateName = this.state.fullSuiteGate?.slot.op.name;
|
|
54430
|
+
const verifierPassedSsot = verifierName !== undefined && phaseExplicitlyPassed(phaseOutputs[verifierName]);
|
|
54431
|
+
if (verifierPassedSsot && gateName !== undefined && !phasePassed(gateName, phaseOutputs[gateName])) {
|
|
54432
|
+
logger?.warn("story-orchestrator", "Full-suite gate failed but verifier judged story OK \u2014 treating gate failures as unrelated regressions", { storyId: this.ctx.storyId, packageDir: this.ctx.packageDir });
|
|
54433
|
+
}
|
|
54434
|
+
const success2 = Object.entries(phaseOutputs).every(([name, output]) => {
|
|
54435
|
+
if (verifierPassedSsot && name === gateName)
|
|
54436
|
+
return true;
|
|
54437
|
+
return phasePassed(name, output);
|
|
54438
|
+
});
|
|
54394
54439
|
const totalCostUsd = Object.values(phaseCosts).reduce((sum, cost) => sum + cost, 0);
|
|
54395
54440
|
return {
|
|
54396
54441
|
success: success2,
|
|
@@ -54599,7 +54644,8 @@ async function assemblePlanInputsFromCtx(ctx) {
|
|
|
54599
54644
|
story,
|
|
54600
54645
|
promptMarkdown: testWriterPrompt,
|
|
54601
54646
|
featureContextMarkdown: ctx.featureContextMarkdown,
|
|
54602
|
-
constitution: ctx.constitution?.content
|
|
54647
|
+
constitution: ctx.constitution?.content,
|
|
54648
|
+
lite: isLite
|
|
54603
54649
|
} : undefined;
|
|
54604
54650
|
const greenfieldGateInput = _isTdd && _isFreshRun && resolvedTestPatterns ? { story, workdir: ctx.workdir, resolvedTestPatterns } : undefined;
|
|
54605
54651
|
const implementerInput = {
|
|
@@ -54800,6 +54846,13 @@ function deriveTddFailureCategory(phaseOutputs) {
|
|
|
54800
54846
|
}
|
|
54801
54847
|
return "tests-failing";
|
|
54802
54848
|
}
|
|
54849
|
+
const verifierPassed = verifierOutput?.success === true;
|
|
54850
|
+
if (!verifierPassed) {
|
|
54851
|
+
const gateOutput = phaseOutputs[fullSuiteGateOp.name];
|
|
54852
|
+
if (gateOutput && (gateOutput.success === false || gateOutput.passed === false)) {
|
|
54853
|
+
return "tests-failing";
|
|
54854
|
+
}
|
|
54855
|
+
}
|
|
54803
54856
|
const implOutput = phaseOutputs[implementerOp.name];
|
|
54804
54857
|
if (implOutput?.success === false) {
|
|
54805
54858
|
return "session-failure";
|
|
@@ -54898,6 +54951,24 @@ async function applyPostRunInspection(ctx, planResult, opts) {
|
|
|
54898
54951
|
}
|
|
54899
54952
|
const pauseReason = extractPauseReason(planResult.phaseOutputs);
|
|
54900
54953
|
const failureCategory = isTdd && !planResult.success ? deriveTddFailureCategory(planResult.phaseOutputs) : undefined;
|
|
54954
|
+
if (isTdd && !planResult.success && !failureCategory) {
|
|
54955
|
+
const phaseSignals = {};
|
|
54956
|
+
for (const [name, output] of Object.entries(planResult.phaseOutputs)) {
|
|
54957
|
+
if (output && typeof output === "object") {
|
|
54958
|
+
const r = output;
|
|
54959
|
+
const signal = {};
|
|
54960
|
+
if (typeof r.success === "boolean")
|
|
54961
|
+
signal.success = r.success;
|
|
54962
|
+
if (typeof r.passed === "boolean")
|
|
54963
|
+
signal.passed = r.passed;
|
|
54964
|
+
phaseSignals[name] = signal;
|
|
54965
|
+
}
|
|
54966
|
+
}
|
|
54967
|
+
logger.warn("execution", "TDD plan failed but no failure category derived \u2014 defaulting to pause", {
|
|
54968
|
+
storyId: ctx.story.id,
|
|
54969
|
+
phaseSignals
|
|
54970
|
+
});
|
|
54971
|
+
}
|
|
54901
54972
|
const tddIsolations = {};
|
|
54902
54973
|
for (const opName of ["test-writer", "implementer", "verifier"]) {
|
|
54903
54974
|
const phaseOut = planResult.phaseOutputs[opName];
|
|
@@ -59182,7 +59253,7 @@ var package_default;
|
|
|
59182
59253
|
var init_package = __esm(() => {
|
|
59183
59254
|
package_default = {
|
|
59184
59255
|
name: "@nathapp/nax",
|
|
59185
|
-
version: "0.67.
|
|
59256
|
+
version: "0.67.3",
|
|
59186
59257
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
59187
59258
|
type: "module",
|
|
59188
59259
|
bin: {
|
|
@@ -59277,8 +59348,8 @@ var init_version = __esm(() => {
|
|
|
59277
59348
|
NAX_VERSION = package_default.version;
|
|
59278
59349
|
NAX_COMMIT = (() => {
|
|
59279
59350
|
try {
|
|
59280
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
59281
|
-
return "
|
|
59351
|
+
if (/^[0-9a-f]{6,10}$/.test("bcfe96ad"))
|
|
59352
|
+
return "bcfe96ad";
|
|
59282
59353
|
} catch {}
|
|
59283
59354
|
try {
|
|
59284
59355
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|