@nathapp/nax 0.64.2-canary.2 → 0.64.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.
- package/dist/nax.js +155 -35
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -29568,7 +29568,43 @@ Fix in priority order. After fixing each priority, re-run the failing check(s) a
|
|
|
29568
29568
|
return parts.join(`
|
|
29569
29569
|
`);
|
|
29570
29570
|
}
|
|
29571
|
-
static testWriterRectification(
|
|
29571
|
+
static testWriterRectification(findings, story, options) {
|
|
29572
|
+
if (options?.mode === "write-failing-test") {
|
|
29573
|
+
return RectifierPromptBuilder._testWriterWriteFailingTest(findings, story);
|
|
29574
|
+
}
|
|
29575
|
+
return RectifierPromptBuilder._testWriterFixTestFiles(findings, story);
|
|
29576
|
+
}
|
|
29577
|
+
static _testWriterWriteFailingTest(findings, story) {
|
|
29578
|
+
const acList = story.acceptanceCriteria.map((ac, i) => `${i + 1}. ${ac}`).join(`
|
|
29579
|
+
`);
|
|
29580
|
+
const findingLines = findings.flatMap((c) => (c.findings ?? []).map((f) => `- [${f.severity}] ${f.file ?? "unknown"}:${f.line ?? "?"} \u2014 ${f.message}`)).join(`
|
|
29581
|
+
`);
|
|
29582
|
+
const scopeConstraint = story.workdir ? `
|
|
29583
|
+
|
|
29584
|
+
IMPORTANT: Only create or modify test files within \`${story.workdir}/\`. Do NOT touch source files.` : `
|
|
29585
|
+
|
|
29586
|
+
IMPORTANT: Only create or modify test files. Do NOT touch source implementation files.`;
|
|
29587
|
+
return `You are writing a failing test that documents spec-correct behavior.
|
|
29588
|
+
|
|
29589
|
+
Story: ${story.title} (${story.id})
|
|
29590
|
+
|
|
29591
|
+
### Acceptance Criteria
|
|
29592
|
+
${acList}
|
|
29593
|
+
|
|
29594
|
+
### Source Bugs Found by Adversarial Review
|
|
29595
|
+
${findingLines}
|
|
29596
|
+
|
|
29597
|
+
**Task:** For each bug above, write a NEW failing test that asserts the spec-correct behavior described in the finding. The test should FAIL with the current (buggy) implementation and PASS once the implementer fixes the source.
|
|
29598
|
+
|
|
29599
|
+
Rules:
|
|
29600
|
+
1. Write the test against the SPECIFICATION, not the current behavior.
|
|
29601
|
+
2. Do NOT fix the source files \u2014 only write test files.
|
|
29602
|
+
3. Do NOT modify existing passing tests.
|
|
29603
|
+
4. The test must fail with the current code.
|
|
29604
|
+
|
|
29605
|
+
Commit your new tests when done.${scopeConstraint}`;
|
|
29606
|
+
}
|
|
29607
|
+
static _testWriterFixTestFiles(testFileFindings, story) {
|
|
29572
29608
|
const scopeConstraint = story.workdir ? `
|
|
29573
29609
|
|
|
29574
29610
|
IMPORTANT: Only modify test files within \`${story.workdir}/\`. Do NOT touch source files.` : `
|
|
@@ -29591,10 +29627,14 @@ IMPORTANT: Only modify test files. Do NOT touch source implementation files.`;
|
|
|
29591
29627
|
`);
|
|
29592
29628
|
const acList = story.acceptanceCriteria.map((ac, i) => `${i + 1}. ${ac}`).join(`
|
|
29593
29629
|
`);
|
|
29594
|
-
const importantNote = isLintOnly ? "**Important:** Fix the lint errors in the test files listed above. Do NOT modify source implementation files." : `**Important:**
|
|
29595
|
-
|
|
29596
|
-
|
|
29597
|
-
|
|
29630
|
+
const importantNote = isLintOnly ? "**Important:** Fix the lint errors in the test files listed above. Do NOT modify source implementation files." : `**Important:** You are encoding the SPECIFICATION, not the current behavior.
|
|
29631
|
+
|
|
29632
|
+
Before making any changes:
|
|
29633
|
+
1. Read the flagged test files to verify each finding is a real issue.
|
|
29634
|
+
2. Do NOT loosen assertions to match current implementation behavior. If a test is failing because the source is wrong, the source is the suspect \u2014 not the test.
|
|
29635
|
+
3. Do NOT delete a failing test because the implementation makes it hard to assert on. Refactor the test structure if needed; never silently drop coverage.
|
|
29636
|
+
4. If the current behavior disagrees with the acceptance criteria, write the test against the spec and let the implementer fix the source.
|
|
29637
|
+
5. Do NOT modify source implementation files.`;
|
|
29598
29638
|
return `${opener}
|
|
29599
29639
|
|
|
29600
29640
|
Story: ${story.title} (${story.id})
|
|
@@ -32448,7 +32488,9 @@ var init_autofix_test_writer = __esm(() => {
|
|
|
32448
32488
|
session: { role: "test-writer", lifetime: "fresh" },
|
|
32449
32489
|
config: autofixConfigSelector,
|
|
32450
32490
|
build(input, _ctx) {
|
|
32451
|
-
const prompt = RectifierPromptBuilder.testWriterRectification(input.failedChecks, input.story
|
|
32491
|
+
const prompt = RectifierPromptBuilder.testWriterRectification(input.failedChecks, input.story, {
|
|
32492
|
+
mode: input.mode
|
|
32493
|
+
});
|
|
32452
32494
|
return {
|
|
32453
32495
|
role: { id: "role", content: "", overridable: false },
|
|
32454
32496
|
task: { id: "task", content: prompt, overridable: false }
|
|
@@ -40082,9 +40124,66 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
|
|
|
40082
40124
|
op: strategy.fixOp.name,
|
|
40083
40125
|
targetFiles: extracted.targetFiles ?? [],
|
|
40084
40126
|
summary: extracted.summary ?? "",
|
|
40127
|
+
...extracted.unresolved ? { unresolved: extracted.unresolved } : {},
|
|
40085
40128
|
costUsd: extracted.costUsd
|
|
40086
40129
|
});
|
|
40087
40130
|
}
|
|
40131
|
+
const unresolvedFa = fixesApplied.find((fa) => fa.unresolved);
|
|
40132
|
+
if (unresolvedFa) {
|
|
40133
|
+
const finishedAt2 = now();
|
|
40134
|
+
cycle.iterations.push({
|
|
40135
|
+
iterationNum: cycle.iterations.length + 1,
|
|
40136
|
+
findingsBefore,
|
|
40137
|
+
fixesApplied,
|
|
40138
|
+
findingsAfter: cycle.findings,
|
|
40139
|
+
outcome: "unchanged",
|
|
40140
|
+
startedAt,
|
|
40141
|
+
finishedAt: finishedAt2
|
|
40142
|
+
});
|
|
40143
|
+
logger?.info("findings.cycle", "cycle exited \u2014 agent gave up", {
|
|
40144
|
+
storyId,
|
|
40145
|
+
packageDir,
|
|
40146
|
+
cycleName,
|
|
40147
|
+
reason: "agent-gave-up",
|
|
40148
|
+
strategyName: unresolvedFa.strategyName,
|
|
40149
|
+
unresolvedDetail: unresolvedFa.unresolved
|
|
40150
|
+
});
|
|
40151
|
+
return {
|
|
40152
|
+
iterations: cycle.iterations,
|
|
40153
|
+
finalFindings: cycle.findings,
|
|
40154
|
+
exitReason: "agent-gave-up",
|
|
40155
|
+
unresolvedDetail: unresolvedFa.unresolved,
|
|
40156
|
+
costUsd: totalCostUsd
|
|
40157
|
+
};
|
|
40158
|
+
}
|
|
40159
|
+
const provisionalIterations = [...cycle.iterations, { fixesApplied }];
|
|
40160
|
+
const allExhausted = group.every((s) => countStrategyAttempts(provisionalIterations, s.name) >= s.maxAttempts);
|
|
40161
|
+
if (allExhausted) {
|
|
40162
|
+
const finishedAt2 = now();
|
|
40163
|
+
cycle.iterations.push({
|
|
40164
|
+
iterationNum: cycle.iterations.length + 1,
|
|
40165
|
+
findingsBefore,
|
|
40166
|
+
fixesApplied,
|
|
40167
|
+
findingsAfter: cycle.findings,
|
|
40168
|
+
outcome: "unchanged",
|
|
40169
|
+
startedAt,
|
|
40170
|
+
finishedAt: finishedAt2
|
|
40171
|
+
});
|
|
40172
|
+
logger?.info("findings.cycle", "cycle exited \u2014 strategy attempt cap reached (skipped final validate)", {
|
|
40173
|
+
storyId,
|
|
40174
|
+
packageDir,
|
|
40175
|
+
cycleName,
|
|
40176
|
+
reason: "max-attempts-per-strategy",
|
|
40177
|
+
exhaustedStrategy: group[0]?.name
|
|
40178
|
+
});
|
|
40179
|
+
return {
|
|
40180
|
+
iterations: cycle.iterations,
|
|
40181
|
+
finalFindings: cycle.findings,
|
|
40182
|
+
exitReason: "max-attempts-per-strategy",
|
|
40183
|
+
exhaustedStrategy: group[0]?.name,
|
|
40184
|
+
costUsd: totalCostUsd
|
|
40185
|
+
};
|
|
40186
|
+
}
|
|
40088
40187
|
let findingsAfter;
|
|
40089
40188
|
let validatorAttempt = 0;
|
|
40090
40189
|
for (;; ) {
|
|
@@ -41647,7 +41746,13 @@ function collectCurrentFindings(ctx) {
|
|
|
41647
41746
|
});
|
|
41648
41747
|
}
|
|
41649
41748
|
function collectTestTargetedChecks(ctx) {
|
|
41650
|
-
return collectFailedChecks(ctx).
|
|
41749
|
+
return collectFailedChecks(ctx).map((c) => ({ ...c, findings: (c.findings ?? []).filter((f) => f.fixTarget === "test") })).filter((c) => c.findings.length > 0);
|
|
41750
|
+
}
|
|
41751
|
+
function collectAdversarialSourceChecks(ctx) {
|
|
41752
|
+
return collectFailedChecks(ctx).map((c) => ({
|
|
41753
|
+
...c,
|
|
41754
|
+
findings: (c.findings ?? []).filter((f) => (f.fixTarget ?? "source") === "source" && f.severity === "error" && f.source === "adversarial-review")
|
|
41755
|
+
})).filter((c) => c.check === "adversarial" && c.findings.length > 0);
|
|
41651
41756
|
}
|
|
41652
41757
|
function buildAutofixStrategies(ctx, maxAttempts) {
|
|
41653
41758
|
const implementer = {
|
|
@@ -41661,31 +41766,41 @@ function buildAutofixStrategies(ctx, maxAttempts) {
|
|
|
41661
41766
|
story: ctx.story
|
|
41662
41767
|
}),
|
|
41663
41768
|
extractApplied: (output) => ({
|
|
41664
|
-
summary: output.unresolvedReason ?? ""
|
|
41769
|
+
summary: output.unresolvedReason ?? "",
|
|
41770
|
+
unresolved: output.unresolvedReason
|
|
41665
41771
|
})
|
|
41666
41772
|
};
|
|
41667
41773
|
const testWriter = {
|
|
41668
41774
|
name: "autofix-test-writer",
|
|
41669
|
-
appliesTo: (f) => f.fixTarget === "test",
|
|
41775
|
+
appliesTo: (f) => f.fixTarget === "test" || (f.fixTarget ?? "source") === "source" && f.severity === "error" && f.source === "adversarial-review",
|
|
41670
41776
|
fixOp: testWriterRectifyOp,
|
|
41671
41777
|
maxAttempts: 1,
|
|
41672
41778
|
coRun: "co-run-sequential",
|
|
41673
|
-
buildInput: (
|
|
41674
|
-
|
|
41675
|
-
|
|
41676
|
-
|
|
41677
|
-
};
|
|
41678
|
-
return [implementer, testWriter];
|
|
41679
|
-
}
|
|
41680
|
-
function findUnresolvedReason(result) {
|
|
41681
|
-
for (const iter of result.iterations) {
|
|
41682
|
-
for (const fa of iter.fixesApplied) {
|
|
41683
|
-
if (fa.strategyName === "autofix-implementer" && fa.summary) {
|
|
41684
|
-
return fa.summary;
|
|
41779
|
+
buildInput: (findings, _prior, _cycleCtx) => {
|
|
41780
|
+
const hasSourceBug = findings.some((f) => (f.fixTarget ?? "source") === "source" && f.source === "adversarial-review");
|
|
41781
|
+
if (hasSourceBug) {
|
|
41782
|
+
return { failedChecks: collectAdversarialSourceChecks(ctx), story: ctx.story, mode: "write-failing-test" };
|
|
41685
41783
|
}
|
|
41784
|
+
return { failedChecks: collectTestTargetedChecks(ctx), story: ctx.story };
|
|
41686
41785
|
}
|
|
41786
|
+
};
|
|
41787
|
+
return [testWriter, implementer];
|
|
41788
|
+
}
|
|
41789
|
+
function buildEscalationDigest(findings) {
|
|
41790
|
+
const byFile = new Map;
|
|
41791
|
+
for (const f of findings) {
|
|
41792
|
+
const file3 = f.file ?? "unknown";
|
|
41793
|
+
const list = byFile.get(file3) ?? [];
|
|
41794
|
+
list.push(f);
|
|
41795
|
+
byFile.set(file3, list);
|
|
41687
41796
|
}
|
|
41688
|
-
|
|
41797
|
+
const lines = [...byFile.entries()].map(([file3, fs]) => {
|
|
41798
|
+
const categories = fs.map((f) => f.category ?? f.source).join(", ");
|
|
41799
|
+
return ` - ${categories} in ${file3}`;
|
|
41800
|
+
});
|
|
41801
|
+
return `Autofix exhausted: ${findings.length} finding${findings.length !== 1 ? "s" : ""} remain
|
|
41802
|
+
${lines.join(`
|
|
41803
|
+
`)}`;
|
|
41689
41804
|
}
|
|
41690
41805
|
async function writeShadowReport(ctx, result, initialFindingsCount) {
|
|
41691
41806
|
const logger = getLogger();
|
|
@@ -41739,7 +41854,8 @@ async function runAgentRectificationV2(ctx, _lintFixCmd, _formatFixCmd, _effecti
|
|
|
41739
41854
|
const result = await runFixCycle(cycle, cycleCtx, "autofix-v2");
|
|
41740
41855
|
ctx.autofixPriorIterations = result.iterations;
|
|
41741
41856
|
await writeShadowReport(ctx, result, initialFindings.length);
|
|
41742
|
-
const unresolvedReason =
|
|
41857
|
+
const unresolvedReason = result.exitReason === "agent-gave-up" ? result.unresolvedDetail : undefined;
|
|
41858
|
+
const escalationDigest = result.exitReason === "max-attempts-per-strategy" && result.finalFindings.length > 0 ? buildEscalationDigest(result.finalFindings) : undefined;
|
|
41743
41859
|
const succeeded = result.exitReason === "resolved" || result.finalFindings.length === 0;
|
|
41744
41860
|
logger.info("autofix-cycle", "V2 fix cycle complete", {
|
|
41745
41861
|
storyId,
|
|
@@ -41747,9 +41863,15 @@ async function runAgentRectificationV2(ctx, _lintFixCmd, _formatFixCmd, _effecti
|
|
|
41747
41863
|
iterations: result.iterations.length,
|
|
41748
41864
|
finalFindingsCount: result.finalFindings.length,
|
|
41749
41865
|
succeeded,
|
|
41750
|
-
...unresolvedReason ? { unresolvedReason } : {}
|
|
41866
|
+
...unresolvedReason ? { unresolvedReason } : {},
|
|
41867
|
+
...escalationDigest ? { escalationDigest } : {}
|
|
41751
41868
|
});
|
|
41752
|
-
return {
|
|
41869
|
+
return {
|
|
41870
|
+
succeeded,
|
|
41871
|
+
cost: 0,
|
|
41872
|
+
...unresolvedReason ? { unresolvedReason } : {},
|
|
41873
|
+
...escalationDigest ? { escalationDigest } : {}
|
|
41874
|
+
};
|
|
41753
41875
|
}
|
|
41754
41876
|
var init_autofix_cycle = __esm(() => {
|
|
41755
41877
|
init_findings();
|
|
@@ -43143,10 +43265,7 @@ async function runAdversarialReview(opts) {
|
|
|
43143
43265
|
const durationMs2 = Date.now() - startTime;
|
|
43144
43266
|
logger?.warn("review", `Adversarial review failed: ${blockingFindings.length} blocking findings`, {
|
|
43145
43267
|
storyId: story.id,
|
|
43146
|
-
durationMs: durationMs2
|
|
43147
|
-
});
|
|
43148
|
-
logger?.debug("review", "Adversarial review findings", {
|
|
43149
|
-
storyId: story.id,
|
|
43268
|
+
durationMs: durationMs2,
|
|
43150
43269
|
findings: blockingFindings.map((f) => ({
|
|
43151
43270
|
severity: f.severity,
|
|
43152
43271
|
category: f.category,
|
|
@@ -45117,9 +45236,10 @@ var init_autofix = __esm(() => {
|
|
|
45117
45236
|
const {
|
|
45118
45237
|
succeeded: agentFixed,
|
|
45119
45238
|
cost: agentCost,
|
|
45120
|
-
unresolvedReason
|
|
45239
|
+
unresolvedReason,
|
|
45240
|
+
escalationDigest
|
|
45121
45241
|
} = await _autofixDeps.runAgentRectification(ctx, lintFixCmd, formatFixCmd, ctx.workdir);
|
|
45122
|
-
if (unresolvedReason) {
|
|
45242
|
+
if (!agentFixed && unresolvedReason) {
|
|
45123
45243
|
if (ctx.mechanicalFailedOnly) {
|
|
45124
45244
|
logger.warn("autofix", "Mechanical-only failure unfixable \u2014 proceeding (LLM review passed)", {
|
|
45125
45245
|
storyId: ctx.story.id,
|
|
@@ -45166,7 +45286,7 @@ var init_autofix = __esm(() => {
|
|
|
45166
45286
|
logger.warn("autofix", "Autofix exhausted \u2014 escalating", { storyId: ctx.story.id });
|
|
45167
45287
|
return {
|
|
45168
45288
|
action: "escalate",
|
|
45169
|
-
reason: "Autofix exhausted: review still failing after fix attempts",
|
|
45289
|
+
reason: escalationDigest ?? "Autofix exhausted: review still failing after fix attempts",
|
|
45170
45290
|
cost: agentCost
|
|
45171
45291
|
};
|
|
45172
45292
|
}
|
|
@@ -50154,7 +50274,7 @@ var package_default;
|
|
|
50154
50274
|
var init_package = __esm(() => {
|
|
50155
50275
|
package_default = {
|
|
50156
50276
|
name: "@nathapp/nax",
|
|
50157
|
-
version: "0.64.2
|
|
50277
|
+
version: "0.64.2",
|
|
50158
50278
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
50159
50279
|
type: "module",
|
|
50160
50280
|
bin: {
|
|
@@ -50238,8 +50358,8 @@ var init_version = __esm(() => {
|
|
|
50238
50358
|
NAX_VERSION = package_default.version;
|
|
50239
50359
|
NAX_COMMIT = (() => {
|
|
50240
50360
|
try {
|
|
50241
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
50242
|
-
return "
|
|
50361
|
+
if (/^[0-9a-f]{6,10}$/.test("7a4c7325"))
|
|
50362
|
+
return "7a4c7325";
|
|
50243
50363
|
} catch {}
|
|
50244
50364
|
try {
|
|
50245
50365
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|