@nathapp/nax 0.67.12 → 0.67.13
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 +313 -60
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -32839,6 +32839,122 @@ var init_categorization = __esm(() => {
|
|
|
32839
32839
|
MECHANICAL_REVIEW_CHECKS = new Set(ORDERED_MECHANICAL_REVIEW_CHECKS);
|
|
32840
32840
|
LLM_REVIEW_CHECKS = new Set(ORDERED_LLM_REVIEW_CHECKS);
|
|
32841
32841
|
});
|
|
32842
|
+
|
|
32843
|
+
// src/review/prepare-inputs.ts
|
|
32844
|
+
import { relative as relative8, sep as sep2 } from "path";
|
|
32845
|
+
function derivePackageDirs(workdir, projectDir) {
|
|
32846
|
+
const repoRoot = projectDir ?? workdir;
|
|
32847
|
+
const packageDir = workdir !== repoRoot ? workdir : undefined;
|
|
32848
|
+
let packageDirRelative;
|
|
32849
|
+
if (projectDir && workdir !== projectDir) {
|
|
32850
|
+
const rel = relative8(projectDir, workdir);
|
|
32851
|
+
if (rel !== ".." && !rel.startsWith(`..${sep2}`)) {
|
|
32852
|
+
packageDirRelative = rel && rel !== "." ? rel : undefined;
|
|
32853
|
+
}
|
|
32854
|
+
}
|
|
32855
|
+
return { repoRoot, packageDir, packageDirRelative };
|
|
32856
|
+
}
|
|
32857
|
+
async function prepareSemanticReviewInput(args) {
|
|
32858
|
+
const { workdir, projectDir, storyId, storyGitRef, config: config2, naxIgnoreIndex, semanticConfig } = args;
|
|
32859
|
+
const effectiveRef = await resolveEffectiveRef(workdir, storyGitRef, storyId);
|
|
32860
|
+
if (!effectiveRef) {
|
|
32861
|
+
return {
|
|
32862
|
+
effectiveRef: undefined,
|
|
32863
|
+
stat: "",
|
|
32864
|
+
diff: undefined,
|
|
32865
|
+
excludePatterns: [],
|
|
32866
|
+
skipReason: "no git ref"
|
|
32867
|
+
};
|
|
32868
|
+
}
|
|
32869
|
+
const { packageDir, packageDirRelative } = derivePackageDirs(workdir, projectDir);
|
|
32870
|
+
const stat = await collectDiffStat(workdir, effectiveRef, { naxIgnoreIndex, packageDir });
|
|
32871
|
+
const resolved = await resolveTestFilePatterns(config2 ?? reviewConfigSelector.select(DEFAULT_CONFIG), projectDir ?? workdir, packageDirRelative);
|
|
32872
|
+
const excludePatterns = [...resolveReviewExcludePatterns(semanticConfig.excludePatterns, resolved)];
|
|
32873
|
+
const diffMode = semanticConfig.diffMode ?? "ref";
|
|
32874
|
+
if (diffMode === "ref") {
|
|
32875
|
+
if (!stat) {
|
|
32876
|
+
return { effectiveRef, stat: "", diff: undefined, excludePatterns, skipReason: "no changes detected" };
|
|
32877
|
+
}
|
|
32878
|
+
return { effectiveRef, stat, diff: undefined, excludePatterns };
|
|
32879
|
+
}
|
|
32880
|
+
const rawDiff = await collectDiff(workdir, effectiveRef, excludePatterns, { naxIgnoreIndex, packageDir });
|
|
32881
|
+
const diff = truncateDiff(rawDiff, rawDiff.length > DIFF_CAP_BYTES ? stat : undefined);
|
|
32882
|
+
if (!diff) {
|
|
32883
|
+
return { effectiveRef, stat, diff: undefined, excludePatterns, skipReason: "no production code changes" };
|
|
32884
|
+
}
|
|
32885
|
+
return { effectiveRef, stat, diff, excludePatterns };
|
|
32886
|
+
}
|
|
32887
|
+
async function prepareAdversarialReviewInput(args) {
|
|
32888
|
+
const { workdir, projectDir, storyId, storyGitRef, config: config2, naxIgnoreIndex, adversarialConfig } = args;
|
|
32889
|
+
const effectiveRef = await resolveEffectiveRef(workdir, storyGitRef, storyId);
|
|
32890
|
+
if (!effectiveRef) {
|
|
32891
|
+
return {
|
|
32892
|
+
effectiveRef: undefined,
|
|
32893
|
+
stat: "",
|
|
32894
|
+
diff: undefined,
|
|
32895
|
+
testInventory: undefined,
|
|
32896
|
+
excludePatterns: [],
|
|
32897
|
+
testGlobs: [],
|
|
32898
|
+
refExcludePatterns: [],
|
|
32899
|
+
skipReason: "no git ref"
|
|
32900
|
+
};
|
|
32901
|
+
}
|
|
32902
|
+
const { packageDir, packageDirRelative } = derivePackageDirs(workdir, projectDir);
|
|
32903
|
+
const stat = await collectDiffStat(workdir, effectiveRef, { naxIgnoreIndex, packageDir });
|
|
32904
|
+
const effectiveConfig = config2 ?? reviewConfigSelector.select(DEFAULT_CONFIG);
|
|
32905
|
+
const resolved = await resolveTestFilePatterns(effectiveConfig, projectDir ?? workdir, packageDirRelative);
|
|
32906
|
+
const refExcludePatterns = [...resolveReviewExcludePatterns(adversarialConfig.excludePatterns, resolved)];
|
|
32907
|
+
const testGlobs = resolved.globs ?? [];
|
|
32908
|
+
const excludePatterns = [...adversarialConfig.excludePatterns ?? []];
|
|
32909
|
+
const diffMode = adversarialConfig.diffMode ?? "ref";
|
|
32910
|
+
const testFilePatterns = (typeof config2?.execution?.smartTestRunner === "object" ? config2.execution.smartTestRunner?.testFilePatterns : undefined) ?? undefined;
|
|
32911
|
+
if (diffMode === "ref") {
|
|
32912
|
+
if (!stat) {
|
|
32913
|
+
return {
|
|
32914
|
+
effectiveRef,
|
|
32915
|
+
stat: "",
|
|
32916
|
+
diff: undefined,
|
|
32917
|
+
testInventory: undefined,
|
|
32918
|
+
excludePatterns,
|
|
32919
|
+
testGlobs,
|
|
32920
|
+
refExcludePatterns,
|
|
32921
|
+
skipReason: "no changes detected"
|
|
32922
|
+
};
|
|
32923
|
+
}
|
|
32924
|
+
return {
|
|
32925
|
+
effectiveRef,
|
|
32926
|
+
stat,
|
|
32927
|
+
diff: undefined,
|
|
32928
|
+
testInventory: undefined,
|
|
32929
|
+
excludePatterns,
|
|
32930
|
+
testGlobs,
|
|
32931
|
+
refExcludePatterns
|
|
32932
|
+
};
|
|
32933
|
+
}
|
|
32934
|
+
const diff = await collectDiff(workdir, effectiveRef, excludePatterns, { naxIgnoreIndex, packageDir });
|
|
32935
|
+
if (!diff) {
|
|
32936
|
+
return {
|
|
32937
|
+
effectiveRef,
|
|
32938
|
+
stat,
|
|
32939
|
+
diff: undefined,
|
|
32940
|
+
testInventory: undefined,
|
|
32941
|
+
excludePatterns,
|
|
32942
|
+
testGlobs,
|
|
32943
|
+
refExcludePatterns,
|
|
32944
|
+
skipReason: "no code changes"
|
|
32945
|
+
};
|
|
32946
|
+
}
|
|
32947
|
+
const testInventory = await computeTestInventory(workdir, effectiveRef, testFilePatterns, {
|
|
32948
|
+
naxIgnoreIndex,
|
|
32949
|
+
packageDir
|
|
32950
|
+
});
|
|
32951
|
+
return { effectiveRef, stat, diff, testInventory, excludePatterns, testGlobs, refExcludePatterns };
|
|
32952
|
+
}
|
|
32953
|
+
var init_prepare_inputs = __esm(() => {
|
|
32954
|
+
init_config();
|
|
32955
|
+
init_test_runners();
|
|
32956
|
+
init_diff_utils();
|
|
32957
|
+
});
|
|
32842
32958
|
// src/utils/process-kill.ts
|
|
32843
32959
|
function killProcessGroup(pid, signal) {
|
|
32844
32960
|
try {
|
|
@@ -33271,7 +33387,7 @@ var init_runners = __esm(() => {
|
|
|
33271
33387
|
});
|
|
33272
33388
|
|
|
33273
33389
|
// src/verification/smart-runner.ts
|
|
33274
|
-
import { join as join19, relative as
|
|
33390
|
+
import { join as join19, relative as relative9 } from "path";
|
|
33275
33391
|
function extractPatternSuffix(pattern) {
|
|
33276
33392
|
const lastStar = pattern.lastIndexOf("*");
|
|
33277
33393
|
if (lastStar === -1)
|
|
@@ -33381,7 +33497,7 @@ async function getChangedNonTestFiles(workdir, baseRef, packagePrefix, testFileR
|
|
|
33381
33497
|
let effectivePrefix = packagePrefix;
|
|
33382
33498
|
if (packagePrefix && repoRoot) {
|
|
33383
33499
|
const gitRoot = await _gitUtilDeps.getGitRoot(workdir);
|
|
33384
|
-
const extraPrefix2 = gitRoot && gitRoot !== repoRoot ?
|
|
33500
|
+
const extraPrefix2 = gitRoot && gitRoot !== repoRoot ? relative9(gitRoot, repoRoot) : "";
|
|
33385
33501
|
effectivePrefix = extraPrefix2 ? `${extraPrefix2}/${packagePrefix}` : packagePrefix;
|
|
33386
33502
|
}
|
|
33387
33503
|
const scopedRaw = effectivePrefix ? lines.filter((f) => f.startsWith(`${effectivePrefix}/`)) : lines;
|
|
@@ -33408,7 +33524,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
|
|
|
33408
33524
|
const packageDir = packagePrefix ? join19(repoRoot, packagePrefix) : undefined;
|
|
33409
33525
|
const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(repoRoot, packageDir);
|
|
33410
33526
|
const gitRoot = await _gitUtilDeps.getGitRoot(workdir);
|
|
33411
|
-
const extraPrefix = gitRoot && gitRoot !== repoRoot ?
|
|
33527
|
+
const extraPrefix = gitRoot && gitRoot !== repoRoot ? relative9(gitRoot, repoRoot) : "";
|
|
33412
33528
|
const effectivePrefix = packagePrefix ? extraPrefix ? `${extraPrefix}/${packagePrefix}` : packagePrefix : undefined;
|
|
33413
33529
|
const scopedRaw = effectivePrefix ? lines.filter((f) => f.startsWith(`${effectivePrefix}/`)) : lines;
|
|
33414
33530
|
const scoped2 = filterNaxInternalPaths(scopedRaw, ignoreMatchers);
|
|
@@ -33841,12 +33957,12 @@ function acceptanceDiagnoseRawArrayToFindings(raw) {
|
|
|
33841
33957
|
}
|
|
33842
33958
|
|
|
33843
33959
|
// src/findings/path-utils.ts
|
|
33844
|
-
import { relative as
|
|
33960
|
+
import { relative as relative10, resolve as resolve11 } from "path";
|
|
33845
33961
|
function rebaseToWorkdir(rawPath, cwd, workdir) {
|
|
33846
33962
|
if (rawPath.startsWith("/")) {
|
|
33847
|
-
return
|
|
33963
|
+
return relative10(workdir, rawPath);
|
|
33848
33964
|
}
|
|
33849
|
-
return
|
|
33965
|
+
return relative10(workdir, resolve11(cwd, rawPath));
|
|
33850
33966
|
}
|
|
33851
33967
|
var init_path_utils = () => {};
|
|
33852
33968
|
|
|
@@ -39211,7 +39327,7 @@ var init_lint_parsing = __esm(() => {
|
|
|
39211
39327
|
});
|
|
39212
39328
|
|
|
39213
39329
|
// src/review/scoped-lint.ts
|
|
39214
|
-
import { join as join24, relative as
|
|
39330
|
+
import { join as join24, relative as relative11 } from "path";
|
|
39215
39331
|
function shellQuotePath4(path5) {
|
|
39216
39332
|
return `'${path5.replaceAll("'", "'\\''")}'`;
|
|
39217
39333
|
}
|
|
@@ -39248,7 +39364,7 @@ async function listChangedFiles(workdir, baseRef) {
|
|
|
39248
39364
|
function inferActivePackageDir(workdir, projectDir) {
|
|
39249
39365
|
if (!projectDir)
|
|
39250
39366
|
return;
|
|
39251
|
-
const rel = normalizePath3(
|
|
39367
|
+
const rel = normalizePath3(relative11(projectDir, workdir));
|
|
39252
39368
|
if (!rel || rel === "." || rel.startsWith(".."))
|
|
39253
39369
|
return;
|
|
39254
39370
|
return rel;
|
|
@@ -39733,7 +39849,7 @@ var init_semantic_debate = __esm(() => {
|
|
|
39733
39849
|
});
|
|
39734
39850
|
|
|
39735
39851
|
// src/review/semantic.ts
|
|
39736
|
-
import { relative as
|
|
39852
|
+
import { relative as relative12, sep as sep3 } from "path";
|
|
39737
39853
|
function recordSemanticAudit(opts) {
|
|
39738
39854
|
opts.runtime?.dispatchEvents.emitReviewDecision({
|
|
39739
39855
|
kind: "review-decision",
|
|
@@ -39799,8 +39915,8 @@ async function runSemanticReview(opts) {
|
|
|
39799
39915
|
const packageDir = workdir !== repoRoot ? workdir : undefined;
|
|
39800
39916
|
const stat = await collectDiffStat(workdir, effectiveRef, { naxIgnoreIndex, packageDir });
|
|
39801
39917
|
const packageDirRelative = projectDir && workdir !== projectDir ? (() => {
|
|
39802
|
-
const rel =
|
|
39803
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
39918
|
+
const rel = relative12(projectDir, workdir);
|
|
39919
|
+
if (rel === ".." || rel.startsWith(`..${sep3}`))
|
|
39804
39920
|
return;
|
|
39805
39921
|
return rel && rel !== "." ? rel : undefined;
|
|
39806
39922
|
})() : undefined;
|
|
@@ -40518,6 +40634,7 @@ var init_review = __esm(() => {
|
|
|
40518
40634
|
init_semantic_evidence();
|
|
40519
40635
|
init_categorization();
|
|
40520
40636
|
init_diff_utils();
|
|
40637
|
+
init_prepare_inputs();
|
|
40521
40638
|
init_finding_projection();
|
|
40522
40639
|
init_runner2();
|
|
40523
40640
|
init_requote_response();
|
|
@@ -44078,9 +44195,9 @@ var init_pid_registry = __esm(() => {
|
|
|
44078
44195
|
// src/session/manager-deps.ts
|
|
44079
44196
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
44080
44197
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
44081
|
-
import { isAbsolute as isAbsolute9, join as join27, relative as
|
|
44198
|
+
import { isAbsolute as isAbsolute9, join as join27, relative as relative13, sep as sep4 } from "path";
|
|
44082
44199
|
function resolveProjectDirFromScratchDir(scratchDir) {
|
|
44083
|
-
const marker = `${
|
|
44200
|
+
const marker = `${sep4}.nax${sep4}features${sep4}`;
|
|
44084
44201
|
const markerIdx = scratchDir.lastIndexOf(marker);
|
|
44085
44202
|
if (markerIdx > 0)
|
|
44086
44203
|
return scratchDir.slice(0, markerIdx);
|
|
@@ -44090,7 +44207,7 @@ function resolveProjectDirFromScratchDir(scratchDir) {
|
|
|
44090
44207
|
return;
|
|
44091
44208
|
}
|
|
44092
44209
|
function toProjectRelativePath(projectDir, pathValue) {
|
|
44093
|
-
const relativePath = isAbsolute9(pathValue) ?
|
|
44210
|
+
const relativePath = isAbsolute9(pathValue) ? relative13(projectDir, pathValue) : pathValue;
|
|
44094
44211
|
return relativePath === "" ? "." : relativePath;
|
|
44095
44212
|
}
|
|
44096
44213
|
var _sessionManagerDeps;
|
|
@@ -45399,7 +45516,7 @@ var init_windsurf = __esm(() => {
|
|
|
45399
45516
|
|
|
45400
45517
|
// src/context/generator.ts
|
|
45401
45518
|
import { existsSync as existsSync9 } from "fs";
|
|
45402
|
-
import { join as join30, relative as
|
|
45519
|
+
import { join as join30, relative as relative14 } from "path";
|
|
45403
45520
|
async function loadContextContent(options, config2) {
|
|
45404
45521
|
if (!_generatorDeps.existsSync(options.contextPath)) {
|
|
45405
45522
|
throw new Error(`Context file not found: ${options.contextPath}`);
|
|
@@ -45527,7 +45644,7 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
45527
45644
|
}
|
|
45528
45645
|
async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
|
|
45529
45646
|
const resolvedRepoRoot = repoRoot ?? packageDir;
|
|
45530
|
-
const relativePkgPath =
|
|
45647
|
+
const relativePkgPath = relative14(resolvedRepoRoot, packageDir);
|
|
45531
45648
|
const contextPath = join30(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
|
|
45532
45649
|
if (!_generatorDeps.existsSync(contextPath)) {
|
|
45533
45650
|
return [
|
|
@@ -52683,26 +52800,34 @@ function phaseExplicitlyPassed(output) {
|
|
|
52683
52800
|
const r = output;
|
|
52684
52801
|
return r.success === true || r.passed === true;
|
|
52685
52802
|
}
|
|
52686
|
-
function phasePassed(opName, output) {
|
|
52803
|
+
function phasePassed(opName, output, storyId) {
|
|
52804
|
+
const strictVerdictPhase = STRICT_VERDICT_PHASE_NAMES.has(opName);
|
|
52687
52805
|
if (output === null || output === undefined) {
|
|
52688
|
-
getSafeLogger()?.warn("story-orchestrator", "Phase produced no output \u2014 treating as pass", {
|
|
52689
|
-
storyId
|
|
52806
|
+
getSafeLogger()?.warn("story-orchestrator", strictVerdictPhase ? "Strict phase produced no output \u2014 treating as fail" : "Phase produced no output \u2014 treating as pass", {
|
|
52807
|
+
storyId,
|
|
52690
52808
|
phase: opName
|
|
52691
52809
|
});
|
|
52692
|
-
return
|
|
52810
|
+
return !strictVerdictPhase;
|
|
52811
|
+
}
|
|
52812
|
+
if (typeof output !== "object") {
|
|
52813
|
+
if (!strictVerdictPhase)
|
|
52814
|
+
return true;
|
|
52815
|
+
getSafeLogger()?.warn("story-orchestrator", "Strict phase produced non-object output \u2014 treating as fail", {
|
|
52816
|
+
storyId,
|
|
52817
|
+
phase: opName
|
|
52818
|
+
});
|
|
52819
|
+
return false;
|
|
52693
52820
|
}
|
|
52694
|
-
if (typeof output !== "object")
|
|
52695
|
-
return true;
|
|
52696
52821
|
const r = output;
|
|
52697
52822
|
if ("success" in r)
|
|
52698
52823
|
return r.success !== false;
|
|
52699
52824
|
if ("passed" in r)
|
|
52700
52825
|
return r.passed !== false;
|
|
52701
|
-
getSafeLogger()?.warn("story-orchestrator", "Phase output has neither 'success' nor 'passed' \u2014 treating as pass", {
|
|
52702
|
-
storyId
|
|
52826
|
+
getSafeLogger()?.warn("story-orchestrator", strictVerdictPhase ? "Strict phase output has neither 'success' nor 'passed' \u2014 treating as fail" : "Phase output has neither 'success' nor 'passed' \u2014 treating as pass", {
|
|
52827
|
+
storyId,
|
|
52703
52828
|
phase: opName
|
|
52704
52829
|
});
|
|
52705
|
-
return
|
|
52830
|
+
return !strictVerdictPhase;
|
|
52706
52831
|
}
|
|
52707
52832
|
function isFinding(value) {
|
|
52708
52833
|
return typeof value === "object" && value !== null && typeof value.source === "string" && value.source.length > 0;
|
|
@@ -52825,6 +52950,29 @@ function logUnifiedReviewPhaseStart(storyId, opName) {
|
|
|
52825
52950
|
logger?.info("review", "Running adversarial check", { storyId });
|
|
52826
52951
|
}
|
|
52827
52952
|
}
|
|
52953
|
+
function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase) {
|
|
52954
|
+
if (isTddPhase)
|
|
52955
|
+
return;
|
|
52956
|
+
if (opName === "semantic-review" || opName === "adversarial-review")
|
|
52957
|
+
return;
|
|
52958
|
+
if (output === null || output === undefined || typeof output !== "object")
|
|
52959
|
+
return;
|
|
52960
|
+
const logger = getSafeLogger();
|
|
52961
|
+
const r = output;
|
|
52962
|
+
const success2 = r.success === true || r.passed === true;
|
|
52963
|
+
const findingsCount = Array.isArray(r.findings) ? r.findings.length : undefined;
|
|
52964
|
+
const status = typeof r.status === "string" ? r.status : undefined;
|
|
52965
|
+
const data = { storyId, phase: opName, durationMs };
|
|
52966
|
+
if (findingsCount !== undefined)
|
|
52967
|
+
data.findingsCount = findingsCount;
|
|
52968
|
+
if (status !== undefined)
|
|
52969
|
+
data.status = status;
|
|
52970
|
+
if (success2) {
|
|
52971
|
+
logger?.info("story-orchestrator", `Phase passed: ${opName}`, data);
|
|
52972
|
+
} else {
|
|
52973
|
+
logger?.warn("story-orchestrator", `Phase failed: ${opName}`, data);
|
|
52974
|
+
}
|
|
52975
|
+
}
|
|
52828
52976
|
function logUnifiedReviewPhaseResult(storyId, opName, output) {
|
|
52829
52977
|
const logger = getSafeLogger();
|
|
52830
52978
|
const payload = toReviewDecisionPayload(opName, output);
|
|
@@ -52894,6 +53042,7 @@ async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = fa
|
|
|
52894
53042
|
phaseOutputs[opName] = output;
|
|
52895
53043
|
emitReviewDecision(ctx, opName, output);
|
|
52896
53044
|
logUnifiedReviewPhaseResult(ctx.storyId, opName, output);
|
|
53045
|
+
logDeterministicPhaseOutcome(ctx.storyId, opName, output, Date.now() - phaseStartedAt, isTddPhase);
|
|
52897
53046
|
if (isTddPhase) {
|
|
52898
53047
|
const durationMs = Date.now() - phaseStartedAt;
|
|
52899
53048
|
logger?.info("tdd", `Session complete: ${opName}`, {
|
|
@@ -52997,8 +53146,22 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
|
|
|
52997
53146
|
};
|
|
52998
53147
|
const cycleResult = await _storyOrchestratorDeps.runFixCycle(cycle, ctx, "story-orchestrator-rectification", { callOp: wrappedCallOp });
|
|
52999
53148
|
phaseOutputs.rectification = { iterationCount: cycleResult.iterations.length };
|
|
53149
|
+
const rectLogger = getSafeLogger();
|
|
53150
|
+
const rectSummary = {
|
|
53151
|
+
storyId: ctx.storyId,
|
|
53152
|
+
initialFindingsCount: initialFindings.length,
|
|
53153
|
+
iterationCount: cycleResult.iterations.length,
|
|
53154
|
+
finalFindingsCount: cycleResult.finalFindings.length,
|
|
53155
|
+
exitReason: cycleResult.exitReason,
|
|
53156
|
+
costUsd: cycleResult.costUsd
|
|
53157
|
+
};
|
|
53158
|
+
if (cycleResult.exitReason === "resolved") {
|
|
53159
|
+
rectLogger?.info("story-orchestrator", "Rectification resolved all findings", rectSummary);
|
|
53160
|
+
} else {
|
|
53161
|
+
rectLogger?.warn("story-orchestrator", `Rectification exited: ${cycleResult.exitReason}`, rectSummary);
|
|
53162
|
+
}
|
|
53000
53163
|
if (cycleResult.exitReason === "validator-error") {
|
|
53001
|
-
|
|
53164
|
+
rectLogger?.warn("story-orchestrator", "rectification cycle aborted \u2014 validator infrastructure error", {
|
|
53002
53165
|
storyId: ctx.storyId
|
|
53003
53166
|
});
|
|
53004
53167
|
}
|
|
@@ -53054,8 +53217,12 @@ class ExecutionPlan {
|
|
|
53054
53217
|
});
|
|
53055
53218
|
throw error48;
|
|
53056
53219
|
}
|
|
53057
|
-
if (!phasePassed(phase.slot.op.name, phaseOutputs[phase.slot.op.name])) {
|
|
53220
|
+
if (!phasePassed(phase.slot.op.name, phaseOutputs[phase.slot.op.name], this.ctx.storyId)) {
|
|
53058
53221
|
if (!shortCircuitExempt.has(phase.slot.op.name)) {
|
|
53222
|
+
logger?.warn("story-orchestrator", "Short-circuiting on phase failure", {
|
|
53223
|
+
storyId: this.ctx.storyId,
|
|
53224
|
+
phase: phase.slot.op.name
|
|
53225
|
+
});
|
|
53059
53226
|
break;
|
|
53060
53227
|
}
|
|
53061
53228
|
}
|
|
@@ -53064,20 +53231,43 @@ class ExecutionPlan {
|
|
|
53064
53231
|
const verifierName = this.state.verifier?.slot.op.name;
|
|
53065
53232
|
const gateName = this.state.fullSuiteGate?.slot.op.name;
|
|
53066
53233
|
const verifierPassedSsot = verifierName !== undefined && phaseExplicitlyPassed(phaseOutputs[verifierName]);
|
|
53067
|
-
if (verifierPassedSsot && gateName !== undefined && !phasePassed(gateName, phaseOutputs[gateName])) {
|
|
53234
|
+
if (verifierPassedSsot && gateName !== undefined && !phasePassed(gateName, phaseOutputs[gateName], this.ctx.storyId)) {
|
|
53068
53235
|
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 });
|
|
53069
53236
|
}
|
|
53070
53237
|
const success2 = Object.entries(phaseOutputs).every(([name, output]) => {
|
|
53071
53238
|
if (verifierPassedSsot && name === gateName)
|
|
53072
53239
|
return true;
|
|
53073
|
-
return phasePassed(name, output);
|
|
53240
|
+
return phasePassed(name, output, this.ctx.storyId);
|
|
53074
53241
|
});
|
|
53075
53242
|
const totalCostUsd = Object.values(phaseCosts).reduce((sum, cost) => sum + cost, 0);
|
|
53243
|
+
const durationMs = Date.now() - startedAt;
|
|
53244
|
+
const failedPhases = Object.entries(phaseOutputs).filter(([name, output]) => {
|
|
53245
|
+
if (verifierPassedSsot && name === gateName)
|
|
53246
|
+
return false;
|
|
53247
|
+
return !phasePassed(name, output, this.ctx.storyId);
|
|
53248
|
+
}).map(([name]) => name);
|
|
53249
|
+
const summary = {
|
|
53250
|
+
storyId: this.ctx.storyId,
|
|
53251
|
+
success: success2,
|
|
53252
|
+
totalCostUsd,
|
|
53253
|
+
durationMs,
|
|
53254
|
+
phaseCount: Object.keys(phaseOutputs).length,
|
|
53255
|
+
failedPhases: failedPhases.length > 0 ? failedPhases : undefined
|
|
53256
|
+
};
|
|
53257
|
+
if (rectResult.rectificationExhausted)
|
|
53258
|
+
summary.rectificationExhausted = true;
|
|
53259
|
+
if (rectResult.unfixedFindings)
|
|
53260
|
+
summary.unfixedFindingsCount = rectResult.unfixedFindings.length;
|
|
53261
|
+
if (success2) {
|
|
53262
|
+
logger?.info("story-orchestrator", "Story orchestration complete", summary);
|
|
53263
|
+
} else {
|
|
53264
|
+
logger?.warn("story-orchestrator", "Story orchestration failed", summary);
|
|
53265
|
+
}
|
|
53076
53266
|
return {
|
|
53077
53267
|
success: success2,
|
|
53078
53268
|
phaseCosts,
|
|
53079
53269
|
totalCostUsd,
|
|
53080
|
-
durationMs
|
|
53270
|
+
durationMs,
|
|
53081
53271
|
phaseOutputs,
|
|
53082
53272
|
...rectResult
|
|
53083
53273
|
};
|
|
@@ -53137,7 +53327,7 @@ class StoryOrchestratorBuilder {
|
|
|
53137
53327
|
return new ExecutionPlan(ctx, { ...this.state }, opts.isThreeSession ?? false);
|
|
53138
53328
|
}
|
|
53139
53329
|
}
|
|
53140
|
-
var _storyOrchestratorDeps, TDD_OP_NAMES, CANONICAL_ORDER, PHASE_KIND_TO_STATE_KEY, STRATEGY_TO_REVALIDATION_PHASES;
|
|
53330
|
+
var _storyOrchestratorDeps, TDD_OP_NAMES, STRICT_VERDICT_PHASE_NAMES, CANONICAL_ORDER, PHASE_KIND_TO_STATE_KEY, STRATEGY_TO_REVALIDATION_PHASES;
|
|
53141
53331
|
var init_story_orchestrator = __esm(() => {
|
|
53142
53332
|
init_errors();
|
|
53143
53333
|
init_findings();
|
|
@@ -53151,6 +53341,13 @@ var init_story_orchestrator = __esm(() => {
|
|
|
53151
53341
|
captureGitRef
|
|
53152
53342
|
};
|
|
53153
53343
|
TDD_OP_NAMES = new Set(["test-writer", "implementer", "verifier"]);
|
|
53344
|
+
STRICT_VERDICT_PHASE_NAMES = new Set([
|
|
53345
|
+
fullSuiteGateOp.name,
|
|
53346
|
+
verifyScopedOp.name,
|
|
53347
|
+
lintCheckOp.name,
|
|
53348
|
+
typecheckCheckOp.name,
|
|
53349
|
+
verifierOp.name
|
|
53350
|
+
]);
|
|
53154
53351
|
CANONICAL_ORDER = [
|
|
53155
53352
|
"test-writer",
|
|
53156
53353
|
"greenfield-gate",
|
|
@@ -53392,24 +53589,63 @@ async function assemblePlanInputsFromCtx(ctx) {
|
|
|
53392
53589
|
const verifyScopedInput = !_isTdd ? { workdir: ctx.workdir, storyId: story.id } : undefined;
|
|
53393
53590
|
const lintCheckInput = ctx.config.review?.enabled === true && ctx.config.review.checks?.includes("lint") && ctx.config.quality.commands.lint ? { workdir: ctx.workdir, storyId: story.id } : undefined;
|
|
53394
53591
|
const typecheckCheckInput = ctx.config.review?.enabled === true && ctx.config.review.checks?.includes("typecheck") && ctx.config.quality.commands.typecheck ? { workdir: ctx.workdir, storyId: story.id } : undefined;
|
|
53395
|
-
const
|
|
53396
|
-
|
|
53397
|
-
|
|
53398
|
-
|
|
53399
|
-
|
|
53400
|
-
|
|
53401
|
-
|
|
53402
|
-
|
|
53403
|
-
|
|
53404
|
-
|
|
53405
|
-
|
|
53406
|
-
|
|
53407
|
-
|
|
53408
|
-
|
|
53409
|
-
|
|
53410
|
-
|
|
53411
|
-
|
|
53412
|
-
|
|
53592
|
+
const semanticEnabled = ctx.config.review?.enabled === true && ctx.config.review.checks?.includes("semantic") && !!ctx.config.review.semantic;
|
|
53593
|
+
const semanticReviewInput = semanticEnabled ? await (async () => {
|
|
53594
|
+
const prepared = await prepareSemanticReviewInput({
|
|
53595
|
+
workdir: ctx.workdir,
|
|
53596
|
+
projectDir: ctx.projectDir,
|
|
53597
|
+
storyId: story.id,
|
|
53598
|
+
storyGitRef: ctx.storyGitRef,
|
|
53599
|
+
config: ctx.config,
|
|
53600
|
+
naxIgnoreIndex: ctx.naxIgnoreIndex,
|
|
53601
|
+
semanticConfig: ctx.config.review.semantic
|
|
53602
|
+
});
|
|
53603
|
+
if (prepared.skipReason)
|
|
53604
|
+
return;
|
|
53605
|
+
return {
|
|
53606
|
+
workdir: ctx.workdir,
|
|
53607
|
+
story,
|
|
53608
|
+
semanticConfig: ctx.config.review.semantic,
|
|
53609
|
+
mode: ctx.config.review.semantic.diffMode,
|
|
53610
|
+
storyGitRef: prepared.effectiveRef,
|
|
53611
|
+
stat: prepared.stat,
|
|
53612
|
+
diff: prepared.diff,
|
|
53613
|
+
excludePatterns: prepared.excludePatterns,
|
|
53614
|
+
featureCtxBlock: buildFeatureCtxBlock(ctx, "reviewer-semantic"),
|
|
53615
|
+
priorSemanticIterations: ctx.priorSemanticIterations,
|
|
53616
|
+
blockingThreshold: ctx.config.review.blockingThreshold
|
|
53617
|
+
};
|
|
53618
|
+
})() : undefined;
|
|
53619
|
+
const adversarialEnabled = ctx.config.review?.enabled === true && ctx.config.review.checks?.includes("adversarial") && !!ctx.config.review.adversarial;
|
|
53620
|
+
const adversarialReviewInput = adversarialEnabled ? await (async () => {
|
|
53621
|
+
const prepared = await prepareAdversarialReviewInput({
|
|
53622
|
+
workdir: ctx.workdir,
|
|
53623
|
+
projectDir: ctx.projectDir,
|
|
53624
|
+
storyId: story.id,
|
|
53625
|
+
storyGitRef: ctx.storyGitRef,
|
|
53626
|
+
config: ctx.config,
|
|
53627
|
+
naxIgnoreIndex: ctx.naxIgnoreIndex,
|
|
53628
|
+
adversarialConfig: ctx.config.review.adversarial
|
|
53629
|
+
});
|
|
53630
|
+
if (prepared.skipReason)
|
|
53631
|
+
return;
|
|
53632
|
+
return {
|
|
53633
|
+
workdir: ctx.workdir,
|
|
53634
|
+
story,
|
|
53635
|
+
adversarialConfig: ctx.config.review.adversarial,
|
|
53636
|
+
mode: ctx.config.review.adversarial.diffMode,
|
|
53637
|
+
storyGitRef: prepared.effectiveRef,
|
|
53638
|
+
stat: prepared.stat,
|
|
53639
|
+
diff: prepared.diff,
|
|
53640
|
+
testInventory: prepared.testInventory,
|
|
53641
|
+
excludePatterns: prepared.excludePatterns,
|
|
53642
|
+
testGlobs: prepared.testGlobs,
|
|
53643
|
+
refExcludePatterns: prepared.refExcludePatterns,
|
|
53644
|
+
featureCtxBlock: buildFeatureCtxBlock(ctx, "reviewer-adversarial"),
|
|
53645
|
+
priorAdversarialIterations: ctx.priorAdversarialIterations,
|
|
53646
|
+
blockingThreshold: ctx.config.review.blockingThreshold
|
|
53647
|
+
};
|
|
53648
|
+
})() : undefined;
|
|
53413
53649
|
const rectificationInput = ctx.config.execution?.rectification?.enabled === true ? {
|
|
53414
53650
|
maxAttempts: ctx.config.execution.rectification.maxAttemptsTotal,
|
|
53415
53651
|
strategies: [],
|
|
@@ -53435,23 +53671,28 @@ var init_plan_inputs = __esm(() => {
|
|
|
53435
53671
|
init_context();
|
|
53436
53672
|
init_errors();
|
|
53437
53673
|
init_prompts();
|
|
53674
|
+
init_review();
|
|
53438
53675
|
init_resolver();
|
|
53439
53676
|
init_build_plan_for_strategy();
|
|
53440
53677
|
});
|
|
53441
53678
|
|
|
53442
53679
|
// src/pipeline/stages/execution-helpers.ts
|
|
53443
|
-
function routeTddFailure(failureCategory, isLiteMode, ctx, reviewReason) {
|
|
53680
|
+
function routeTddFailure(failureCategory, isLiteMode, ctx, reviewReason, failureDetail) {
|
|
53681
|
+
const buildReason = (category) => {
|
|
53682
|
+
const trimmedDetail = failureDetail?.trim();
|
|
53683
|
+
return trimmedDetail ? `TDD ${category}: ${trimmedDetail}` : `TDD ${category}`;
|
|
53684
|
+
};
|
|
53444
53685
|
if (failureCategory === "isolation-violation") {
|
|
53445
53686
|
if (!isLiteMode) {
|
|
53446
53687
|
ctx.retryAsLite = true;
|
|
53447
53688
|
}
|
|
53448
|
-
return { action: "escalate" };
|
|
53689
|
+
return { action: "escalate", reason: buildReason("isolation-violation") };
|
|
53449
53690
|
}
|
|
53450
53691
|
if (failureCategory === "session-failure" || failureCategory === "tests-failing" || failureCategory === "full-suite-gate-exhausted" || failureCategory === "verifier-rejected") {
|
|
53451
|
-
return { action: "escalate" };
|
|
53692
|
+
return { action: "escalate", reason: buildReason(failureCategory) };
|
|
53452
53693
|
}
|
|
53453
53694
|
if (failureCategory === "greenfield-no-tests") {
|
|
53454
|
-
return { action: "escalate" };
|
|
53695
|
+
return { action: "escalate", reason: buildReason("greenfield-no-tests") };
|
|
53455
53696
|
}
|
|
53456
53697
|
return {
|
|
53457
53698
|
action: "pause",
|
|
@@ -53905,7 +54146,16 @@ Category: ${failureCategory ?? "unknown"}`,
|
|
|
53905
54146
|
logger.warn("execution", "Rate limited \u2014 will retry", { storyId: ctx.story.id });
|
|
53906
54147
|
}
|
|
53907
54148
|
await cleanupSessionOnFailure(ctx);
|
|
53908
|
-
|
|
54149
|
+
const failedPhaseNames = Object.keys(failedPhases);
|
|
54150
|
+
const reasonParts = [];
|
|
54151
|
+
reasonParts.push(`agent session failed (exit ${agentResult.exitCode ?? "?"})`);
|
|
54152
|
+
if (failureCategory)
|
|
54153
|
+
reasonParts.push(`category=${failureCategory}`);
|
|
54154
|
+
if (agentResult.rateLimited)
|
|
54155
|
+
reasonParts.push("rate-limited");
|
|
54156
|
+
if (failedPhaseNames.length > 0)
|
|
54157
|
+
reasonParts.push(`phases=${failedPhaseNames.join(",")}`);
|
|
54158
|
+
return { action: "escalate", reason: reasonParts.join("; ") };
|
|
53909
54159
|
}
|
|
53910
54160
|
if (!isTdd) {
|
|
53911
54161
|
await _postRunDeps.autoCommitIfDirty(ctx.workdir, "execution", "single-session", ctx.story.id);
|
|
@@ -57730,7 +57980,7 @@ var package_default;
|
|
|
57730
57980
|
var init_package = __esm(() => {
|
|
57731
57981
|
package_default = {
|
|
57732
57982
|
name: "@nathapp/nax",
|
|
57733
|
-
version: "0.67.
|
|
57983
|
+
version: "0.67.13",
|
|
57734
57984
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
57735
57985
|
type: "module",
|
|
57736
57986
|
bin: {
|
|
@@ -57825,8 +58075,8 @@ var init_version = __esm(() => {
|
|
|
57825
58075
|
NAX_VERSION = package_default.version;
|
|
57826
58076
|
NAX_COMMIT = (() => {
|
|
57827
58077
|
try {
|
|
57828
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
57829
|
-
return "
|
|
58078
|
+
if (/^[0-9a-f]{6,10}$/.test("45ff95d3"))
|
|
58079
|
+
return "45ff95d3";
|
|
57830
58080
|
} catch {}
|
|
57831
58081
|
try {
|
|
57832
58082
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -60593,13 +60843,16 @@ var init_tier_outcome = __esm(() => {
|
|
|
60593
60843
|
});
|
|
60594
60844
|
|
|
60595
60845
|
// src/execution/escalation/tier-escalation.ts
|
|
60596
|
-
function buildEscalationFailure(story, currentTier, reviewFindings, cost) {
|
|
60846
|
+
function buildEscalationFailure(story, currentTier, reviewFindings, cost, pipelineReason, failureCategory) {
|
|
60597
60847
|
const stage = reviewFindings && reviewFindings.length > 0 ? "review" : "escalation";
|
|
60848
|
+
const trimmedReason = pipelineReason?.trim();
|
|
60849
|
+
const categoryPart = failureCategory ? ` [${failureCategory}]` : "";
|
|
60850
|
+
const summary = trimmedReason ? `Tier ${currentTier}${categoryPart}: ${trimmedReason}` : `Tier ${currentTier}${categoryPart} failed \u2014 no pipeline reason recorded`;
|
|
60598
60851
|
return {
|
|
60599
60852
|
attempt: (story.attempts ?? 0) + 1,
|
|
60600
60853
|
modelTier: currentTier,
|
|
60601
60854
|
stage,
|
|
60602
|
-
summary
|
|
60855
|
+
summary,
|
|
60603
60856
|
reviewFindings: reviewFindings && reviewFindings.length > 0 ? reviewFindings : undefined,
|
|
60604
60857
|
cost: cost ?? 0,
|
|
60605
60858
|
timestamp: new Date().toISOString()
|
|
@@ -60703,7 +60956,7 @@ async function handleTierEscalation(ctx) {
|
|
|
60703
60956
|
const isChangingTier = currentStoryTier !== escalatedTier;
|
|
60704
60957
|
const shouldResetAttempts = isChangingTier || shouldSwitchToTestAfter;
|
|
60705
60958
|
const escalationRecord = isChangingTier || shouldSwitchToTestAfter ? buildEscalationRecord(currentStoryTier, shouldSwitchToTestAfter ? currentStoryTier : escalatedTier, ctx.pipelineResult.reason ?? "Escalated to next retry path") : undefined;
|
|
60706
|
-
const escalationFailure = buildEscalationFailure(s, currentStoryTier, escalateReviewFindings, ctx.attemptCost);
|
|
60959
|
+
const escalationFailure = buildEscalationFailure(s, currentStoryTier, escalateReviewFindings, ctx.attemptCost, verifiedPipelineReason, escalateFailureCategory);
|
|
60707
60960
|
return {
|
|
60708
60961
|
...s,
|
|
60709
60962
|
attempts: shouldResetAttempts ? 0 : (s.attempts ?? 0) + 1,
|