@codacy/verity-cli 0.29.4-experimental.0d2e4a3 → 0.29.4-experimental.31b8ab8
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 +178 -47
- 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.31b8ab8" : "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
|
}
|
|
@@ -19598,6 +19661,10 @@ async function buildRequest(run) {
|
|
|
19598
19661
|
intentContext.user_prompt = w4Task.goal;
|
|
19599
19662
|
logEvent("w4_issue_anchor", { issue: w4Task.number, via: w4Task.via });
|
|
19600
19663
|
}
|
|
19664
|
+
if (foldResult?.planApproval?.activeSinceLastPrompt && intentContext.user_prompt) {
|
|
19665
|
+
intentContext.plan_approved = true;
|
|
19666
|
+
logEvent("plan_approval_carried", { approvals: foldResult.planApproval.approvals });
|
|
19667
|
+
}
|
|
19601
19668
|
if (assistantResponse) {
|
|
19602
19669
|
const cap = analysisMode === "plan" ? MAX_ASSISTANT_RESPONSE_CHARS_PLAN : MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT;
|
|
19603
19670
|
intentContext.assistant_response = assistantResponse.length > cap ? assistantResponse.slice(0, cap) : assistantResponse;
|
|
@@ -19861,6 +19928,11 @@ async function reconcile(run) {
|
|
|
19861
19928
|
decision,
|
|
19862
19929
|
branch: getCurrentBranch(),
|
|
19863
19930
|
watermarkSha: watermarkIsPartial ? null : watermarkHash,
|
|
19931
|
+
// The byte witness — the same "only honest definition of reviewed" the
|
|
19932
|
+
// coverage column uses. A finding on a path outside this set records no
|
|
19933
|
+
// statement (plan-mode prose anchored to unsent files must not become
|
|
19934
|
+
// "STILL OPEN … the tree is not clean").
|
|
19935
|
+
sentPaths,
|
|
19864
19936
|
findings: response.findings?.map((f) => ({
|
|
19865
19937
|
file: f.file,
|
|
19866
19938
|
line: f.line,
|
|
@@ -19927,6 +19999,39 @@ ${YELLOW2}${note}${NC2}
|
|
|
19927
19999
|
return exit(0);
|
|
19928
20000
|
}
|
|
19929
20001
|
|
|
20002
|
+
// src/lib/may-block.ts
|
|
20003
|
+
var HARD_BLOCK_CEILING = 5;
|
|
20004
|
+
function mayBlock(input) {
|
|
20005
|
+
const ceiling = input.ceiling ?? HARD_BLOCK_CEILING;
|
|
20006
|
+
if (input.reviewedFileCount === 0 && input.staticFindingCount === 0) {
|
|
20007
|
+
return { block: false, release: "no-code-reviewed" };
|
|
20008
|
+
}
|
|
20009
|
+
if (input.cycleCutFired) {
|
|
20010
|
+
return { block: false, release: "nothing-moved" };
|
|
20011
|
+
}
|
|
20012
|
+
if (input.attempts > input.maxIterations) {
|
|
20013
|
+
return { block: false, release: "same-problem-cap" };
|
|
20014
|
+
}
|
|
20015
|
+
if (input.blocks > ceiling) {
|
|
20016
|
+
return { block: false, release: "block-ceiling" };
|
|
20017
|
+
}
|
|
20018
|
+
return { block: true, release: null };
|
|
20019
|
+
}
|
|
20020
|
+
function describeRelease(release, input) {
|
|
20021
|
+
const ceiling = input.ceiling ?? HARD_BLOCK_CEILING;
|
|
20022
|
+
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.";
|
|
20023
|
+
switch (release) {
|
|
20024
|
+
case "no-code-reviewed":
|
|
20025
|
+
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}`;
|
|
20026
|
+
case "nothing-moved":
|
|
20027
|
+
return `Verity: WARN \u2014 NOT BLOCKING: nothing has changed since the last verdict, so re-raising it cannot move anything forward. ${open}`;
|
|
20028
|
+
case "same-problem-cap":
|
|
20029
|
+
return `Verity: WARN \u2014 self-healing limit (${input.maxIterations}) reached on the same finding. NO LONGER BLOCKING, but ${open}`;
|
|
20030
|
+
case "block-ceiling":
|
|
20031
|
+
return `Verity: WARN \u2014 ${ceiling} consecutive blocking verdicts reached; releasing the block so this cannot loop. ${open}`;
|
|
20032
|
+
}
|
|
20033
|
+
}
|
|
20034
|
+
|
|
19930
20035
|
// src/lib/remediation-guard.ts
|
|
19931
20036
|
var TOOL_CONFIG_PATTERNS = [
|
|
19932
20037
|
/(^|\/)\.codacy\//,
|
|
@@ -20075,38 +20180,63 @@ async function render(run) {
|
|
|
20075
20180
|
reverify_by: response.reverify_by
|
|
20076
20181
|
});
|
|
20077
20182
|
const grantNudge = grantWarning ? ` \u26A0\uFE0F ${grantWarning}` : "";
|
|
20078
|
-
let
|
|
20183
|
+
let release = null;
|
|
20079
20184
|
let effectiveDecision = decision;
|
|
20080
20185
|
if (decision === "FAIL") {
|
|
20081
|
-
const
|
|
20186
|
+
const findings = response.findings ?? [];
|
|
20187
|
+
const blocking = findings.filter((f) => {
|
|
20082
20188
|
const sev = String(f.severity ?? "").toLowerCase();
|
|
20083
20189
|
return sev === "critical" || sev === "high";
|
|
20084
20190
|
});
|
|
20085
20191
|
const fingerprint = findingsFingerprint(blocking);
|
|
20086
|
-
const prior =
|
|
20192
|
+
const prior = readBlockState(currentCommit, {
|
|
20193
|
+
newUserPrompt: (conversation?.prompts?.length ?? 0) > 0
|
|
20194
|
+
});
|
|
20087
20195
|
const sameProblem = isSameProblem(prior.fingerprint, fingerprint);
|
|
20088
|
-
const nextIteration = sameProblem ? prior.iteration + 1 : 1;
|
|
20089
20196
|
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
20090
|
-
|
|
20091
|
-
|
|
20092
|
-
|
|
20093
|
-
|
|
20197
|
+
const attempts = sameProblem ? prior.attempts + 1 : 1;
|
|
20198
|
+
const blocks = prior.blocks + 1;
|
|
20199
|
+
const decisionNow = mayBlock({
|
|
20200
|
+
reviewedFileCount: codeDelta.files.length,
|
|
20201
|
+
staticFindingCount: run.staticResults?.findings?.length ?? 0,
|
|
20202
|
+
cycleCutFired: silenced !== null,
|
|
20203
|
+
attempts,
|
|
20204
|
+
blocks,
|
|
20205
|
+
maxIterations
|
|
20206
|
+
});
|
|
20207
|
+
if (decisionNow.block) {
|
|
20208
|
+
writeBlockState(currentCommit, { attempts, blocks, fingerprint });
|
|
20209
|
+
iteration = attempts;
|
|
20210
|
+
} else {
|
|
20211
|
+
release = decisionNow.release;
|
|
20094
20212
|
effectiveDecision = "WARN";
|
|
20095
|
-
logEvent("
|
|
20213
|
+
logEvent("block_released", {
|
|
20214
|
+
reason: release,
|
|
20215
|
+
attempts,
|
|
20216
|
+
blocks,
|
|
20217
|
+
reviewed_files: codeDelta.files.length,
|
|
20218
|
+
cycle_cut: silenced,
|
|
20219
|
+
fingerprint
|
|
20220
|
+
});
|
|
20096
20221
|
}
|
|
20097
20222
|
}
|
|
20098
|
-
if (
|
|
20223
|
+
if (release) {
|
|
20099
20224
|
const findings = response.findings ?? [];
|
|
20100
20225
|
const lines = findings.slice(0, 5).map((f) => ` [${String(f.severity ?? "?").toUpperCase()}] ${String(f.title ?? f.message ?? "")} (${String(f.file ?? "?")}:${String(f.line ?? "?")})`);
|
|
20226
|
+
const summary = describeRelease(release, {
|
|
20227
|
+
findingCount: findings.length,
|
|
20228
|
+
maxIterations: parseInt(opts.maxIterations, 10)
|
|
20229
|
+
});
|
|
20101
20230
|
emitVerdict({
|
|
20102
20231
|
proposed: "WARN",
|
|
20103
20232
|
changed: run.changedUniverse,
|
|
20104
20233
|
coverage: reviewCoverage,
|
|
20105
|
-
userSummary:
|
|
20106
|
-
${lines.join("\n")}
|
|
20234
|
+
userSummary: lines.length > 0 ? `${summary}
|
|
20235
|
+
${lines.join("\n")}` : summary,
|
|
20107
20236
|
agentContext: null,
|
|
20108
20237
|
silenced: true
|
|
20109
20238
|
});
|
|
20239
|
+
return;
|
|
20110
20240
|
}
|
|
20111
20241
|
switch (effectiveDecision) {
|
|
20112
20242
|
case "FAIL": {
|
|
@@ -20205,7 +20335,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
20205
20335
|
break;
|
|
20206
20336
|
}
|
|
20207
20337
|
case "PASS": {
|
|
20208
|
-
|
|
20338
|
+
resetBlockState(currentCommit);
|
|
20209
20339
|
if (watermarkHash) recordPassHash(watermarkHash, baselineSessionId);
|
|
20210
20340
|
if (!watermarkIsPartial && currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
20211
20341
|
let userSummary = response.user_summary ?? "Verity: PASS";
|
|
@@ -20226,6 +20356,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
20226
20356
|
break;
|
|
20227
20357
|
}
|
|
20228
20358
|
case "WARN": {
|
|
20359
|
+
if (decision !== "FAIL") resetBlockState(currentCommit);
|
|
20229
20360
|
if (watermarkHash) recordPassHash(watermarkHash, baselineSessionId);
|
|
20230
20361
|
if (!watermarkIsPartial && currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
20231
20362
|
let userSummary = response.user_summary ?? "Verity: WARN";
|
|
@@ -22114,8 +22245,8 @@ function registerTelemetryCommands(program2) {
|
|
|
22114
22245
|
}
|
|
22115
22246
|
|
|
22116
22247
|
// 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.
|
|
22248
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.29.4-experimental.31b8ab8").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) => {
|
|
22249
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.29.4-experimental.31b8ab8");
|
|
22119
22250
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
22120
22251
|
try {
|
|
22121
22252
|
await foldLegacyLocalCredential();
|
package/package.json
CHANGED