@codacy/verity-cli 0.29.4-experimental.0d2e4a3 → 0.29.4-experimental.2ae1813
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 +180 -48
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -15060,8 +15060,10 @@ function recordVerdict(d, v) {
|
|
|
15060
15060
|
if (summary) appendEvent(d, { k: "goal_delivered", summary });
|
|
15061
15061
|
}
|
|
15062
15062
|
const lines = /* @__PURE__ */ new Map();
|
|
15063
|
+
const sent = new Set(v.sentPaths);
|
|
15063
15064
|
for (const f of v.findings) {
|
|
15064
15065
|
if (!f.file || typeof f.line !== "number" || !f.pattern_id) continue;
|
|
15066
|
+
if (!sent.has(f.file)) continue;
|
|
15065
15067
|
if (!lines.has(f.file)) {
|
|
15066
15068
|
try {
|
|
15067
15069
|
const abs = (0, import_node_path12.join)(root, f.file);
|
|
@@ -16717,7 +16719,7 @@ function formatRunEvidence(run, startedAt) {
|
|
|
16717
16719
|
const md = run.modeDecision;
|
|
16718
16720
|
if (md) {
|
|
16719
16721
|
const how = md.forced ? "forced by --mode" : `predicted=${md.predicted ?? "none"} \u2192 ${md.resolved}`;
|
|
16720
|
-
out += row("mode", `${md.resolved} \xB7 ${how} \xB7 authored=${md.authored ? "yes" : "no"} \xB7 investigated=${md.investigated ? "yes" : "no"}`);
|
|
16722
|
+
out += row("mode", `${md.resolved} \xB7 ${how}` + (md.flip ? ` (flipped to plan: no delta at ${md.flip})` : "") + ` \xB7 authored=${md.authored ? "yes" : "no"} \xB7 investigated=${md.investigated ? "yes" : "no"}`);
|
|
16721
16723
|
} else {
|
|
16722
16724
|
out += row("mode", `? (this run stopped in ${run.phaseReached || "no phase"}, before the mode was decided)`);
|
|
16723
16725
|
}
|
|
@@ -17240,7 +17242,7 @@ function channelSilence(input) {
|
|
|
17240
17242
|
// src/lib/cli-version.ts
|
|
17241
17243
|
function cliVersion() {
|
|
17242
17244
|
try {
|
|
17243
|
-
return true ? "0.29.4-experimental.
|
|
17245
|
+
return true ? "0.29.4-experimental.2ae1813" : "dev";
|
|
17244
17246
|
} catch {
|
|
17245
17247
|
return "dev";
|
|
17246
17248
|
}
|
|
@@ -18057,26 +18059,50 @@ function narrowToRecent(files, sessionId) {
|
|
|
18057
18059
|
});
|
|
18058
18060
|
return recent.length > 0 ? recent : files;
|
|
18059
18061
|
}
|
|
18060
|
-
function
|
|
18061
|
-
|
|
18062
|
+
function readIteration(currentCommit, _contentHash) {
|
|
18063
|
+
return Math.max(1, readBlockState(currentCommit).attempts);
|
|
18064
|
+
}
|
|
18065
|
+
var NO_BLOCKS = { attempts: 0, blocks: 0, fingerprint: null };
|
|
18066
|
+
function readBlockState(currentCommit, opts) {
|
|
18067
|
+
if (opts?.newUserPrompt) return NO_BLOCKS;
|
|
18068
|
+
if (!(0, import_node_fs22.existsSync)(ITERATION_FILE)) return NO_BLOCKS;
|
|
18062
18069
|
try {
|
|
18063
18070
|
const stored = (0, import_node_fs22.readFileSync)(ITERATION_FILE, "utf-8").trim();
|
|
18064
|
-
const
|
|
18065
|
-
|
|
18066
|
-
|
|
18067
|
-
|
|
18068
|
-
|
|
18069
|
-
if (isNaN(iter)) return { iteration: 1, fingerprint: null };
|
|
18070
|
-
if (storedCommit !== currentCommit) return { iteration: 1, fingerprint: null };
|
|
18071
|
-
if (storedTimestamp > 0) {
|
|
18072
|
-
const elapsed = Math.floor(Date.now() / 1e3) - storedTimestamp;
|
|
18073
|
-
if (elapsed > 600) return { iteration: 1, fingerprint: null };
|
|
18074
|
-
}
|
|
18075
|
-
return { iteration: iter, fingerprint };
|
|
18071
|
+
const parsed = stored.startsWith("{") ? parseJsonState(stored) : parseLegacyState(stored);
|
|
18072
|
+
if (!parsed) return NO_BLOCKS;
|
|
18073
|
+
if (parsed.commit !== currentCommit) return NO_BLOCKS;
|
|
18074
|
+
if (parsed.ts > 0 && Math.floor(Date.now() / 1e3) - parsed.ts > 600) return NO_BLOCKS;
|
|
18075
|
+
return { attempts: parsed.attempts, blocks: parsed.blocks, fingerprint: parsed.fingerprint };
|
|
18076
18076
|
} catch {
|
|
18077
|
-
return
|
|
18077
|
+
return NO_BLOCKS;
|
|
18078
18078
|
}
|
|
18079
18079
|
}
|
|
18080
|
+
function parseJsonState(raw) {
|
|
18081
|
+
const o = JSON.parse(raw);
|
|
18082
|
+
const attempts = typeof o.attempts === "number" ? o.attempts : NaN;
|
|
18083
|
+
if (isNaN(attempts)) return null;
|
|
18084
|
+
return {
|
|
18085
|
+
attempts,
|
|
18086
|
+
blocks: typeof o.blocks === "number" ? o.blocks : attempts,
|
|
18087
|
+
fingerprint: typeof o.fingerprint === "string" && o.fingerprint ? o.fingerprint : null,
|
|
18088
|
+
commit: typeof o.commit === "string" ? o.commit : "",
|
|
18089
|
+
ts: typeof o.ts === "number" ? o.ts : 0
|
|
18090
|
+
};
|
|
18091
|
+
}
|
|
18092
|
+
function parseLegacyState(raw) {
|
|
18093
|
+
const parts = raw.split(":");
|
|
18094
|
+
const n = parseInt(parts[0], 10);
|
|
18095
|
+
if (isNaN(n)) return null;
|
|
18096
|
+
return {
|
|
18097
|
+
attempts: n,
|
|
18098
|
+
// The old file has no separate block count; the old counter is the closest
|
|
18099
|
+
// honest answer, and it errs toward releasing sooner rather than later.
|
|
18100
|
+
blocks: n,
|
|
18101
|
+
fingerprint: parts.slice(3).join(":") || null,
|
|
18102
|
+
commit: parts[1] ?? "",
|
|
18103
|
+
ts: parseInt(parts[2] ?? "0", 10)
|
|
18104
|
+
};
|
|
18105
|
+
}
|
|
18080
18106
|
function findingsFingerprint(findings) {
|
|
18081
18107
|
const keys = findings.map((f) => `${String(f.pattern_id ?? "?")}|${String(f.file ?? "?")}`).filter((k) => k !== "?|?");
|
|
18082
18108
|
return [...new Set(keys)].sort().join(",");
|
|
@@ -18086,11 +18112,22 @@ function isSameProblem(previous, current) {
|
|
|
18086
18112
|
const prev = new Set(previous.split(","));
|
|
18087
18113
|
return current.split(",").some((k) => prev.has(k));
|
|
18088
18114
|
}
|
|
18089
|
-
function
|
|
18115
|
+
function writeBlockState(commit, state) {
|
|
18090
18116
|
(0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
18091
|
-
|
|
18092
|
-
|
|
18093
|
-
|
|
18117
|
+
(0, import_node_fs22.writeFileSync)(
|
|
18118
|
+
ITERATION_FILE,
|
|
18119
|
+
JSON.stringify({
|
|
18120
|
+
v: 2,
|
|
18121
|
+
attempts: state.attempts,
|
|
18122
|
+
blocks: state.blocks,
|
|
18123
|
+
commit,
|
|
18124
|
+
ts: Math.floor(Date.now() / 1e3),
|
|
18125
|
+
fingerprint: state.fingerprint ?? void 0
|
|
18126
|
+
})
|
|
18127
|
+
);
|
|
18128
|
+
}
|
|
18129
|
+
function resetBlockState(commit) {
|
|
18130
|
+
writeBlockState(commit, { attempts: 0, blocks: 0, fingerprint: null });
|
|
18094
18131
|
}
|
|
18095
18132
|
|
|
18096
18133
|
// src/lib/fold.ts
|
|
@@ -18282,8 +18319,10 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18282
18319
|
subagentSkipped: 0,
|
|
18283
18320
|
compactions: 0,
|
|
18284
18321
|
complete: false
|
|
18285
|
-
}
|
|
18322
|
+
},
|
|
18323
|
+
planApproval: { approvals: 0, activeSinceLastPrompt: false }
|
|
18286
18324
|
};
|
|
18325
|
+
const flow = { seq: 0, lastPrompt: -1, lastApproval: -1, approvals: 0 };
|
|
18287
18326
|
const byPath = /* @__PURE__ */ new Map();
|
|
18288
18327
|
const commandStats = /* @__PURE__ */ new Map();
|
|
18289
18328
|
const pendingByToolUse = /* @__PURE__ */ new Map();
|
|
@@ -18309,8 +18348,12 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18309
18348
|
if (type === "system" && record.subtype === "compact_boundary") {
|
|
18310
18349
|
result.coverage.compactions++;
|
|
18311
18350
|
}
|
|
18312
|
-
if (type === "user" && hasUserText(record))
|
|
18313
|
-
|
|
18351
|
+
if (type === "user" && hasUserText(record)) {
|
|
18352
|
+
result.coverage.userMessages++;
|
|
18353
|
+
if (owner === "agent") flow.lastPrompt = flow.seq;
|
|
18354
|
+
}
|
|
18355
|
+
if (owner === "agent") flow.seq++;
|
|
18356
|
+
collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, toolStats, pendingToolName, taskById, pendingTaskName, opts.repoRoot, result.coverage, flow);
|
|
18314
18357
|
}
|
|
18315
18358
|
};
|
|
18316
18359
|
try {
|
|
@@ -18382,6 +18425,10 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18382
18425
|
if (!p || authoredPaths.has(p)) continue;
|
|
18383
18426
|
result.unobserved.push({ p, cause: classifyUnobserved(raw) });
|
|
18384
18427
|
}
|
|
18428
|
+
result.planApproval = {
|
|
18429
|
+
approvals: flow.approvals,
|
|
18430
|
+
activeSinceLastPrompt: flow.lastApproval >= 0 && flow.lastApproval > flow.lastPrompt
|
|
18431
|
+
};
|
|
18385
18432
|
return result;
|
|
18386
18433
|
}
|
|
18387
18434
|
function classifyUnobserved(path) {
|
|
@@ -18394,7 +18441,7 @@ function classifyUnobserved(path) {
|
|
|
18394
18441
|
if (/\.(png|jpg|jpeg|gif|pdf|zip|woff2?|ico|mp4)$/i.test(path)) return "binary";
|
|
18395
18442
|
return "no_edit_record";
|
|
18396
18443
|
}
|
|
18397
|
-
function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, toolStats, pendingToolName, taskById, pendingTaskName, repoRoot2, tally) {
|
|
18444
|
+
function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, toolStats, pendingToolName, taskById, pendingTaskName, repoRoot2, tally, flow) {
|
|
18398
18445
|
const message = record.message;
|
|
18399
18446
|
const content = message?.content ?? record.content;
|
|
18400
18447
|
const blocks = Array.isArray(content) ? content : content && typeof content === "object" ? [content] : [];
|
|
@@ -18478,6 +18525,13 @@ function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse
|
|
|
18478
18525
|
}
|
|
18479
18526
|
}
|
|
18480
18527
|
const toolName = id ? pendingToolName.get(id) : void 0;
|
|
18528
|
+
if (toolName === "ExitPlanMode" && flow && block.is_error !== true) {
|
|
18529
|
+
const body = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => typeof c.text === "string" ? c.text : "").join(" ") : "";
|
|
18530
|
+
if (/approved your plan/i.test(body)) {
|
|
18531
|
+
flow.lastApproval = flow.seq;
|
|
18532
|
+
flow.approvals += 1;
|
|
18533
|
+
}
|
|
18534
|
+
}
|
|
18481
18535
|
if (toolName) {
|
|
18482
18536
|
pendingToolName.delete(id);
|
|
18483
18537
|
const prevTool = toolStats.get(toolName);
|
|
@@ -18535,8 +18589,13 @@ function checkConservation(changedFiles, result, repoRoot2) {
|
|
|
18535
18589
|
// src/commands/analyze/phases/06-evidence.ts
|
|
18536
18590
|
async function evidence(run) {
|
|
18537
18591
|
const { opts } = run;
|
|
18538
|
-
const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath } = run;
|
|
18592
|
+
const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath, turnAuthoredCode } = run;
|
|
18539
18593
|
let { analysisMode, earlyFold } = run;
|
|
18594
|
+
const recordFlip = (stage) => {
|
|
18595
|
+
if (run.modeDecision) run.modeDecision = { ...run.modeDecision, resolved: "plan", flip: stage };
|
|
18596
|
+
logEvent("mode_flipped", { stage, to: "plan" });
|
|
18597
|
+
};
|
|
18598
|
+
const planWorthy = !!assistantResponse && !turnAuthoredCode;
|
|
18540
18599
|
let staticResults = {
|
|
18541
18600
|
tool: "@codacy/analysis-cli",
|
|
18542
18601
|
findings: [],
|
|
@@ -18556,8 +18615,9 @@ async function evidence(run) {
|
|
|
18556
18615
|
const debounceSeconds = parseInt(opts.debounce, 10);
|
|
18557
18616
|
const debounceSkip = checkDebounce(debounceSeconds, baselineSessionId);
|
|
18558
18617
|
if (debounceSkip) {
|
|
18559
|
-
if (
|
|
18618
|
+
if (planWorthy) {
|
|
18560
18619
|
analysisMode = "plan";
|
|
18620
|
+
recordFlip("debounce");
|
|
18561
18621
|
} else {
|
|
18562
18622
|
await passAndExit(run, debounceSkip, "debounce");
|
|
18563
18623
|
}
|
|
@@ -18566,8 +18626,9 @@ async function evidence(run) {
|
|
|
18566
18626
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
18567
18627
|
const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles, baselineSessionId);
|
|
18568
18628
|
if (mtimeSkip) {
|
|
18569
|
-
if (
|
|
18629
|
+
if (planWorthy) {
|
|
18570
18630
|
analysisMode = "plan";
|
|
18631
|
+
recordFlip("mtime");
|
|
18571
18632
|
} else {
|
|
18572
18633
|
await passAndExit(run, mtimeSkip, "no-delta-since-last-review");
|
|
18573
18634
|
}
|
|
@@ -18578,8 +18639,9 @@ async function evidence(run) {
|
|
|
18578
18639
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
18579
18640
|
const hashResult = checkContentHash(allCheckable, baselineSessionId);
|
|
18580
18641
|
if (hashResult.skip) {
|
|
18581
|
-
if (
|
|
18642
|
+
if (planWorthy) {
|
|
18582
18643
|
analysisMode = "plan";
|
|
18644
|
+
recordFlip("content-hash");
|
|
18583
18645
|
} else {
|
|
18584
18646
|
await passAndExit(run, hashResult.skip, "no-delta-since-last-review");
|
|
18585
18647
|
}
|
|
@@ -18641,8 +18703,9 @@ async function evidence(run) {
|
|
|
18641
18703
|
maxTotalBytes: parseInt(opts.maxTotalSize, 10)
|
|
18642
18704
|
});
|
|
18643
18705
|
if (codeDelta.files.length === 0 && staticResults.findings.length === 0) {
|
|
18644
|
-
if (
|
|
18706
|
+
if (planWorthy) {
|
|
18645
18707
|
analysisMode = "plan";
|
|
18708
|
+
recordFlip("empty-after-scoping");
|
|
18646
18709
|
} else {
|
|
18647
18710
|
await passAndExit(
|
|
18648
18711
|
run,
|
|
@@ -18662,13 +18725,13 @@ async function evidence(run) {
|
|
|
18662
18725
|
snapshotResult = generateSnapshotDiffs(codeDelta.files);
|
|
18663
18726
|
}
|
|
18664
18727
|
currentCommit = getCurrentCommit();
|
|
18665
|
-
iteration =
|
|
18728
|
+
iteration = readIteration(currentCommit);
|
|
18666
18729
|
}
|
|
18667
18730
|
}
|
|
18668
18731
|
if (analysisMode === "plan") {
|
|
18669
18732
|
recordAnalysisStart();
|
|
18670
18733
|
currentCommit = getCurrentCommit();
|
|
18671
|
-
iteration =
|
|
18734
|
+
iteration = readIteration(currentCommit);
|
|
18672
18735
|
}
|
|
18673
18736
|
Object.assign(run, { analysisMode, codeDelta, contentHash, currentCommit, earlyFold, iteration, snapshotResult, staticResults });
|
|
18674
18737
|
}
|
|
@@ -19566,7 +19629,8 @@ async function buildRequest(run) {
|
|
|
19566
19629
|
}
|
|
19567
19630
|
const noHumanPrompt = (conversation?.prompts?.length ?? 0) === 0;
|
|
19568
19631
|
const w4Task = noHumanPrompt && isExplicitlyAutonomous() ? resolveTaskContext() : null;
|
|
19569
|
-
const
|
|
19632
|
+
const planApprovalActive = foldResult?.planApproval?.activeSinceLastPrompt === true;
|
|
19633
|
+
const hasIntent = (conversation?.prompts?.length ?? 0) > 0 || specs.length > 0 || plans.length > 0 || !!assistantResponse || !!w4Task || planApprovalActive;
|
|
19570
19634
|
if (hasIntent) {
|
|
19571
19635
|
const intentContext = {};
|
|
19572
19636
|
if (conversation && conversation.prompts.length > 0) {
|
|
@@ -19598,6 +19662,10 @@ async function buildRequest(run) {
|
|
|
19598
19662
|
intentContext.user_prompt = w4Task.goal;
|
|
19599
19663
|
logEvent("w4_issue_anchor", { issue: w4Task.number, via: w4Task.via });
|
|
19600
19664
|
}
|
|
19665
|
+
if (planApprovalActive) {
|
|
19666
|
+
intentContext.plan_approved = true;
|
|
19667
|
+
logEvent("plan_approval_carried", { approvals: foldResult.planApproval.approvals });
|
|
19668
|
+
}
|
|
19601
19669
|
if (assistantResponse) {
|
|
19602
19670
|
const cap = analysisMode === "plan" ? MAX_ASSISTANT_RESPONSE_CHARS_PLAN : MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT;
|
|
19603
19671
|
intentContext.assistant_response = assistantResponse.length > cap ? assistantResponse.slice(0, cap) : assistantResponse;
|
|
@@ -19861,6 +19929,11 @@ async function reconcile(run) {
|
|
|
19861
19929
|
decision,
|
|
19862
19930
|
branch: getCurrentBranch(),
|
|
19863
19931
|
watermarkSha: watermarkIsPartial ? null : watermarkHash,
|
|
19932
|
+
// The byte witness — the same "only honest definition of reviewed" the
|
|
19933
|
+
// coverage column uses. A finding on a path outside this set records no
|
|
19934
|
+
// statement (plan-mode prose anchored to unsent files must not become
|
|
19935
|
+
// "STILL OPEN … the tree is not clean").
|
|
19936
|
+
sentPaths,
|
|
19864
19937
|
findings: response.findings?.map((f) => ({
|
|
19865
19938
|
file: f.file,
|
|
19866
19939
|
line: f.line,
|
|
@@ -19927,6 +20000,39 @@ ${YELLOW2}${note}${NC2}
|
|
|
19927
20000
|
return exit(0);
|
|
19928
20001
|
}
|
|
19929
20002
|
|
|
20003
|
+
// src/lib/may-block.ts
|
|
20004
|
+
var HARD_BLOCK_CEILING = 5;
|
|
20005
|
+
function mayBlock(input) {
|
|
20006
|
+
const ceiling = input.ceiling ?? HARD_BLOCK_CEILING;
|
|
20007
|
+
if (input.reviewedFileCount === 0 && input.staticFindingCount === 0) {
|
|
20008
|
+
return { block: false, release: "no-code-reviewed" };
|
|
20009
|
+
}
|
|
20010
|
+
if (input.cycleCutFired) {
|
|
20011
|
+
return { block: false, release: "nothing-moved" };
|
|
20012
|
+
}
|
|
20013
|
+
if (input.attempts > input.maxIterations) {
|
|
20014
|
+
return { block: false, release: "same-problem-cap" };
|
|
20015
|
+
}
|
|
20016
|
+
if (input.blocks > ceiling) {
|
|
20017
|
+
return { block: false, release: "block-ceiling" };
|
|
20018
|
+
}
|
|
20019
|
+
return { block: true, release: null };
|
|
20020
|
+
}
|
|
20021
|
+
function describeRelease(release, input) {
|
|
20022
|
+
const ceiling = input.ceiling ?? HARD_BLOCK_CEILING;
|
|
20023
|
+
const open = input.findingCount > 0 ? `${input.findingCount} finding(s) remain OPEN and were NOT fixed. Human review required before deploying.` : "Human review required before deploying.";
|
|
20024
|
+
switch (release) {
|
|
20025
|
+
case "no-code-reviewed":
|
|
20026
|
+
return `Verity: WARN \u2014 NOT BLOCKING: no code was reviewed on this turn, so there is nothing here to fix. Reported as advice instead. ${open}`;
|
|
20027
|
+
case "nothing-moved":
|
|
20028
|
+
return `Verity: WARN \u2014 NOT BLOCKING: nothing has changed since the last verdict, so re-raising it cannot move anything forward. ${open}`;
|
|
20029
|
+
case "same-problem-cap":
|
|
20030
|
+
return `Verity: WARN \u2014 self-healing limit (${input.maxIterations}) reached on the same finding. NO LONGER BLOCKING, but ${open}`;
|
|
20031
|
+
case "block-ceiling":
|
|
20032
|
+
return `Verity: WARN \u2014 ${ceiling} consecutive blocking verdicts reached; releasing the block so this cannot loop. ${open}`;
|
|
20033
|
+
}
|
|
20034
|
+
}
|
|
20035
|
+
|
|
19930
20036
|
// src/lib/remediation-guard.ts
|
|
19931
20037
|
var TOOL_CONFIG_PATTERNS = [
|
|
19932
20038
|
/(^|\/)\.codacy\//,
|
|
@@ -20075,38 +20181,63 @@ async function render(run) {
|
|
|
20075
20181
|
reverify_by: response.reverify_by
|
|
20076
20182
|
});
|
|
20077
20183
|
const grantNudge = grantWarning ? ` \u26A0\uFE0F ${grantWarning}` : "";
|
|
20078
|
-
let
|
|
20184
|
+
let release = null;
|
|
20079
20185
|
let effectiveDecision = decision;
|
|
20080
20186
|
if (decision === "FAIL") {
|
|
20081
|
-
const
|
|
20187
|
+
const findings = response.findings ?? [];
|
|
20188
|
+
const blocking = findings.filter((f) => {
|
|
20082
20189
|
const sev = String(f.severity ?? "").toLowerCase();
|
|
20083
20190
|
return sev === "critical" || sev === "high";
|
|
20084
20191
|
});
|
|
20085
20192
|
const fingerprint = findingsFingerprint(blocking);
|
|
20086
|
-
const prior =
|
|
20193
|
+
const prior = readBlockState(currentCommit, {
|
|
20194
|
+
newUserPrompt: (conversation?.prompts?.length ?? 0) > 0
|
|
20195
|
+
});
|
|
20087
20196
|
const sameProblem = isSameProblem(prior.fingerprint, fingerprint);
|
|
20088
|
-
const nextIteration = sameProblem ? prior.iteration + 1 : 1;
|
|
20089
20197
|
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
20090
|
-
|
|
20091
|
-
|
|
20092
|
-
|
|
20093
|
-
|
|
20198
|
+
const attempts = sameProblem ? prior.attempts + 1 : 1;
|
|
20199
|
+
const blocks = prior.blocks + 1;
|
|
20200
|
+
const decisionNow = mayBlock({
|
|
20201
|
+
reviewedFileCount: codeDelta.files.length,
|
|
20202
|
+
staticFindingCount: run.staticResults?.findings?.length ?? 0,
|
|
20203
|
+
cycleCutFired: silenced !== null,
|
|
20204
|
+
attempts,
|
|
20205
|
+
blocks,
|
|
20206
|
+
maxIterations
|
|
20207
|
+
});
|
|
20208
|
+
if (decisionNow.block) {
|
|
20209
|
+
writeBlockState(currentCommit, { attempts, blocks, fingerprint });
|
|
20210
|
+
iteration = attempts;
|
|
20211
|
+
} else {
|
|
20212
|
+
release = decisionNow.release;
|
|
20094
20213
|
effectiveDecision = "WARN";
|
|
20095
|
-
logEvent("
|
|
20214
|
+
logEvent("block_released", {
|
|
20215
|
+
reason: release,
|
|
20216
|
+
attempts,
|
|
20217
|
+
blocks,
|
|
20218
|
+
reviewed_files: codeDelta.files.length,
|
|
20219
|
+
cycle_cut: silenced,
|
|
20220
|
+
fingerprint
|
|
20221
|
+
});
|
|
20096
20222
|
}
|
|
20097
20223
|
}
|
|
20098
|
-
if (
|
|
20224
|
+
if (release) {
|
|
20099
20225
|
const findings = response.findings ?? [];
|
|
20100
20226
|
const lines = findings.slice(0, 5).map((f) => ` [${String(f.severity ?? "?").toUpperCase()}] ${String(f.title ?? f.message ?? "")} (${String(f.file ?? "?")}:${String(f.line ?? "?")})`);
|
|
20227
|
+
const summary = describeRelease(release, {
|
|
20228
|
+
findingCount: findings.length,
|
|
20229
|
+
maxIterations: parseInt(opts.maxIterations, 10)
|
|
20230
|
+
});
|
|
20101
20231
|
emitVerdict({
|
|
20102
20232
|
proposed: "WARN",
|
|
20103
20233
|
changed: run.changedUniverse,
|
|
20104
20234
|
coverage: reviewCoverage,
|
|
20105
|
-
userSummary:
|
|
20106
|
-
${lines.join("\n")}
|
|
20235
|
+
userSummary: lines.length > 0 ? `${summary}
|
|
20236
|
+
${lines.join("\n")}` : summary,
|
|
20107
20237
|
agentContext: null,
|
|
20108
20238
|
silenced: true
|
|
20109
20239
|
});
|
|
20240
|
+
return;
|
|
20110
20241
|
}
|
|
20111
20242
|
switch (effectiveDecision) {
|
|
20112
20243
|
case "FAIL": {
|
|
@@ -20205,7 +20336,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
20205
20336
|
break;
|
|
20206
20337
|
}
|
|
20207
20338
|
case "PASS": {
|
|
20208
|
-
|
|
20339
|
+
resetBlockState(currentCommit);
|
|
20209
20340
|
if (watermarkHash) recordPassHash(watermarkHash, baselineSessionId);
|
|
20210
20341
|
if (!watermarkIsPartial && currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
20211
20342
|
let userSummary = response.user_summary ?? "Verity: PASS";
|
|
@@ -20226,6 +20357,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
20226
20357
|
break;
|
|
20227
20358
|
}
|
|
20228
20359
|
case "WARN": {
|
|
20360
|
+
if (decision !== "FAIL") resetBlockState(currentCommit);
|
|
20229
20361
|
if (watermarkHash) recordPassHash(watermarkHash, baselineSessionId);
|
|
20230
20362
|
if (!watermarkIsPartial && currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
20231
20363
|
let userSummary = response.user_summary ?? "Verity: WARN";
|
|
@@ -22114,8 +22246,8 @@ function registerTelemetryCommands(program2) {
|
|
|
22114
22246
|
}
|
|
22115
22247
|
|
|
22116
22248
|
// src/cli.ts
|
|
22117
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.29.4-experimental.
|
|
22118
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.29.4-experimental.
|
|
22249
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.29.4-experimental.2ae1813").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
|
|
22250
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.29.4-experimental.2ae1813");
|
|
22119
22251
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
22120
22252
|
try {
|
|
22121
22253
|
await foldLegacyLocalCredential();
|
package/package.json
CHANGED