@codacy/verity-cli 0.28.1-experimental.39013ce → 0.28.1-experimental.4410c26
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/bin/verity.js +196 -42
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -10386,10 +10386,9 @@ function projectPath(relativePath) {
|
|
|
10386
10386
|
return (0, import_node_path.join)(repoRoot(), relativePath);
|
|
10387
10387
|
}
|
|
10388
10388
|
var MAX_DELTA_BYTES = 194560;
|
|
10389
|
-
var MAX_FILES =
|
|
10389
|
+
var MAX_FILES = 40;
|
|
10390
10390
|
var MAX_FILE_BYTES = 51200;
|
|
10391
10391
|
var DEBOUNCE_SECONDS = 30;
|
|
10392
|
-
var MAX_ITERATIONS = 2;
|
|
10393
10392
|
var MAX_SPEC_FILES = 10;
|
|
10394
10393
|
var MAX_SPEC_FILE_BYTES = 10240;
|
|
10395
10394
|
var MAX_TOTAL_SPEC_BYTES = 30720;
|
|
@@ -15543,6 +15542,34 @@ ${addedLines}`,
|
|
|
15543
15542
|
}
|
|
15544
15543
|
return { diffs, has_baseline: true };
|
|
15545
15544
|
}
|
|
15545
|
+
function absorbIntoBaseline(paths, sessionId) {
|
|
15546
|
+
const baseline = readBaseline(sessionId);
|
|
15547
|
+
if (!baseline || paths.length === 0) return 0;
|
|
15548
|
+
const dir = sessionDir(sessionKey(baseline.session_id));
|
|
15549
|
+
let adopted = 0;
|
|
15550
|
+
const dirty = new Set(baseline.dirty_paths);
|
|
15551
|
+
for (const p of paths) {
|
|
15552
|
+
try {
|
|
15553
|
+
const content = safeReadForMirror(projectPath(p));
|
|
15554
|
+
if (content === null) continue;
|
|
15555
|
+
const dest = mirrorPath(dir, p);
|
|
15556
|
+
(0, import_node_fs13.mkdirSync)((0, import_node_path14.dirname)(dest), { recursive: true });
|
|
15557
|
+
(0, import_node_fs13.writeFileSync)(dest, content);
|
|
15558
|
+
dirty.add(p);
|
|
15559
|
+
adopted++;
|
|
15560
|
+
} catch {
|
|
15561
|
+
}
|
|
15562
|
+
}
|
|
15563
|
+
if (adopted === 0) return 0;
|
|
15564
|
+
try {
|
|
15565
|
+
const updated = { ...baseline, dirty_paths: [...dirty] };
|
|
15566
|
+
(0, import_node_fs13.writeFileSync)(manifestPath(dir), JSON.stringify(updated));
|
|
15567
|
+
preImageCache.delete(baseline);
|
|
15568
|
+
} catch {
|
|
15569
|
+
return 0;
|
|
15570
|
+
}
|
|
15571
|
+
return adopted;
|
|
15572
|
+
}
|
|
15546
15573
|
function changedSinceBaseline(repoRelPath, baseline) {
|
|
15547
15574
|
const pre = preImage(repoRelPath, baseline);
|
|
15548
15575
|
let current;
|
|
@@ -16419,40 +16446,40 @@ function narrowToRecent(files, sessionId) {
|
|
|
16419
16446
|
});
|
|
16420
16447
|
return recent.length > 0 ? recent : files;
|
|
16421
16448
|
}
|
|
16422
|
-
function
|
|
16423
|
-
if (!(0, import_node_fs15.existsSync)(ITERATION_FILE)) return 1;
|
|
16449
|
+
function readIterationState(currentCommit) {
|
|
16450
|
+
if (!(0, import_node_fs15.existsSync)(ITERATION_FILE)) return { iteration: 1, fingerprint: null };
|
|
16424
16451
|
try {
|
|
16425
16452
|
const stored = (0, import_node_fs15.readFileSync)(ITERATION_FILE, "utf-8").trim();
|
|
16426
16453
|
const parts = stored.split(":");
|
|
16427
16454
|
const iter = parseInt(parts[0], 10);
|
|
16428
16455
|
const storedCommit = parts[1] ?? "";
|
|
16429
16456
|
const storedTimestamp = parseInt(parts[2] ?? "0", 10);
|
|
16430
|
-
|
|
16431
|
-
if (
|
|
16457
|
+
const fingerprint = parts.slice(3).join(":") || null;
|
|
16458
|
+
if (isNaN(iter)) return { iteration: 1, fingerprint: null };
|
|
16459
|
+
if (storedCommit !== currentCommit) return { iteration: 1, fingerprint: null };
|
|
16432
16460
|
if (storedTimestamp > 0) {
|
|
16433
16461
|
const elapsed = Math.floor(Date.now() / 1e3) - storedTimestamp;
|
|
16434
|
-
if (elapsed > 600) return 1;
|
|
16462
|
+
if (elapsed > 600) return { iteration: 1, fingerprint: null };
|
|
16435
16463
|
}
|
|
16436
|
-
return iter;
|
|
16464
|
+
return { iteration: iter, fingerprint };
|
|
16437
16465
|
} catch {
|
|
16438
|
-
return 1;
|
|
16466
|
+
return { iteration: 1, fingerprint: null };
|
|
16439
16467
|
}
|
|
16440
16468
|
}
|
|
16441
|
-
function
|
|
16442
|
-
const
|
|
16443
|
-
|
|
16444
|
-
|
|
16445
|
-
|
|
16446
|
-
|
|
16447
|
-
|
|
16448
|
-
|
|
16449
|
-
}
|
|
16450
|
-
return { skip: null, iteration };
|
|
16469
|
+
function findingsFingerprint(findings) {
|
|
16470
|
+
const keys = findings.map((f) => `${String(f.pattern_id ?? "?")}|${String(f.file ?? "?")}`).filter((k) => k !== "?|?");
|
|
16471
|
+
return [...new Set(keys)].sort().join(",");
|
|
16472
|
+
}
|
|
16473
|
+
function isSameProblem(previous, current) {
|
|
16474
|
+
if (!previous || !current) return false;
|
|
16475
|
+
const prev = new Set(previous.split(","));
|
|
16476
|
+
return current.split(",").some((k) => prev.has(k));
|
|
16451
16477
|
}
|
|
16452
|
-
function writeIteration(iteration, commit, _contentHash) {
|
|
16478
|
+
function writeIteration(iteration, commit, _contentHash, fingerprint) {
|
|
16453
16479
|
(0, import_node_fs15.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
16454
16480
|
const ts = Math.floor(Date.now() / 1e3);
|
|
16455
|
-
|
|
16481
|
+
const fp = fingerprint ? `:${fingerprint}` : "";
|
|
16482
|
+
(0, import_node_fs15.writeFileSync)(ITERATION_FILE, `${iteration}:${commit}:${ts}${fp}`);
|
|
16456
16483
|
}
|
|
16457
16484
|
|
|
16458
16485
|
// src/lib/static-analysis.ts
|
|
@@ -16701,7 +16728,7 @@ function resolveTaskContext(opts) {
|
|
|
16701
16728
|
// src/lib/cli-version.ts
|
|
16702
16729
|
function cliVersion() {
|
|
16703
16730
|
try {
|
|
16704
|
-
return true ? "0.28.1-experimental.
|
|
16731
|
+
return true ? "0.28.1-experimental.4410c26" : "dev";
|
|
16705
16732
|
} catch {
|
|
16706
16733
|
return "dev";
|
|
16707
16734
|
}
|
|
@@ -17375,6 +17402,7 @@ function buildAgentContext(input) {
|
|
|
17375
17402
|
}
|
|
17376
17403
|
for (const p of input.pendingItems ?? []) {
|
|
17377
17404
|
if (lines.length >= MAX_AGENT_ITEMS) break;
|
|
17405
|
+
if (p.pattern_id === "intent-misalignment") continue;
|
|
17378
17406
|
const text = p.description ?? p.title ?? p.reason;
|
|
17379
17407
|
if (!text) continue;
|
|
17380
17408
|
lines.push(renderItem("", text, p.pattern_id, p.file, p.line));
|
|
@@ -17638,9 +17666,11 @@ var MAX_SUMMARY_BYTES = 4096;
|
|
|
17638
17666
|
var HOME = process.env.HOME ?? "";
|
|
17639
17667
|
async function extractActionSummary(transcriptPath) {
|
|
17640
17668
|
try {
|
|
17641
|
-
const
|
|
17642
|
-
if (!
|
|
17643
|
-
|
|
17669
|
+
const read = readTurnLines(transcriptPath);
|
|
17670
|
+
if (!read || read.lines.length === 0) return null;
|
|
17671
|
+
const summary = buildSummary(read.lines);
|
|
17672
|
+
if (summary) summary.transcript_windowed = read.window;
|
|
17673
|
+
return summary;
|
|
17644
17674
|
} catch {
|
|
17645
17675
|
return null;
|
|
17646
17676
|
}
|
|
@@ -17654,9 +17684,11 @@ function readTurnLines(transcriptPath) {
|
|
|
17654
17684
|
}
|
|
17655
17685
|
if (size === 0) return null;
|
|
17656
17686
|
let raw;
|
|
17687
|
+
let windowed = false;
|
|
17657
17688
|
if (size <= SMALL_FILE_BYTES) {
|
|
17658
17689
|
raw = (0, import_node_fs22.readFileSync)(transcriptPath, "utf-8");
|
|
17659
17690
|
} else {
|
|
17691
|
+
windowed = true;
|
|
17660
17692
|
const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
|
|
17661
17693
|
const fd = require("node:fs").openSync(transcriptPath, "r");
|
|
17662
17694
|
try {
|
|
@@ -17674,17 +17706,22 @@ function readTurnLines(transcriptPath) {
|
|
|
17674
17706
|
const allLines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
17675
17707
|
if (allLines.length === 0) return null;
|
|
17676
17708
|
let turnStart = 0;
|
|
17709
|
+
let boundaryFound = false;
|
|
17677
17710
|
for (let i = allLines.length - 1; i >= 0; i--) {
|
|
17678
17711
|
try {
|
|
17679
17712
|
const parsed = JSON.parse(allLines[i]);
|
|
17680
17713
|
if (parsed.type === "user" && isRealUserMessage(parsed)) {
|
|
17681
17714
|
turnStart = i;
|
|
17715
|
+
boundaryFound = true;
|
|
17682
17716
|
break;
|
|
17683
17717
|
}
|
|
17684
17718
|
} catch {
|
|
17685
17719
|
}
|
|
17686
17720
|
}
|
|
17687
|
-
return
|
|
17721
|
+
return {
|
|
17722
|
+
lines: allLines.slice(turnStart),
|
|
17723
|
+
window: !windowed ? "whole" : boundaryFound ? "windowed" : "orphaned"
|
|
17724
|
+
};
|
|
17688
17725
|
}
|
|
17689
17726
|
function isRealUserMessage(parsed) {
|
|
17690
17727
|
const message = parsed.message;
|
|
@@ -18281,7 +18318,7 @@ async function passAndExit(reason, skip, kindOverride) {
|
|
|
18281
18318
|
if (unaccounted.length > 0) {
|
|
18282
18319
|
logEvent("coverage_unaccounted", { where: "passAndExit", skip, count: unaccounted.length });
|
|
18283
18320
|
}
|
|
18284
|
-
const AGENT_SILENT_SKIPS = /* @__PURE__ */ new Set([
|
|
18321
|
+
const AGENT_SILENT_SKIPS = /* @__PURE__ */ new Set([]);
|
|
18285
18322
|
const agentNote = AGENT_SILENT_SKIPS.has(skip) ? null : note;
|
|
18286
18323
|
printJsonCompact(
|
|
18287
18324
|
buildHookOutput(
|
|
@@ -18382,13 +18419,21 @@ async function runAnalyze(opts, globals) {
|
|
|
18382
18419
|
const conversation = await readAndClearConversationBuffer(baselineSessionId);
|
|
18383
18420
|
const specs = discoverSpecs();
|
|
18384
18421
|
const plans = discoverPlans();
|
|
18422
|
+
const agentAuthoredCodeThisTurn = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0));
|
|
18423
|
+
const turnAuthoredCode = agentAuthoredCodeThisTurn || !!baseline && allForReview.some((f) => changedSinceBaseline(f, baseline));
|
|
18424
|
+
const canSeeTurnAuthorship = !!actionSummary || !!baseline;
|
|
18385
18425
|
const latestPrompt = conversation?.prompts?.[conversation.prompts.length - 1]?.prompt ?? "";
|
|
18386
18426
|
if (/^\s*\/verity-/i.test(latestPrompt)) {
|
|
18427
|
+
const setupAuthored = [
|
|
18428
|
+
...actionSummary?.files_edited ?? [],
|
|
18429
|
+
...actionSummary?.files_created ?? []
|
|
18430
|
+
];
|
|
18431
|
+
if (setupAuthored.length > 0) {
|
|
18432
|
+
const adopted = absorbIntoBaseline(setupAuthored, baselineSessionId);
|
|
18433
|
+
logEvent("baseline_absorbed", { skip: "verity-command", offered: setupAuthored.length, adopted });
|
|
18434
|
+
}
|
|
18387
18435
|
await passAndExit("Verity command \u2014 skipping analysis", "verity-command");
|
|
18388
18436
|
}
|
|
18389
|
-
const agentAuthoredCodeThisTurn = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0));
|
|
18390
|
-
const turnAuthoredCode = agentAuthoredCodeThisTurn || !!baseline && allForReview.some((f) => changedSinceBaseline(f, baseline));
|
|
18391
|
-
const canSeeTurnAuthorship = !!actionSummary || !!baseline;
|
|
18392
18437
|
if (shouldSkipForBareAck({ prompt: latestPrompt, turnAuthoredCode, canSeeTurnAuthorship })) {
|
|
18393
18438
|
await passAndExit("Bare acknowledgment \u2014 skipping analysis", "bare-acknowledgment");
|
|
18394
18439
|
}
|
|
@@ -18438,7 +18483,11 @@ async function runAnalyze(opts, globals) {
|
|
|
18438
18483
|
);
|
|
18439
18484
|
}
|
|
18440
18485
|
if (analysisMode === "skip") {
|
|
18441
|
-
await passAndExit(
|
|
18486
|
+
await passAndExit(
|
|
18487
|
+
"Skip mode \u2014 no code work to analyze",
|
|
18488
|
+
"skip-mode",
|
|
18489
|
+
turnAuthoredCode ? "capacity" : void 0
|
|
18490
|
+
);
|
|
18442
18491
|
}
|
|
18443
18492
|
let staticResults = {
|
|
18444
18493
|
tool: "@codacy/analysis-cli",
|
|
@@ -18531,19 +18580,13 @@ async function runAnalyze(opts, globals) {
|
|
|
18531
18580
|
snapshotResult = generateSnapshotDiffs(codeDelta.files);
|
|
18532
18581
|
}
|
|
18533
18582
|
currentCommit = getCurrentCommit();
|
|
18534
|
-
|
|
18535
|
-
const iterResult = checkMaxIterations(currentCommit, maxIterations, contentHash ?? void 0);
|
|
18536
|
-
if (iterResult.skip) await passAndExit(iterResult.skip, "iteration-cap");
|
|
18537
|
-
iteration = iterResult.iteration;
|
|
18583
|
+
iteration = readIterationState(currentCommit).iteration;
|
|
18538
18584
|
}
|
|
18539
18585
|
}
|
|
18540
18586
|
if (analysisMode === "plan") {
|
|
18541
18587
|
recordAnalysisStart();
|
|
18542
18588
|
currentCommit = getCurrentCommit();
|
|
18543
|
-
|
|
18544
|
-
const iterResult = checkMaxIterations(currentCommit, maxIterations);
|
|
18545
|
-
if (iterResult.skip) await passAndExit(iterResult.skip, "iteration-cap");
|
|
18546
|
-
iteration = iterResult.iteration;
|
|
18589
|
+
iteration = readIterationState(currentCommit).iteration;
|
|
18547
18590
|
}
|
|
18548
18591
|
const contextFiles = gatherContextFiles(contextFilePaths, codeDelta.files);
|
|
18549
18592
|
for (const f of codeDelta.files) {
|
|
@@ -18716,7 +18759,30 @@ async function runAnalyze(opts, globals) {
|
|
|
18716
18759
|
hasUserPrompt: (conversation?.prompts?.length ?? 0) > 0,
|
|
18717
18760
|
isTTY: process.stdout.isTTY === true
|
|
18718
18761
|
});
|
|
18762
|
+
const excludedByReason = {};
|
|
18763
|
+
for (const e of codeDelta.excluded ?? []) {
|
|
18764
|
+
excludedByReason[e.reason] = (excludedByReason[e.reason] ?? 0) + 1;
|
|
18765
|
+
}
|
|
18766
|
+
const coverageTelemetry = {
|
|
18767
|
+
// git's whole answer, before ANY narrowing. The number that has never been sent.
|
|
18768
|
+
changed_all: allChanged.length,
|
|
18769
|
+
analyzable: analyzable.length,
|
|
18770
|
+
reviewable: reviewable.length,
|
|
18771
|
+
security: securityFiles.length,
|
|
18772
|
+
// after the allowlist, before authorship scoping and the caps
|
|
18773
|
+
for_review: allForReview.length,
|
|
18774
|
+
// what actually reaches the reviewer
|
|
18775
|
+
sent: codeDelta.files.length,
|
|
18776
|
+
// the two silent narrowings, counted separately so they can be told apart
|
|
18777
|
+
capped_out: actionSummary?.capped_out?.length ?? 0,
|
|
18778
|
+
excluded: (codeDelta.excluded ?? []).length,
|
|
18779
|
+
excluded_by_reason: excludedByReason,
|
|
18780
|
+
// was the transcript itself truncated? The 256 KB window means "this turn"
|
|
18781
|
+
// can quietly mean "the last 256 KB of it".
|
|
18782
|
+
transcript_windowed: actionSummary?.transcript_windowed ?? null
|
|
18783
|
+
};
|
|
18719
18784
|
const requestBody = {
|
|
18785
|
+
coverage_telemetry: coverageTelemetry,
|
|
18720
18786
|
static_results: staticResults,
|
|
18721
18787
|
code_delta: codeDelta,
|
|
18722
18788
|
changed_files: allForReview,
|
|
@@ -18802,6 +18868,54 @@ async function runAnalyze(opts, globals) {
|
|
|
18802
18868
|
// replaced a population floor with ≈35% power that was sub-integer for
|
|
18803
18869
|
// three-quarters of the fleet.
|
|
18804
18870
|
conservation: foldConservation,
|
|
18871
|
+
// ⚠ VRT-52 — RECORDED, NOT APPLIED. The number nobody has.
|
|
18872
|
+
//
|
|
18873
|
+
// The whole "task-scoped delta" design space rests on an assumption that
|
|
18874
|
+
// has been observed exactly ONCE: that delta files routinely belong to
|
|
18875
|
+
// earlier work. Three designs were built on it and all three were killed
|
|
18876
|
+
// adversarially — two by measurement — so before another is attempted,
|
|
18877
|
+
// measure the base rate.
|
|
18878
|
+
//
|
|
18879
|
+
// `authored_under_earlier_goal` counts delta paths whose LAST authorship
|
|
18880
|
+
// event precedes the seq of the goal now in force. Both numbers come from
|
|
18881
|
+
// the same append-only counter (`nextSeq`), so the comparison is exact.
|
|
18882
|
+
//
|
|
18883
|
+
// Keyed on the GOAL, deliberately, not on the task id. The task classifier
|
|
18884
|
+
// reported `is_new_task` on two consecutive turns of one task 25 seconds
|
|
18885
|
+
// apart, so a task-keyed number would measure its unreliability rather
|
|
18886
|
+
// than the phenomenon. And this only became meaningful once `recordGoal`
|
|
18887
|
+
// stopped letting a bare "ok" supersede the goal — before that the seq
|
|
18888
|
+
// advanced every turn and this would have degenerated to "not edited this
|
|
18889
|
+
// turn", which is the exact mistake that sank one of the three designs.
|
|
18890
|
+
//
|
|
18891
|
+
// Changes no payload the reviewer sees, no narrowing, no verdict.
|
|
18892
|
+
vrt52: (() => {
|
|
18893
|
+
const goalSeq = memory?.projection.goal?.seq;
|
|
18894
|
+
if (goalSeq === void 0 || !memorySession) return { known: false };
|
|
18895
|
+
const lastSeq2 = new Map(
|
|
18896
|
+
foldDossier(memorySession.d).authored_all.map((a) => [a.path, a.last_seq])
|
|
18897
|
+
);
|
|
18898
|
+
let earlier = 0;
|
|
18899
|
+
let unknown = 0;
|
|
18900
|
+
for (const f of codeDelta.files) {
|
|
18901
|
+
const seen = lastSeq2.get(f.path);
|
|
18902
|
+
if (seen === void 0) unknown++;
|
|
18903
|
+
else if (seen < goalSeq) earlier++;
|
|
18904
|
+
}
|
|
18905
|
+
return {
|
|
18906
|
+
known: true,
|
|
18907
|
+
goal_seq: goalSeq,
|
|
18908
|
+
delta: codeDelta.files.length,
|
|
18909
|
+
// Files this delta carries that were last written under an EARLIER
|
|
18910
|
+
// instruction. If this stays near zero, VRT-52's code half is
|
|
18911
|
+
// unnecessary and should be closed saying so.
|
|
18912
|
+
authored_under_earlier_goal: earlier,
|
|
18913
|
+
// Delta files the dossier has no authorship record for at all —
|
|
18914
|
+
// pre-existing tree state, or an authorship channel the fold cannot
|
|
18915
|
+
// see. Reported separately so a blind spot is never counted as a zero.
|
|
18916
|
+
no_authorship_record: unknown
|
|
18917
|
+
};
|
|
18918
|
+
})(),
|
|
18805
18919
|
// P2 — RECORDED, NOT APPLIED. What this run WOULD have reviewed if it
|
|
18806
18920
|
// narrowed to the within-session increment: what changed since the last
|
|
18807
18921
|
// VERDICT rather than since task start.
|
|
@@ -18836,6 +18950,14 @@ async function runAnalyze(opts, globals) {
|
|
|
18836
18950
|
const latest = conversation.prompts[conversation.prompts.length - 1];
|
|
18837
18951
|
const goalPrompt = resolveGoalPrompt(conversation.prompts) ?? { entry: latest, turnsBack: 0 };
|
|
18838
18952
|
intentContext.user_prompt = goalPrompt.entry.prompt;
|
|
18953
|
+
if (isContinuationPrompt(intentContext.user_prompt)) {
|
|
18954
|
+
const carried = memory?.projection.goal?.text;
|
|
18955
|
+
if (carried && !isContinuationPrompt(carried)) {
|
|
18956
|
+
intentContext.continuation_prompt = latest.prompt;
|
|
18957
|
+
intentContext.user_prompt = carried;
|
|
18958
|
+
logEvent("goal_from_dossier", { chars: carried.length });
|
|
18959
|
+
}
|
|
18960
|
+
}
|
|
18839
18961
|
if (goalPrompt.turnsBack > 0) {
|
|
18840
18962
|
intentContext.continuation_prompt = latest.prompt;
|
|
18841
18963
|
logEvent("goal_walked_back", { turns_back: goalPrompt.turnsBack });
|
|
@@ -19162,9 +19284,41 @@ async function runAnalyze(opts, globals) {
|
|
|
19162
19284
|
reverify_by: response.reverify_by
|
|
19163
19285
|
});
|
|
19164
19286
|
const grantNudge = grantWarning ? ` \u26A0\uFE0F ${grantWarning}` : "";
|
|
19165
|
-
|
|
19287
|
+
let capReleased = false;
|
|
19288
|
+
let effectiveDecision = decision;
|
|
19289
|
+
if (decision === "FAIL") {
|
|
19290
|
+
const blocking = (response.findings ?? []).filter((f) => {
|
|
19291
|
+
const sev = String(f.severity ?? "").toLowerCase();
|
|
19292
|
+
return sev === "critical" || sev === "high";
|
|
19293
|
+
});
|
|
19294
|
+
const fingerprint = findingsFingerprint(blocking);
|
|
19295
|
+
const prior = readIterationState(currentCommit);
|
|
19296
|
+
const sameProblem = isSameProblem(prior.fingerprint, fingerprint);
|
|
19297
|
+
const nextIteration = sameProblem ? prior.iteration + 1 : 1;
|
|
19298
|
+
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
19299
|
+
writeIteration(nextIteration, currentCommit, contentHash ?? void 0, fingerprint);
|
|
19300
|
+
iteration = nextIteration;
|
|
19301
|
+
if (nextIteration > maxIterations) {
|
|
19302
|
+
capReleased = true;
|
|
19303
|
+
effectiveDecision = "WARN";
|
|
19304
|
+
logEvent("iteration_cap_released", { iteration: nextIteration, fingerprint });
|
|
19305
|
+
}
|
|
19306
|
+
}
|
|
19307
|
+
if (capReleased) {
|
|
19308
|
+
const findings = response.findings ?? [];
|
|
19309
|
+
const lines = findings.slice(0, 5).map((f) => ` [${String(f.severity ?? "?").toUpperCase()}] ${String(f.title ?? f.message ?? "")} (${String(f.file ?? "?")}:${String(f.line ?? "?")})`);
|
|
19310
|
+
emitVerdict({
|
|
19311
|
+
proposed: "WARN",
|
|
19312
|
+
changed: skipCoverageChanged,
|
|
19313
|
+
coverage: reviewCoverage,
|
|
19314
|
+
userSummary: `Verity: WARN \u2014 self-healing limit (${opts.maxIterations}) reached on the same finding. NO LONGER BLOCKING, but ${findings.length} finding(s) remain OPEN and were NOT fixed. Human review required before deploying.
|
|
19315
|
+
${lines.join("\n")}`,
|
|
19316
|
+
agentContext: null,
|
|
19317
|
+
silenced: true
|
|
19318
|
+
});
|
|
19319
|
+
}
|
|
19320
|
+
switch (effectiveDecision) {
|
|
19166
19321
|
case "FAIL": {
|
|
19167
|
-
writeIteration(iteration + 1, currentCommit, contentHash ?? void 0);
|
|
19168
19322
|
const assessment = response.assessment;
|
|
19169
19323
|
const narrative = assessment?.narrative ?? "";
|
|
19170
19324
|
const findings = response.findings ?? [];
|
|
@@ -21094,7 +21248,7 @@ function registerTelemetryCommands(program2) {
|
|
|
21094
21248
|
}
|
|
21095
21249
|
|
|
21096
21250
|
// src/cli.ts
|
|
21097
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.28.1-experimental.
|
|
21251
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.28.1-experimental.4410c26").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async () => {
|
|
21098
21252
|
try {
|
|
21099
21253
|
await foldLegacyLocalCredential();
|
|
21100
21254
|
} catch {
|