@codacy/verity-cli 0.34.0-experimental.7e3f52d → 0.34.0-experimental.8b93a3c
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/CHANGELOG.md +77 -0
- package/bin/verity.js +125 -93
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -64,6 +64,78 @@ installs are untouched.
|
|
|
64
64
|
which is the ordinary case for someone whose first Verity install *is* the
|
|
65
65
|
plugin.
|
|
66
66
|
|
|
67
|
+
## [0.34.0] — 2026-09-30
|
|
68
|
+
|
|
69
|
+
**Formal verification at push: Verity can now prove a bug is real before it
|
|
70
|
+
blocks — and the code map and the knowledge loop tell the reviewer more about
|
|
71
|
+
the code around a change.**
|
|
72
|
+
|
|
73
|
+
### 🧮 Formal verification, opt-in
|
|
74
|
+
|
|
75
|
+
A third review at `git push`, next to the one-shot and the agent. It picks the
|
|
76
|
+
riskiest function the push changed, has the Verity service write a small Lean 4
|
|
77
|
+
model of it, checks the model on your machine, and replays any counterexample
|
|
78
|
+
on the real code in an isolated container with the network off. **Only a
|
|
79
|
+
reproduced bug blocks;** a counterexample that does not reproduce on the real
|
|
80
|
+
code is reported, never enforced. Most pushes get no model at all.
|
|
81
|
+
|
|
82
|
+
- **Off until you say yes.** `verity init` asks once, on a terminal, with "no"
|
|
83
|
+
as the default, and remembers either answer. `--yes` and headless runs are not
|
|
84
|
+
asked and never probe for a toolchain. `verity config formal push` turns it on
|
|
85
|
+
for a checkout, `verity config formal off` turns it off. It needs you to be
|
|
86
|
+
signed in: ranking, model writing and replay run on the Verity service. The
|
|
87
|
+
function's source is sent for the replay; no code is stored.
|
|
88
|
+
- **`verity lean install | status | remove`** manages the pinned official Lean 4
|
|
89
|
+
release under `~/.verity/lean` (≈800 MB download, 2.7 GB on disk). The
|
|
90
|
+
download is checksum-verified, abandoned after 60 s with nothing received,
|
|
91
|
+
and refused up front when the disk is short. An elan install is used when it
|
|
92
|
+
already has the pinned toolchain; Verity never makes elan download one.
|
|
93
|
+
- **On demand:** `verity prove candidates | generate | check | replay | auto`,
|
|
94
|
+
and the `/verity-prove <worry>` skill, where the words after the command are
|
|
95
|
+
what you want checked. Under the plugin, `/verity:verity-prove` arrives with
|
|
96
|
+
the plugin's next release.
|
|
97
|
+
- **A finding holds until the code changes.** Pushing the same bytes again
|
|
98
|
+
re-blocks without a new model. `verity prove waive <file>` accepts the risk
|
|
99
|
+
for the file's current bytes, and voids when they change.
|
|
100
|
+
- **Yours, never the repository's.** A `.verity/config.json` that git tracks,
|
|
101
|
+
or that is reached through a symlink, cannot switch formal verification on —
|
|
102
|
+
`verity doctor`, `verity config formal` and `verity init` say why it is off.
|
|
103
|
+
Findings and spend records a repository ships are ignored, and Lean never runs
|
|
104
|
+
on a `.verity/proofs/` workspace the repository supplies.
|
|
105
|
+
- **Admins can switch it off** per project, organization or globally with the
|
|
106
|
+
`formal` flag in the dashboard; the push then reports `disabled-by-server`.
|
|
107
|
+
|
|
108
|
+
### 🗺️ The code map
|
|
109
|
+
|
|
110
|
+
The CLI now parses TypeScript, TSX, JavaScript, Python, Go and Java with bundled
|
|
111
|
+
tree-sitter grammars (about 6 MB added to the package).
|
|
112
|
+
|
|
113
|
+
- **The reviewer sees what else touches the same state.** At commit and push,
|
|
114
|
+
the review's context names the other functions that write the state a change
|
|
115
|
+
writes, and the agent reviewer can ask for a function's callers and
|
|
116
|
+
definitions. Formal verification models the exact function, not a guess at
|
|
117
|
+
its extent.
|
|
118
|
+
- **Reads use the bytes under review:** at a push, the pushed commit; at a
|
|
119
|
+
commit, the staged files — not whatever is in the working tree.
|
|
120
|
+
- **Paths the project denies are never shown** by the code tools or in the
|
|
121
|
+
code-map context.
|
|
122
|
+
|
|
123
|
+
### 🧠 The knowledge loop
|
|
124
|
+
|
|
125
|
+
- **The reviewer says what it did with each node it was shown** — applied,
|
|
126
|
+
violated, irrelevant or unclear, each tied to a line in the change — and
|
|
127
|
+
ranking learns from it which nodes help. One scorer now chooses the nodes for
|
|
128
|
+
both the review and your agent's prompt.
|
|
129
|
+
- **A known rule said back to the agent.** When a change breaks a rule the
|
|
130
|
+
project has already learned, Verity can tell the agent which one. This ships
|
|
131
|
+
in shadow: the decision is made and recorded, and nothing is sent.
|
|
132
|
+
- **Fewer, better nodes.** The extractor's rubric was rewritten from 143
|
|
133
|
+
human-graded nodes; it no longer records what the code already says, and it
|
|
134
|
+
checks each new node against the nearest existing ones before adding it.
|
|
135
|
+
- **A failed diff in `verity memory` sync no longer drops a node edit you made.**
|
|
136
|
+
- **A failed knowledge lookup no longer fails a review.** The review runs
|
|
137
|
+
without knowledge and records that it did.
|
|
138
|
+
|
|
67
139
|
### 🎯 `/verity:init` does everything `verity init` does, in the session
|
|
68
140
|
|
|
69
141
|
Under the plugin, setup used to stop short of the terminal flow: `/verity:init`
|
|
@@ -89,6 +161,11 @@ sign-in meant leaving Claude Code for a terminal.
|
|
|
89
161
|
- **`verity telemetry install|uninstall` update `.verity/setup.json`**, so a
|
|
90
162
|
request deferred until sign-in stops being reported as still to do.
|
|
91
163
|
|
|
164
|
+
### 🔧 Fixed
|
|
165
|
+
|
|
166
|
+
- **A swapped `.verity` directory cannot redirect a write.** File writes check
|
|
167
|
+
the file they opened is the one they meant to write, after opening it.
|
|
168
|
+
|
|
92
169
|
## [0.33.3] — 2026-09-24
|
|
93
170
|
|
|
94
171
|
**The knowledge seed runs once per project, not once per checkout.**
|
package/bin/verity.js
CHANGED
|
@@ -17940,7 +17940,7 @@ function registerHooksCommands(program2) {
|
|
|
17940
17940
|
}
|
|
17941
17941
|
|
|
17942
17942
|
// src/commands/intent.ts
|
|
17943
|
-
var
|
|
17943
|
+
var import_node_crypto10 = require("node:crypto");
|
|
17944
17944
|
|
|
17945
17945
|
// src/lib/conversation-buffer.ts
|
|
17946
17946
|
var import_promises5 = require("node:fs/promises");
|
|
@@ -20544,7 +20544,7 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
|
|
|
20544
20544
|
|
|
20545
20545
|
// src/lib/dossier-session.ts
|
|
20546
20546
|
var import_node_fs26 = require("node:fs");
|
|
20547
|
-
var
|
|
20547
|
+
var import_node_crypto9 = require("node:crypto");
|
|
20548
20548
|
var import_node_path22 = require("node:path");
|
|
20549
20549
|
|
|
20550
20550
|
// src/lib/pending-repeat.ts
|
|
@@ -20621,6 +20621,7 @@ function isRepeatOfAny(text, priorFingerprints, threshold = REPEAT_THRESHOLD) {
|
|
|
20621
20621
|
}
|
|
20622
20622
|
|
|
20623
20623
|
// src/lib/dossier/bounds.ts
|
|
20624
|
+
var import_node_crypto5 = require("node:crypto");
|
|
20624
20625
|
var MAX_LINE_BYTES = 4096;
|
|
20625
20626
|
var MAX_GOAL_CHARS = 2e3;
|
|
20626
20627
|
var GOAL_KEEP = 8;
|
|
@@ -20632,10 +20633,17 @@ var NOT_MINE_CAP = 300;
|
|
|
20632
20633
|
var STATEMENTS_OPEN_CAP = 40;
|
|
20633
20634
|
var COMMANDS_CAP = 12;
|
|
20634
20635
|
var DIVERGENCE_CAP = 10;
|
|
20635
|
-
var
|
|
20636
|
+
var DIVERGENCE_BULK_AT = 300;
|
|
20637
|
+
var DIVERGENCE_KEYS_PER_LINE = 200;
|
|
20636
20638
|
function divergenceKey(path, hash) {
|
|
20637
20639
|
return `${path}@${hash ?? "unknown"}`;
|
|
20638
20640
|
}
|
|
20641
|
+
function packedDivergenceDigest(key) {
|
|
20642
|
+
return (0, import_node_crypto5.createHash)("sha256").update(key).digest("hex").slice(0, 12);
|
|
20643
|
+
}
|
|
20644
|
+
function packedDivergenceKey(digest) {
|
|
20645
|
+
return `#${digest}`;
|
|
20646
|
+
}
|
|
20639
20647
|
var MARKS_CAP = 50;
|
|
20640
20648
|
var ROTATE_BYTES = 1048576;
|
|
20641
20649
|
var ROTATE_KEEP = 2;
|
|
@@ -20649,7 +20657,7 @@ function statementAnchorKey(file, patternId) {
|
|
|
20649
20657
|
}
|
|
20650
20658
|
|
|
20651
20659
|
// src/lib/dossier/log.ts
|
|
20652
|
-
var
|
|
20660
|
+
var import_node_crypto6 = require("node:crypto");
|
|
20653
20661
|
var import_node_fs22 = require("node:fs");
|
|
20654
20662
|
var import_node_path20 = require("node:path");
|
|
20655
20663
|
var CRC_TABLE = (() => {
|
|
@@ -20691,7 +20699,7 @@ function nextSeq() {
|
|
|
20691
20699
|
function encodeEvent(ev) {
|
|
20692
20700
|
const full = {
|
|
20693
20701
|
v: 1,
|
|
20694
|
-
id: (0,
|
|
20702
|
+
id: (0, import_node_crypto6.randomBytes)(4).toString("hex"),
|
|
20695
20703
|
...ev
|
|
20696
20704
|
};
|
|
20697
20705
|
let body2 = JSON.stringify(full);
|
|
@@ -20762,7 +20770,7 @@ function rotateIfNeeded2(d) {
|
|
|
20762
20770
|
}
|
|
20763
20771
|
|
|
20764
20772
|
// src/lib/dossier/fold-dossier.ts
|
|
20765
|
-
var
|
|
20773
|
+
var import_node_crypto7 = require("node:crypto");
|
|
20766
20774
|
var import_node_fs23 = require("node:fs");
|
|
20767
20775
|
var import_node_path21 = require("node:path");
|
|
20768
20776
|
var EMPTY_CAPABILITIES = () => ({
|
|
@@ -20854,7 +20862,7 @@ function reduce(state, events, now) {
|
|
|
20854
20862
|
state.meta.seq = Math.max(state.meta.seq, ev.seq);
|
|
20855
20863
|
switch (ev.k) {
|
|
20856
20864
|
case "goal": {
|
|
20857
|
-
const hash = (0,
|
|
20865
|
+
const hash = (0, import_node_crypto7.createHash)("sha256").update(ev.text).digest("hex").slice(0, 16);
|
|
20858
20866
|
const prev = state.goal[state.goal.length - 1];
|
|
20859
20867
|
if (prev && prev.hash === hash) {
|
|
20860
20868
|
prev.repeats = (prev.repeats ?? 0) + 1;
|
|
@@ -20982,12 +20990,13 @@ function reduce(state, events, now) {
|
|
|
20982
20990
|
break;
|
|
20983
20991
|
}
|
|
20984
20992
|
case "divergence": {
|
|
20985
|
-
state.divergence.counts[ev.kind] = (state.divergence.counts[ev.kind] ?? 0) + 1;
|
|
20986
20993
|
state.divergence.events.push({ kind: ev.kind, path: ev.path, at: ev.at });
|
|
20987
|
-
const
|
|
20988
|
-
|
|
20994
|
+
const keys = Array.isArray(ev.keys) ? ev.keys.filter((k) => typeof k === "string").map(packedDivergenceKey) : [divergenceKey(ev.path, ev.hash ?? null)];
|
|
20995
|
+
for (const key of keys) {
|
|
20996
|
+
if (seenDivergence.has(key)) continue;
|
|
20989
20997
|
seenDivergence.add(key);
|
|
20990
|
-
|
|
20998
|
+
state.divergence.seen.push(key);
|
|
20999
|
+
state.divergence.counts[ev.kind] = (state.divergence.counts[ev.kind] ?? 0) + 1;
|
|
20991
21000
|
}
|
|
20992
21001
|
break;
|
|
20993
21002
|
}
|
|
@@ -21294,14 +21303,14 @@ function assessContinuity(i2) {
|
|
|
21294
21303
|
}
|
|
21295
21304
|
|
|
21296
21305
|
// src/lib/dossier/reanchor.ts
|
|
21297
|
-
var
|
|
21306
|
+
var import_node_crypto8 = require("node:crypto");
|
|
21298
21307
|
var import_node_fs25 = require("node:fs");
|
|
21299
21308
|
function lineSha(text) {
|
|
21300
|
-
return (0,
|
|
21309
|
+
return (0, import_node_crypto8.createHash)("sha256").update(text.trim()).digest("hex").slice(0, HASH_WIDTH);
|
|
21301
21310
|
}
|
|
21302
21311
|
function fileHash(path) {
|
|
21303
21312
|
try {
|
|
21304
|
-
return (0,
|
|
21313
|
+
return (0, import_node_crypto8.createHash)("sha256").update((0, import_node_fs25.readFileSync)(path)).digest("hex").slice(0, HASH_WIDTH);
|
|
21305
21314
|
} catch {
|
|
21306
21315
|
return null;
|
|
21307
21316
|
}
|
|
@@ -21798,7 +21807,7 @@ function recordTurn(d, t) {
|
|
|
21798
21807
|
// Identity is the COMMAND, not its category — otherwise `npm test -- a` and
|
|
21799
21808
|
// `npm test -- b` are one row and the streak counts two different commands
|
|
21800
21809
|
// as a run of one.
|
|
21801
|
-
cmd_hash: (0,
|
|
21810
|
+
cmd_hash: (0, import_node_crypto9.createHash)("sha256").update(c.head || c.class).digest("hex").slice(0, 12),
|
|
21802
21811
|
head: c.head || c.class,
|
|
21803
21812
|
status: toStatus(c.last_status)
|
|
21804
21813
|
});
|
|
@@ -21816,10 +21825,23 @@ function recordTurn(d, t) {
|
|
|
21816
21825
|
}
|
|
21817
21826
|
}
|
|
21818
21827
|
const seenDivergence = t.known?.divergence ?? /* @__PURE__ */ new Set();
|
|
21828
|
+
const fresh = [];
|
|
21819
21829
|
for (const u of t.unobserved) {
|
|
21820
21830
|
const hash = fileHash((0, import_node_path22.join)(root, u.p));
|
|
21821
|
-
|
|
21822
|
-
|
|
21831
|
+
const key = divergenceKey(u.p, hash);
|
|
21832
|
+
const digest = packedDivergenceDigest(key);
|
|
21833
|
+
if (seenDivergence.has(key) || seenDivergence.has(packedDivergenceKey(digest))) continue;
|
|
21834
|
+
fresh.push({ path: u.p, hash, digest });
|
|
21835
|
+
}
|
|
21836
|
+
if (fresh.length > DIVERGENCE_BULK_AT) {
|
|
21837
|
+
for (let i2 = 0; i2 < fresh.length; i2 += DIVERGENCE_KEYS_PER_LINE) {
|
|
21838
|
+
const slice2 = fresh.slice(i2, i2 + DIVERGENCE_KEYS_PER_LINE);
|
|
21839
|
+
appendEvent(d, { k: "divergence", kind: "external_mutation", path: slice2[0].path, keys: slice2.map((f) => f.digest) });
|
|
21840
|
+
}
|
|
21841
|
+
} else {
|
|
21842
|
+
for (const f of fresh) {
|
|
21843
|
+
appendEvent(d, { k: "divergence", kind: "external_mutation", path: f.path, hash: f.hash });
|
|
21844
|
+
}
|
|
21823
21845
|
}
|
|
21824
21846
|
const newCompactions = (t.compactions ?? 0) - (t.known?.compactions ?? 0);
|
|
21825
21847
|
for (let i2 = 0; i2 < newCompactions; i2++) {
|
|
@@ -22084,7 +22106,7 @@ async function fireClassify(prompt, sessionId, globals) {
|
|
|
22084
22106
|
logEvent("classify_skipped", { reason: "no_service_url", detail: urlResult.error });
|
|
22085
22107
|
return;
|
|
22086
22108
|
}
|
|
22087
|
-
const promptHash = (0,
|
|
22109
|
+
const promptHash = (0, import_node_crypto10.createHash)("sha256").update(prompt).digest("hex");
|
|
22088
22110
|
const result = await apiRequest({
|
|
22089
22111
|
method: "POST",
|
|
22090
22112
|
path: "/classify-task",
|
|
@@ -22128,7 +22150,7 @@ var import_node_path26 = require("node:path");
|
|
|
22128
22150
|
// src/lib/baseline.ts
|
|
22129
22151
|
var import_node_fs28 = require("node:fs");
|
|
22130
22152
|
var import_node_path25 = require("node:path");
|
|
22131
|
-
var
|
|
22153
|
+
var import_node_crypto11 = require("node:crypto");
|
|
22132
22154
|
|
|
22133
22155
|
// src/lib/snapshot.ts
|
|
22134
22156
|
var import_node_fs27 = require("node:fs");
|
|
@@ -22397,7 +22419,7 @@ var MIRROR_MAX_BYTES = 2 * 1024 * 1024;
|
|
|
22397
22419
|
var DEFAULT_SESSION_KEY = "_default";
|
|
22398
22420
|
function sessionKey(sessionId) {
|
|
22399
22421
|
if (!sessionId) return DEFAULT_SESSION_KEY;
|
|
22400
|
-
return (0,
|
|
22422
|
+
return (0, import_node_crypto11.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
|
|
22401
22423
|
}
|
|
22402
22424
|
function sessionDir(key) {
|
|
22403
22425
|
return (0, import_node_path25.join)(projectPath(BASELINE_DIR), key);
|
|
@@ -24317,10 +24339,10 @@ var import_node_fs38 = require("node:fs");
|
|
|
24317
24339
|
|
|
24318
24340
|
// src/lib/debounce.ts
|
|
24319
24341
|
var import_node_fs37 = require("node:fs");
|
|
24320
|
-
var
|
|
24342
|
+
var import_node_crypto12 = require("node:crypto");
|
|
24321
24343
|
function scopedFile(base, sessionId) {
|
|
24322
24344
|
if (!sessionId) return base;
|
|
24323
|
-
return `${base}.${(0,
|
|
24345
|
+
return `${base}.${(0, import_node_crypto12.createHash)("sha1").update(sessionId).digest("hex").slice(0, 12)}`;
|
|
24324
24346
|
}
|
|
24325
24347
|
function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
|
|
24326
24348
|
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
@@ -24361,7 +24383,7 @@ function checkMtime(files, bypassForRecentCommits, sessionId) {
|
|
|
24361
24383
|
return "No files modified since last analysis";
|
|
24362
24384
|
}
|
|
24363
24385
|
function computeContentHash(files) {
|
|
24364
|
-
const hash = (0,
|
|
24386
|
+
const hash = (0, import_node_crypto12.createHash)("sha1");
|
|
24365
24387
|
const sorted = [...files].sort();
|
|
24366
24388
|
for (const f of sorted) {
|
|
24367
24389
|
const resolved = resolveFile(f) ?? f;
|
|
@@ -27073,7 +27095,7 @@ function channelSilence(input) {
|
|
|
27073
27095
|
// src/lib/cli-version.ts
|
|
27074
27096
|
function cliVersion() {
|
|
27075
27097
|
try {
|
|
27076
|
-
return true ? "0.34.0-experimental.
|
|
27098
|
+
return true ? "0.34.0-experimental.8b93a3c" : "dev";
|
|
27077
27099
|
} catch {
|
|
27078
27100
|
return "dev";
|
|
27079
27101
|
}
|
|
@@ -28541,7 +28563,7 @@ async function contextFiles(run4) {
|
|
|
28541
28563
|
}
|
|
28542
28564
|
|
|
28543
28565
|
// src/lib/code/index.ts
|
|
28544
|
-
var
|
|
28566
|
+
var import_node_crypto13 = require("node:crypto");
|
|
28545
28567
|
|
|
28546
28568
|
// src/lib/code/languages.ts
|
|
28547
28569
|
var JS_LIKE = {
|
|
@@ -30319,7 +30341,7 @@ function byPath(fns, name2) {
|
|
|
30319
30341
|
function makeCodeMap(rt) {
|
|
30320
30342
|
const cache = /* @__PURE__ */ new Map();
|
|
30321
30343
|
const outline = (path, content, deadlineAt = Date.now() + DEFAULT_PARSE_MS) => {
|
|
30322
|
-
const key = `${path}\0${(0,
|
|
30344
|
+
const key = `${path}\0${(0, import_node_crypto13.createHash)("sha1").update(content).digest("hex")}`;
|
|
30323
30345
|
const hit = cache.get(key);
|
|
30324
30346
|
if (hit) return { source: "tree-sitter", value: hit, elapsedMs: 0 };
|
|
30325
30347
|
let r;
|
|
@@ -31688,6 +31710,22 @@ async function workingMemory(run4) {
|
|
|
31688
31710
|
Object.assign(run4, { incrementReport, memory: memory2, memorySession, reachability });
|
|
31689
31711
|
}
|
|
31690
31712
|
|
|
31713
|
+
// src/lib/payload-bounds.ts
|
|
31714
|
+
var MAX_WIRE_PATHS = 500;
|
|
31715
|
+
function boundPaths(paths, first, max = MAX_WIRE_PATHS) {
|
|
31716
|
+
if (paths.length <= max) return [...paths];
|
|
31717
|
+
const present = new Set(paths);
|
|
31718
|
+
const kept = /* @__PURE__ */ new Set();
|
|
31719
|
+
const out2 = [];
|
|
31720
|
+
for (const p of [...first, ...paths]) {
|
|
31721
|
+
if (out2.length >= max) break;
|
|
31722
|
+
if (!present.has(p) || kept.has(p)) continue;
|
|
31723
|
+
kept.add(p);
|
|
31724
|
+
out2.push(p);
|
|
31725
|
+
}
|
|
31726
|
+
return out2;
|
|
31727
|
+
}
|
|
31728
|
+
|
|
31691
31729
|
// src/lib/note-budget.ts
|
|
31692
31730
|
var import_node_fs49 = require("node:fs");
|
|
31693
31731
|
var ADVISORY_BUDGET = { PASS: 1, WARN: 2 };
|
|
@@ -31802,6 +31840,7 @@ async function buildRequest(run4) {
|
|
|
31802
31840
|
(conversation?.prompts?.length ?? 0) > 0
|
|
31803
31841
|
);
|
|
31804
31842
|
const noteConfirmation = peekNoteOutcome();
|
|
31843
|
+
const unobserved = foldResult?.unobserved ?? [];
|
|
31805
31844
|
const excludedByReason = {};
|
|
31806
31845
|
for (const e of codeDelta.excluded ?? []) {
|
|
31807
31846
|
excludedByReason[e.reason] = (excludedByReason[e.reason] ?? 0) + 1;
|
|
@@ -31846,7 +31885,9 @@ async function buildRequest(run4) {
|
|
|
31846
31885
|
coverage_telemetry: coverageTelemetry,
|
|
31847
31886
|
static_results: staticResults,
|
|
31848
31887
|
code_delta: codeDelta,
|
|
31849
|
-
|
|
31888
|
+
// Bounded, reviewed paths first — the server applies the same bound. The
|
|
31889
|
+
// full count is `coverage_telemetry.for_review`. VRT-242.
|
|
31890
|
+
changed_files: boundPaths(allForReview, codeDelta.files.map((f) => f.path)),
|
|
31850
31891
|
standard_version: "latest",
|
|
31851
31892
|
analysis_mode: analysisMode,
|
|
31852
31893
|
plan_only: analysisMode === "plan",
|
|
@@ -31897,7 +31938,9 @@ async function buildRequest(run4) {
|
|
|
31897
31938
|
// `hash_at_last_verdict` is frozen at each verdict and `hash_now` tracks
|
|
31898
31939
|
// disk, so their inequality IS "changed since we last spoke".
|
|
31899
31940
|
authored_since_verdict: memorySession ? foldDossier(memorySession.d).authored_all.filter((a) => a.hash_now !== a.hash_at_last_verdict).map((a) => a.path) : [],
|
|
31900
|
-
|
|
31941
|
+
// The server reads only how many; a bulk change sent every path. VRT-242.
|
|
31942
|
+
unobserved: unobserved.slice(0, MAX_WIRE_PATHS),
|
|
31943
|
+
...unobserved.length > MAX_WIRE_PATHS && { unobserved_total: unobserved.length },
|
|
31901
31944
|
commands: foldResult?.commands ?? [],
|
|
31902
31945
|
tools: foldResult?.tools ?? [],
|
|
31903
31946
|
tasks: foldResult?.tasks ?? [],
|
|
@@ -32061,11 +32104,11 @@ async function buildRequest(run4) {
|
|
|
32061
32104
|
|
|
32062
32105
|
// src/lib/offline.ts
|
|
32063
32106
|
var import_node_fs50 = require("node:fs");
|
|
32064
|
-
var
|
|
32107
|
+
var import_node_crypto14 = require("node:crypto");
|
|
32065
32108
|
function cacheRequest(body2) {
|
|
32066
32109
|
try {
|
|
32067
32110
|
(0, import_node_fs50.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
32068
|
-
const suffix = (0,
|
|
32111
|
+
const suffix = (0, import_node_crypto14.randomBytes)(4).toString("hex");
|
|
32069
32112
|
const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
|
|
32070
32113
|
(0, import_node_fs50.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(redactRequest(body2)));
|
|
32071
32114
|
} catch {
|
|
@@ -32108,8 +32151,10 @@ function buildOfflineFallback(reason, staticResults) {
|
|
|
32108
32151
|
}
|
|
32109
32152
|
|
|
32110
32153
|
// src/lib/retry.ts
|
|
32154
|
+
var WORKER_RESOURCE_LIMIT = 546;
|
|
32111
32155
|
function shouldWarmRetryAnalyze(result) {
|
|
32112
32156
|
if (result.ok) return false;
|
|
32157
|
+
if (result.http_status === WORKER_RESOURCE_LIMIT) return false;
|
|
32113
32158
|
if (result.category === "network" || result.category === "timeout") return true;
|
|
32114
32159
|
if (result.category === "http_5xx") return true;
|
|
32115
32160
|
if (result.category === "invalid_json" && (result.http_status ?? 0) >= 500) return true;
|
|
@@ -32119,7 +32164,7 @@ function shouldWarmRetryAnalyze(result) {
|
|
|
32119
32164
|
// src/commands/analyze/phases/12-transmit.ts
|
|
32120
32165
|
async function transmit(run4) {
|
|
32121
32166
|
const { globals } = run4;
|
|
32122
|
-
const {
|
|
32167
|
+
const { requestBody, serviceUrl, staticResults, token } = run4;
|
|
32123
32168
|
const ANALYZE_TIMEOUT_MS = 1e5;
|
|
32124
32169
|
let result = await analyzeRequest({
|
|
32125
32170
|
serviceUrl,
|
|
@@ -32156,11 +32201,12 @@ async function transmit(run4) {
|
|
|
32156
32201
|
}
|
|
32157
32202
|
if (!result.ok) {
|
|
32158
32203
|
cacheRequest(requestBody);
|
|
32204
|
+
const sentKb = () => Math.round(Buffer.byteLength(JSON.stringify(requestBody)) / 1024);
|
|
32159
32205
|
let message;
|
|
32160
32206
|
if (result.http_status === 413) {
|
|
32161
|
-
|
|
32162
|
-
|
|
32163
|
-
message = `Verity:
|
|
32207
|
+
message = `Verity: request rejected \u2014 payload too large (${sentKb()} KB sent: ${result.error}). Commit or stash pre-existing changes, or run with --mode skip if you didn't intend to submit code this turn.`;
|
|
32208
|
+
} else if (result.http_status === WORKER_RESOURCE_LIMIT) {
|
|
32209
|
+
message = `Verity: the service ran out of CPU or memory on this request (HTTP 546, ${sentKb()} KB sent). This is not a cold start: the same change fails the same way, so it was not retried. Commit or stash unrelated changes to shrink the request (local analysis this run).`;
|
|
32164
32210
|
} else if (result.category === "network" || result.category === "timeout") {
|
|
32165
32211
|
message = `Verity offline \u2014 ${result.error}`;
|
|
32166
32212
|
} else if (result.error.startsWith("STALE_VERIFICATION")) {
|
|
@@ -33461,7 +33507,7 @@ async function runAgentReview(options) {
|
|
|
33461
33507
|
|
|
33462
33508
|
// src/lib/prove/auto.ts
|
|
33463
33509
|
var import_node_path44 = require("node:path");
|
|
33464
|
-
var
|
|
33510
|
+
var import_node_crypto19 = require("node:crypto");
|
|
33465
33511
|
|
|
33466
33512
|
// src/lib/prove/signals.ts
|
|
33467
33513
|
var DEFAULT_SINCE = "180 days ago";
|
|
@@ -33869,6 +33915,11 @@ async function rankViaServer(candidates2, ctx, auth2) {
|
|
|
33869
33915
|
cmd: "prove-rank"
|
|
33870
33916
|
});
|
|
33871
33917
|
if (!res.ok) return { ok: false, ranker: "luna", error: res.error, ranked: fallback };
|
|
33918
|
+
const off = res.data;
|
|
33919
|
+
if (off?.enabled === false) {
|
|
33920
|
+
const why = typeof off.reason === "string" ? off.reason.slice(0, 40) : "disabled";
|
|
33921
|
+
return { ok: false, ranker: "luna", error: `formal is off for this project (${why})`, ranked: fallback, disabled: why };
|
|
33922
|
+
}
|
|
33872
33923
|
const items = Array.isArray(res.data?.items) ? res.data.items : [];
|
|
33873
33924
|
return { ok: true, ranker: "luna", ranked: applyAnnotations(candidates2, items), costUsd: typeof res.data?.cost_usd === "number" ? res.data.cost_usd : null };
|
|
33874
33925
|
}
|
|
@@ -34593,6 +34644,8 @@ async function generateModel(target, opts) {
|
|
|
34593
34644
|
});
|
|
34594
34645
|
if (!res.ok) return { ok: false, error: res.error };
|
|
34595
34646
|
const d = res.data ?? {};
|
|
34647
|
+
const off = d;
|
|
34648
|
+
if (off.enabled === false) return { ok: false, error: `formal is off for this project (${typeof off.reason === "string" ? off.reason.slice(0, 40) : "disabled"})` };
|
|
34596
34649
|
if (d.error && typeof d.error === "object") return { ok: false, error: `${d.error.code ?? "SERVICE_ERROR"}: ${d.error.message ?? "the service failed"}` };
|
|
34597
34650
|
const usage2 = d.usage && typeof d.usage === "object" ? { inputTokens: Number(d.usage.inputTokens ?? 0), outputTokens: Number(d.usage.outputTokens ?? 0) } : null;
|
|
34598
34651
|
return acceptAnswer(d, usage2, typeof d.cost_usd === "number" ? d.cost_usd : null);
|
|
@@ -34772,8 +34825,7 @@ Rules:
|
|
|
34772
34825
|
5. Write the harness as ONE file and run it in the SAME shell call that writes it (a heredoc followed by the run). TypeScript: node --experimental-strip-types harness.ts (detect runtimes only if that fails: node --version, python3 --version); if strip-types is unavailable, remove type annotations ONLY on lines that are not part of the function under test's logic, and say so. Python: python3 harness.py.
|
|
34773
34826
|
6. Nothing you run can reach the network. Do not try.
|
|
34774
34827
|
7. Keep it under about eight shell calls. Output tokens are time.
|
|
34775
|
-
8. Answer with the JSON object only: verdict (reproduced | diverged | unreplayable), reason (2-4 sentences: what you ran, what the real code did, and how that compares to the trace's final state), harness_files (every file you wrote: path and its line count \u2014 do NOT paste contents, your shell calls already carry them), executed_output (the last relevant stdout, at most 2000 characters)
|
|
34776
|
-
9. impact \u2014 AFTER the verdict, and never a reason to change it: what the violation you ran costs a user of this code, the most serious one the run showed. money: a balance, charge, refund, price or stock is wrong. access: someone gets access they should not have, or an auth check is bypassed. data: a user's records are lost, corrupted or wrongly duplicated. external: an effect outside the system happens twice or never (a charge, an email, a shipment, a webhook). count: a counter, metric or statistic is off. cache: a cached or derived value is stale until it refreshes. internal: the program's own bookkeeping is off, with no effect a user would see. none: the verdict is not reproduced.
|
|
34828
|
+
8. Answer with the JSON object only: verdict (reproduced | diverged | unreplayable), reason (2-4 sentences: what you ran, what the real code did, and how that compares to the trace's final state), harness_files (every file you wrote: path and its line count \u2014 do NOT paste contents, your shell calls already carry them), executed_output (the last relevant stdout, at most 2000 characters).
|
|
34777
34829
|
|
|
34778
34830
|
"reproduced" means the real code, driven by the trace, reached the property violation the model describes. "diverged" means it did not \u2014 the model is wrong about the code, which is a useful answer. "unreplayable" means you could not run the real function faithfully; say why.`;
|
|
34779
34831
|
var REPLAY_SCHEMA = {
|
|
@@ -34791,10 +34843,9 @@ var REPLAY_SCHEMA = {
|
|
|
34791
34843
|
required: ["path", "lines"]
|
|
34792
34844
|
}
|
|
34793
34845
|
},
|
|
34794
|
-
executed_output: { type: "string" }
|
|
34795
|
-
impact: { type: "string", enum: ["money", "access", "data", "external", "count", "cache", "internal", "none"] }
|
|
34846
|
+
executed_output: { type: "string" }
|
|
34796
34847
|
},
|
|
34797
|
-
required: ["verdict", "reason", "harness_files", "executed_output"
|
|
34848
|
+
required: ["verdict", "reason", "harness_files", "executed_output"]
|
|
34798
34849
|
};
|
|
34799
34850
|
function renderBriefInput(brief) {
|
|
34800
34851
|
const p = brief.property;
|
|
@@ -34821,8 +34872,6 @@ function renderBriefInput(brief) {
|
|
|
34821
34872
|
}
|
|
34822
34873
|
return parts2.join("\n");
|
|
34823
34874
|
}
|
|
34824
|
-
var IMPACTS = ["money", "access", "data", "external", "count", "cache", "internal", "none"];
|
|
34825
|
-
var isImpact = (v) => typeof v === "string" && IMPACTS.includes(v);
|
|
34826
34875
|
function stringsIn(value, out2 = [], depth = 0) {
|
|
34827
34876
|
if (depth > 8) return out2;
|
|
34828
34877
|
if (typeof value === "string") out2.push(value);
|
|
@@ -34864,8 +34913,7 @@ function parseReplayOutput(output) {
|
|
|
34864
34913
|
verdict,
|
|
34865
34914
|
reason: typeof o.reason === "string" ? o.reason.slice(0, 2e3) : "",
|
|
34866
34915
|
harness_files: Array.isArray(o.harness_files) ? o.harness_files.filter((f) => typeof f.path === "string").map((f) => ({ path: String(f.path).slice(0, 200), lines: Number.isInteger(f.lines) ? f.lines : 0 })) : [],
|
|
34867
|
-
executed_output: typeof o.executed_output === "string" ? o.executed_output.slice(0, 2e3) : ""
|
|
34868
|
-
impact: isImpact(o.impact) ? o.impact : "none"
|
|
34916
|
+
executed_output: typeof o.executed_output === "string" ? o.executed_output.slice(0, 2e3) : ""
|
|
34869
34917
|
};
|
|
34870
34918
|
}
|
|
34871
34919
|
} catch {
|
|
@@ -35066,7 +35114,6 @@ async function runReplayInContainer(brief, opts) {
|
|
|
35066
35114
|
...judged,
|
|
35067
35115
|
executedCalls,
|
|
35068
35116
|
shellCalls,
|
|
35069
|
-
...judged.verdict === "reproduced" && parsed.claim ? { impact: parsed.claim.impact } : {},
|
|
35070
35117
|
executedOutput: parsed.claim?.executed_output ?? "",
|
|
35071
35118
|
harnessFiles: parsed.claim?.harness_files ?? [],
|
|
35072
35119
|
usage,
|
|
@@ -35113,6 +35160,11 @@ async function runReplayViaServer(brief, auth2, budgetMs = 24e4) {
|
|
|
35113
35160
|
return { claimed: null, verdict: "unverified", reason: "", downgrade: res.error, executedCalls: 0, shellCalls: 0, verbatim: none, executedOutput: "", harnessFiles: [], usage: null, durationMs: Date.now() - started, containerId: null, error: res.error };
|
|
35114
35161
|
}
|
|
35115
35162
|
const d = res.data ?? {};
|
|
35163
|
+
if (d.enabled === false) {
|
|
35164
|
+
const why = d.reason;
|
|
35165
|
+
const msg = `formal is off for this project (${typeof why === "string" ? why.slice(0, 40) : "disabled"})`;
|
|
35166
|
+
return { claimed: null, verdict: "unverified", reason: "", downgrade: msg, executedCalls: 0, shellCalls: 0, verbatim: none, executedOutput: "", harnessFiles: [], usage: null, durationMs: Date.now() - started, containerId: null, error: msg };
|
|
35167
|
+
}
|
|
35116
35168
|
if (d.error && typeof d.error === "object") {
|
|
35117
35169
|
const e = d.error;
|
|
35118
35170
|
const msg = `${e.code ?? "SERVICE_ERROR"}: ${e.message ?? "the service failed"}`;
|
|
@@ -35125,7 +35177,6 @@ async function runReplayViaServer(brief, auth2, budgetMs = 24e4) {
|
|
|
35125
35177
|
verdict: verdict === "reproduced" || verdict === "diverged" || verdict === "unreplayable" ? verdict : "unverified",
|
|
35126
35178
|
reason: typeof d.reason === "string" ? d.reason : "",
|
|
35127
35179
|
downgrade: typeof d.downgrade === "string" ? d.downgrade : null,
|
|
35128
|
-
...isImpact(d.impact) ? { impact: d.impact } : {},
|
|
35129
35180
|
executedCalls: Number(d.executed_calls ?? 0),
|
|
35130
35181
|
shellCalls: Number(d.shell_calls ?? 0),
|
|
35131
35182
|
verbatim: { covered: Number(v.covered ?? 0), total: Number(v.total ?? 0), ratio: Number(v.ratio ?? 0), basis: v.basis === "symbol" || v.basis === "file" ? v.basis : "none" },
|
|
@@ -35192,7 +35243,7 @@ var import_node_path42 = require("node:path");
|
|
|
35192
35243
|
|
|
35193
35244
|
// src/lib/prove/safe-write.ts
|
|
35194
35245
|
var import_node_fs56 = require("node:fs");
|
|
35195
|
-
var
|
|
35246
|
+
var import_node_crypto15 = require("node:crypto");
|
|
35196
35247
|
var import_node_path41 = require("node:path");
|
|
35197
35248
|
var SafeWriteError = class extends Error {
|
|
35198
35249
|
constructor(reason, path) {
|
|
@@ -35253,7 +35304,7 @@ function writeFileInRootNoFollow(root, rel, content) {
|
|
|
35253
35304
|
} catch {
|
|
35254
35305
|
throw new SafeWriteError("unwritable", root);
|
|
35255
35306
|
}
|
|
35256
|
-
const tmp = (0, import_node_path41.join)(dir, `.${(0, import_node_path41.basename)(dest)}.${(0,
|
|
35307
|
+
const tmp = (0, import_node_path41.join)(dir, `.${(0, import_node_path41.basename)(dest)}.${(0, import_node_crypto15.randomBytes)(6).toString("hex")}.tmp`);
|
|
35257
35308
|
let fd;
|
|
35258
35309
|
try {
|
|
35259
35310
|
fd = (0, import_node_fs56.openSync)(tmp, import_node_fs56.constants.O_WRONLY | import_node_fs56.constants.O_CREAT | import_node_fs56.constants.O_EXCL | NOFOLLOW2, 420);
|
|
@@ -35655,7 +35706,7 @@ function existsNoFollow(abs) {
|
|
|
35655
35706
|
}
|
|
35656
35707
|
|
|
35657
35708
|
// src/lib/prove/report.ts
|
|
35658
|
-
var
|
|
35709
|
+
var import_node_crypto16 = require("node:crypto");
|
|
35659
35710
|
var IDENT2 = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,63}$/;
|
|
35660
35711
|
var SHA256 = /^(sha256:)?[0-9a-f]{64}$/;
|
|
35661
35712
|
var KINDS2 = /* @__PURE__ */ new Set(["structural", "intent"]);
|
|
@@ -35803,7 +35854,7 @@ function sha256InRoot(root, file) {
|
|
|
35803
35854
|
const opened = openRegularInRoot(root, file);
|
|
35804
35855
|
if (!opened.ok) return null;
|
|
35805
35856
|
try {
|
|
35806
|
-
return (0,
|
|
35857
|
+
return (0, import_node_crypto16.createHash)("sha256").update(readOpened(opened.fd, opened.size)).digest("hex");
|
|
35807
35858
|
} catch {
|
|
35808
35859
|
return null;
|
|
35809
35860
|
}
|
|
@@ -36108,33 +36159,18 @@ function writeResult(root, report) {
|
|
|
36108
36159
|
function isCrashTrace(trace) {
|
|
36109
36160
|
return trace.some((s) => /\b(crash\w*|kill\w*|restart\w*|interrupt\w*|power|abort\w*|process (dies|exit\w*)|lost (reply|response))\b/i.test(s));
|
|
36110
36161
|
}
|
|
36111
|
-
var SEVERITY_BY_IMPACT = {
|
|
36112
|
-
money: "high",
|
|
36113
|
-
access: "high",
|
|
36114
|
-
data: "high",
|
|
36115
|
-
external: "high",
|
|
36116
|
-
count: "medium",
|
|
36117
|
-
cache: "medium",
|
|
36118
|
-
internal: "medium",
|
|
36119
|
-
// A reproduced finding always has a real code; 'none' here is a malformed answer, so it blocks.
|
|
36120
|
-
none: "high"
|
|
36121
|
-
};
|
|
36122
|
-
function formalSeverity(f) {
|
|
36123
|
-
return f.impact && SEVERITY_BY_IMPACT[f.impact] || "high";
|
|
36124
|
-
}
|
|
36125
36162
|
function mergeFormalFindings(response, outcome, pushedFiles) {
|
|
36126
|
-
if (!outcome) return { blocking: 0
|
|
36163
|
+
if (!outcome) return { blocking: 0 };
|
|
36127
36164
|
const norm2 = (p) => p.replace(/\\/g, "/");
|
|
36128
36165
|
const pushed = new Set(pushedFiles.map(norm2));
|
|
36129
|
-
const
|
|
36130
|
-
if (
|
|
36131
|
-
const blocking = reproduced.filter((f) => formalSeverity(f) === "high");
|
|
36166
|
+
const blocking = outcome.findings.filter((f) => f.verdict === "reproduced" && !f.waived && pushed.has(norm2(f.file)));
|
|
36167
|
+
if (blocking.length === 0) return { blocking: 0 };
|
|
36132
36168
|
const existing = Array.isArray(response.findings) ? response.findings : [];
|
|
36133
36169
|
response.findings = [
|
|
36134
36170
|
...existing,
|
|
36135
|
-
...
|
|
36171
|
+
...blocking.map((f) => ({
|
|
36136
36172
|
pattern_id: "formal-counterexample",
|
|
36137
|
-
severity:
|
|
36173
|
+
severity: "high",
|
|
36138
36174
|
scope: "change",
|
|
36139
36175
|
file: f.file,
|
|
36140
36176
|
line: outcome.target?.lines?.from ?? null,
|
|
@@ -36142,19 +36178,18 @@ function mergeFormalFindings(response, outcome, pushedFiles) {
|
|
|
36142
36178
|
// as a title it read backwards (e2e, 2026-09-26). A trace that needs a
|
|
36143
36179
|
// crash between two writes is labelled: reproducing it means injecting
|
|
36144
36180
|
// the crash, so it is a crash-consistency finding, not a race.
|
|
36145
|
-
title: `Formal: ${terminalText(f.symbol, 128)} breaks "${terminalText(f.desc, 280)}"${isCrashTrace(f.trace) ? " after a crash (crash consistency)" : ""} \u2014 reproduced on the real code
|
|
36181
|
+
title: `Formal: ${terminalText(f.symbol, 128)} breaks "${terminalText(f.desc, 280)}"${isCrashTrace(f.trace) ? " after a crash (crash consistency)" : ""} \u2014 reproduced on the real code`,
|
|
36146
36182
|
description: terminalText(f.reason ?? "", 2e3),
|
|
36147
36183
|
fix: { description: `Trace: ${terminalText(f.trace.join(" \u2192 ") || "(initial state)", 2e3)}. Replay: ${terminalText(f.reason ?? "reproduced in the container", 2e3)}` },
|
|
36148
36184
|
source: "formal"
|
|
36149
36185
|
}))
|
|
36150
36186
|
];
|
|
36151
|
-
if (
|
|
36152
|
-
|
|
36153
|
-
return { blocking: blocking.length, warning: reproduced.length - blocking.length };
|
|
36187
|
+
if (response.gate_decision !== "FAIL") response.gate_decision = "FAIL";
|
|
36188
|
+
return { blocking: blocking.length };
|
|
36154
36189
|
}
|
|
36155
36190
|
|
|
36156
36191
|
// src/lib/prove/scalpel.ts
|
|
36157
|
-
var
|
|
36192
|
+
var import_node_crypto17 = require("node:crypto");
|
|
36158
36193
|
var ARITHMETIC = /Math\.(?:round|floor|ceil|trunc)\(|\bround\(|\bdivmod\(|[\w)\]]\s*(?:\*|\/(?!\/)|%)\s*[\w(]/;
|
|
36159
36194
|
function functionHazard(numbered2) {
|
|
36160
36195
|
const sites = effectSites(numbered2);
|
|
@@ -36184,7 +36219,7 @@ function semanticFingerprint(numbered2) {
|
|
|
36184
36219
|
if (LOG_LINE.test(t)) continue;
|
|
36185
36220
|
kept.push(t.replace(/\s+/g, " "));
|
|
36186
36221
|
}
|
|
36187
|
-
return (0,
|
|
36222
|
+
return (0, import_node_crypto17.createHash)("sha256").update(kept.join("\n")).digest("hex");
|
|
36188
36223
|
}
|
|
36189
36224
|
function readVerified(root, now = Date.now()) {
|
|
36190
36225
|
const read2 = readRegularFileInRoot(root, VERIFIED_FILE, 256 * 1024);
|
|
@@ -36207,9 +36242,9 @@ function recordVerified(root, rec, now = Date.now()) {
|
|
|
36207
36242
|
}
|
|
36208
36243
|
|
|
36209
36244
|
// src/lib/prove/context.ts
|
|
36210
|
-
var
|
|
36245
|
+
var import_node_crypto18 = require("node:crypto");
|
|
36211
36246
|
var CONTEXT_BUDGET_MS = 2e3;
|
|
36212
|
-
var sha256 = (s) => (0,
|
|
36247
|
+
var sha256 = (s) => (0, import_node_crypto18.createHash)("sha256").update(s).digest("hex");
|
|
36213
36248
|
var TEST_PATH4 = /(^|\/)(tests?|__tests__|spec)\/|\.(test|spec)\.[a-z]+$/;
|
|
36214
36249
|
function slice(content, f, maxLines) {
|
|
36215
36250
|
const all = content.split("\n");
|
|
@@ -36398,7 +36433,7 @@ function workingTreeReader(root) {
|
|
|
36398
36433
|
};
|
|
36399
36434
|
}
|
|
36400
36435
|
function sha256Of(content) {
|
|
36401
|
-
return (0,
|
|
36436
|
+
return (0, import_node_crypto19.createHash)("sha256").update(content).digest("hex");
|
|
36402
36437
|
}
|
|
36403
36438
|
function defaultDeps(opts) {
|
|
36404
36439
|
const env = opts.env ?? process.env;
|
|
@@ -36469,9 +36504,7 @@ async function runFormalPhase(input) {
|
|
|
36469
36504
|
symbol: f.symbol,
|
|
36470
36505
|
verdict: "reproduced",
|
|
36471
36506
|
reason: f.waived ? `waived ${f.waived.at.slice(0, 10)}: ${f.waived.reason} (standing since ${f.at.slice(0, 10)}; the file has not changed)` : `${f.reason ?? "reproduced on the real code"} (standing since ${f.at.slice(0, 10)}; the file has not changed)`,
|
|
36472
|
-
...f.waived ? { waived: true } : {}
|
|
36473
|
-
// A hand-edited file cannot invent a code: unknown values read as absent (HIGH).
|
|
36474
|
-
...isImpact(f.impact) ? { impact: f.impact } : {}
|
|
36507
|
+
...f.waived ? { waived: true } : {}
|
|
36475
36508
|
});
|
|
36476
36509
|
}
|
|
36477
36510
|
}
|
|
@@ -36510,6 +36543,7 @@ async function runFormalPhase(input) {
|
|
|
36510
36543
|
return done({ status: "skipped", skip: "not-eligible", detail: `${best.file}: ${isEligible(best, largePush).why}` });
|
|
36511
36544
|
}
|
|
36512
36545
|
const ranked = await deps.rank(eligible, root, input.changedLines);
|
|
36546
|
+
if (!ranked.ok && ranked.disabled) return done({ status: "skipped", skip: "disabled-by-server", detail: ranked.error });
|
|
36513
36547
|
if (ranked.ok) spend(ranked);
|
|
36514
36548
|
candidates2 = ranked.ranked.slice(0, 5).map((c) => ({ file: c.file, symbol: c.ai?.symbol ?? null, score: c.ai?.score ?? null, criteria: c.ai?.criteria ?? null, reasked: !!c.ai?.reasked, reasons: (c.ai?.reasons ?? []).map((r) => r.slice(0, 200)) }));
|
|
36515
36549
|
if (cancelled()) return done({ status: "skipped", skip: "deadline", detail: "cut off after ranking" });
|
|
@@ -36744,7 +36778,7 @@ async function runFormalPhase(input) {
|
|
|
36744
36778
|
continue;
|
|
36745
36779
|
}
|
|
36746
36780
|
const verdict = r.verdict === "reproduced" ? "reproduced" : r.verdict === "diverged" ? "diverged" : r.verdict === "unreplayable" ? "model-only" : "unverified";
|
|
36747
|
-
findings.push({ ...baseOf(p), verdict, reason: r.reason || r.downgrade || void 0
|
|
36781
|
+
findings.push({ ...baseOf(p), verdict, reason: r.reason || r.downgrade || void 0 });
|
|
36748
36782
|
}
|
|
36749
36783
|
const reproduced = findings.filter((f) => f.verdict === "reproduced");
|
|
36750
36784
|
if (reproduced.length > 0 && !cancelled()) {
|
|
@@ -36752,7 +36786,7 @@ async function runFormalPhase(input) {
|
|
|
36752
36786
|
const others = readPersistedFindings(root).filter((f) => f.file !== top.file);
|
|
36753
36787
|
writePersistedFindings(root, [
|
|
36754
36788
|
...others,
|
|
36755
|
-
...reproduced.map((f) => ({ file: f.file, symbol: f.symbol, sha256: sha2562, property: f.property, desc: f.desc, kind: f.kind, trace: f.trace, model: modelName, reason: f.reason, ...
|
|
36789
|
+
...reproduced.map((f) => ({ file: f.file, symbol: f.symbol, sha256: sha2562, property: f.property, desc: f.desc, kind: f.kind, trace: f.trace, model: modelName, reason: f.reason, ...lines ? { lines } : {}, at: now }))
|
|
36756
36790
|
]);
|
|
36757
36791
|
}
|
|
36758
36792
|
const clean2 = findings.every((f) => f.verdict === "diverged");
|
|
@@ -37492,8 +37526,6 @@ async function runGuard(opts, globals) {
|
|
|
37492
37526
|
rounds: formalOutcome.model?.rounds ?? null,
|
|
37493
37527
|
properties: formalOutcome.properties?.map((p) => `${p.id}:${p.result}`) ?? [],
|
|
37494
37528
|
findings: formalOutcome.findings.map((f) => `${f.property}:${f.verdict}`),
|
|
37495
|
-
// Decision 82: the replay's impact code per reproduced finding; count/cache/internal warn, the rest block.
|
|
37496
|
-
impacts: formalOutcome.findings.filter((f) => f.impact).map((f) => `${f.property}:${f.impact}`),
|
|
37497
37529
|
// Decision 81: what each property rests on (a rule, or "line"), what was
|
|
37498
37530
|
// sent back or dropped as ungrounded, and which counterexamples lack their
|
|
37499
37531
|
// property's trigger (shadow). Ids and rule names only, never code.
|
|
@@ -37734,7 +37766,7 @@ function registerIgnoreCommand(program2) {
|
|
|
37734
37766
|
}
|
|
37735
37767
|
|
|
37736
37768
|
// src/commands/waive.ts
|
|
37737
|
-
var
|
|
37769
|
+
var import_node_crypto20 = require("node:crypto");
|
|
37738
37770
|
var import_node_fs60 = require("node:fs");
|
|
37739
37771
|
function registerWaiveCommand(program2) {
|
|
37740
37772
|
program2.command("waive <pattern-id>").description("Record an accepted-risk disposition for an open finding (voids when the file changes)").option("--file <path>", "File the finding is anchored to, REPO-RELATIVE (recommended \u2014 narrows the waive)").requiredOption("--reason <text>", "The human disposition this records (reviewer finding, ADR, \u2026)").action(async (patternId, opts) => {
|
|
@@ -37764,7 +37796,7 @@ function registerWaiveCommand(program2) {
|
|
|
37764
37796
|
if (opts.file) {
|
|
37765
37797
|
body2.file = opts.file;
|
|
37766
37798
|
try {
|
|
37767
|
-
body2.file_sha256 = (0,
|
|
37799
|
+
body2.file_sha256 = (0, import_node_crypto20.createHash)("sha256").update((0, import_node_fs60.readFileSync)(opts.file)).digest("hex");
|
|
37768
37800
|
} catch {
|
|
37769
37801
|
printError(`Cannot read ${opts.file} \u2014 run from the repo root, or omit --file to waive by pattern.`);
|
|
37770
37802
|
process.exit(1);
|
|
@@ -38795,7 +38827,7 @@ async function promptMultiSelect(question, choices, fallback) {
|
|
|
38795
38827
|
|
|
38796
38828
|
// src/lib/prove/lean-install.ts
|
|
38797
38829
|
var import_node_fs63 = require("node:fs");
|
|
38798
|
-
var
|
|
38830
|
+
var import_node_crypto21 = require("node:crypto");
|
|
38799
38831
|
var import_node_child_process18 = require("node:child_process");
|
|
38800
38832
|
var import_node_os10 = require("node:os");
|
|
38801
38833
|
var import_node_path47 = require("node:path");
|
|
@@ -38833,7 +38865,7 @@ function expectedSha256(asset, version = pinnedVersion()) {
|
|
|
38833
38865
|
}
|
|
38834
38866
|
function sha256File(path) {
|
|
38835
38867
|
return new Promise((resolve8, reject) => {
|
|
38836
|
-
const hash = (0,
|
|
38868
|
+
const hash = (0, import_node_crypto21.createHash)("sha256");
|
|
38837
38869
|
(0, import_node_fs63.createReadStream)(path).on("data", (chunk) => hash.update(chunk)).on("end", () => resolve8(hash.digest("hex"))).on("error", reject);
|
|
38838
38870
|
});
|
|
38839
38871
|
}
|
|
@@ -39946,7 +39978,7 @@ function registerInitCommand(program2) {
|
|
|
39946
39978
|
...telemetryChoice ? { telemetry: telemetryChoice } : {},
|
|
39947
39979
|
init: {
|
|
39948
39980
|
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
39949
|
-
cli_version: true ? "0.34.0-experimental.
|
|
39981
|
+
cli_version: true ? "0.34.0-experimental.8b93a3c" : "dev"
|
|
39950
39982
|
}
|
|
39951
39983
|
});
|
|
39952
39984
|
} catch (err2) {
|
|
@@ -41419,8 +41451,8 @@ function registerLeanCommand(program2) {
|
|
|
41419
41451
|
}
|
|
41420
41452
|
|
|
41421
41453
|
// src/cli.ts
|
|
41422
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.34.0-experimental.
|
|
41423
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.34.0-experimental.
|
|
41454
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.34.0-experimental.8b93a3c").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) => {
|
|
41455
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.34.0-experimental.8b93a3c");
|
|
41424
41456
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
41425
41457
|
try {
|
|
41426
41458
|
await foldLegacyLocalCredential();
|
package/package.json
CHANGED