@codacy/verity-cli 0.32.1-experimental.1371ba8 → 0.32.1-experimental.c640abd
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 +232 -100
- package/bin/verity.js +58 -39
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,10 @@ All notable changes to Verity are documented here. This project follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
<!-- Shipped in 0.31.2 – 0.32.0 but deliberately not announced yet: the plugin
|
|
9
|
+
lane is being closed-loop tested first (Road to 1.0 · Phase 2.6). Cut it
|
|
10
|
+
into its own heading when it is ready to be announced. -->
|
|
11
|
+
|
|
8
12
|
**Verity ships as a Claude Code plugin.** `/plugin marketplace add codacy/verity`
|
|
9
13
|
then `/plugin install verity@codacy` gives a working gate with nothing else to
|
|
10
14
|
install — the plugin carries the CLI as a pinned dependency, and Claude Code
|
|
@@ -24,16 +28,6 @@ installs and updates it for you.
|
|
|
24
28
|
plugin is present it owns the hooks, and the settings.json copy stands down.
|
|
25
29
|
`verity init` detects the plugin and removes the duplicate wiring.
|
|
26
30
|
|
|
27
|
-
### 🚪 A project that was never set up is left alone
|
|
28
|
-
|
|
29
|
-
Every hook now asks the same question the same way. `verity analyze` used to run
|
|
30
|
-
in a project that had never seen `verity init`, create `.verity/` there, and then —
|
|
31
|
-
with no baseline to compare against — review nothing. It now stands down like the
|
|
32
|
-
other hooks always have, so installing Verity no longer scatters state through
|
|
33
|
-
directories you merely opened. On the first session in such a project, Claude is
|
|
34
|
-
told once that the gate is off here and can offer `/verity:setup` (or `verity init`
|
|
35
|
-
on an npm install). Works correctly from linked git worktrees.
|
|
36
|
-
|
|
37
31
|
### ⚙️ Git-moment gating moved into the project
|
|
38
32
|
|
|
39
33
|
`verity guard` now reads `.verity/config.json` instead of taking its moments from
|
|
@@ -42,13 +36,140 @@ default gates nothing** — set it with `verity config git-moments commit,push`,
|
|
|
42
36
|
answer the question in `verity init`. An explicit `--on` still wins, so existing
|
|
43
37
|
installs are untouched.
|
|
44
38
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
39
|
+
### 🔧 Plugin fixes
|
|
40
|
+
|
|
41
|
+
- **Uninstalling the plugin left projects with no gate at all.** Whether the
|
|
42
|
+
plugin owned a project's hooks was decided by whether its install directory
|
|
43
|
+
existed on disk — and that directory survives an uninstall. Every project the
|
|
44
|
+
plugin had ever run in went on standing its own hooks down for a plugin that
|
|
45
|
+
was gone. Ownership now comes from Claude Code's install registry, and
|
|
46
|
+
`verity init` clears the stale marker.
|
|
47
|
+
- **A repository can no longer switch the gate off by shipping a file.**
|
|
48
|
+
Ownership was claimed by `.verity/.plugin-active`, a file inside the project,
|
|
49
|
+
and one naming any directory that happened to exist disabled every hook there.
|
|
50
|
+
An install Claude Code cannot vouch for is ignored — and "vouch" now means
|
|
51
|
+
*for this checkout*: the cache path a real install lives at is predictable, so
|
|
52
|
+
a marker naming a plugin installed for **somebody else's project** used to be
|
|
53
|
+
believed here.
|
|
54
|
+
- **A plugin installed from a local marketplace directory is recognised.**
|
|
55
|
+
Claude Code runs a marketplace served from a directory out of that directory
|
|
56
|
+
while recording the cache copy it made, so the two paths disagreed and a live,
|
|
57
|
+
gating install read as unverified — every reporter then said "no analysis
|
|
58
|
+
moment is active" about a project the plugin was gating on every turn.
|
|
59
|
+
- **`verity init` knows the plugin is here, and says so before it acts.** It
|
|
60
|
+
does two opposite things depending on that answer — write this project's own
|
|
61
|
+
hooks, or leave them to the plugin — and it used to choose silently. It now
|
|
62
|
+
names the install it found and the division of labour, and `--no-plugin`
|
|
63
|
+
overrules it. It detects a plugin in a project the plugin has never set up,
|
|
64
|
+
which is the ordinary case for someone whose first Verity install *is* the
|
|
65
|
+
plugin.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## [0.32.1] — 2026-09-09
|
|
69
|
+
|
|
70
|
+
**The gate said it was on while it wasn't.** Every fix here is the same failure
|
|
71
|
+
seen from a different side: Verity reported itself wired and watching, and the
|
|
72
|
+
commit went through unreviewed. Two of them were shipped by 0.32.0 itself.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm install -g @codacy/verity-cli # binary only — no re-run of init needed
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 🔒 The gate is on when it says it is
|
|
79
|
+
|
|
80
|
+
- **Typing `2,3` at the setup question left you with no git gate.** The review
|
|
81
|
+
moments used to be answered by number — `2,3` *was* the answer — and 0.32.0's
|
|
82
|
+
arrow-key rewrite kept the digit shortcut on the single-select question and
|
|
83
|
+
lost it on this one. A digit moved the cursor without ticking anything, the
|
|
84
|
+
comma was ignored, and Enter then confirmed the untouched default. You got
|
|
85
|
+
`stop` alone: no pre-commit hook was wired, and the git gate silently did not
|
|
86
|
+
exist. Reported from the field as *"pre-commit hooks are not added any more"*.
|
|
87
|
+
A digit toggles again, and the prompt's own legend now names the keys it
|
|
88
|
+
accepts.
|
|
89
|
+
- **`git add … && git commit` was never reviewed.** The hook runs *before* the
|
|
90
|
+
command, so the index it inspected had not had the `add` applied yet: nothing
|
|
91
|
+
staged, and the gate took its "nothing to review" exit — a silent allow, with
|
|
92
|
+
no trace that a commit had gone unreviewed. It is the shape an agent writes by
|
|
93
|
+
default, so it was most of the commit gate's coverage. The gate now works out
|
|
94
|
+
what the command is about to stage, using git's own pathspec matching, and
|
|
95
|
+
understands `git commit -a` and `git commit <paths>`. Where it genuinely
|
|
96
|
+
cannot — a pathspec the shell expands before git sees it, an interactive
|
|
97
|
+
`git add -p` — it says so and lets the commit through **loudly**, rather than
|
|
98
|
+
reviewing a set it guessed at.
|
|
99
|
+
- **A verdict the CLI does not understand is no longer drawn as a pass.** An
|
|
100
|
+
absent or unrecognised gate decision — a truncated response, a proxy error
|
|
101
|
+
page — reached the `✓ PASS` line. It is reported as unreviewed.
|
|
102
|
+
- **`verity hooks install --moments <typo>` no longer removes every gate.**
|
|
103
|
+
Unrecognised moments were dropped in silence, so a single misspelling
|
|
104
|
+
reconciled the hooks down to nothing and printed success. It is an error now;
|
|
105
|
+
`--moments none` is still the way to turn them off on purpose.
|
|
106
|
+
- **`verity init` no longer overwrites the moments you chose.** Re-running init
|
|
107
|
+
to heal a config replaced an answer you had set with `verity config
|
|
108
|
+
git-moments`, substituting the shipped default. Your answer is now recorded
|
|
109
|
+
as yours, kept, and named when init keeps it.
|
|
110
|
+
- **A linked worktree gates what the main checkout gates.** `.verity/` is
|
|
111
|
+
gitignored, so a fresh `git worktree add` had no configuration of its own and
|
|
112
|
+
the guard there gated nothing while `verity hooks check` reported it wired. A
|
|
113
|
+
worktree's own `.verity/config.json` still wins when it has one — including an
|
|
114
|
+
explicit `none` — and `hooks check` names the file it inherited from.
|
|
115
|
+
- **`verity status`, `verity doctor` and `verity hooks check` answer from one
|
|
116
|
+
place.** They could disagree about whether a hook was wired, and two of them
|
|
117
|
+
were wrong from a subdirectory.
|
|
118
|
+
|
|
119
|
+
### 🔧 Fixed
|
|
120
|
+
|
|
121
|
+
- `verity status --limit` rejects a value that is not a number instead of
|
|
122
|
+
quietly ignoring it.
|
|
123
|
+
|
|
124
|
+
### Migrating
|
|
125
|
+
|
|
126
|
+
Upgrading the binary is enough for six of the seven — but not for the moments
|
|
127
|
+
question. If you set this project up **on 0.32.0** and answered that question by
|
|
128
|
+
typing numbers, the gates you asked for were never recorded, and a new binary
|
|
129
|
+
cannot know what you meant. Check and repair in one command each:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
verity status # Moments: is pre-commit / pre-push listed?
|
|
133
|
+
verity config git-moments commit,push # set what you meant; init now keeps it
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Projects set up before 0.32.0, or set up with the arrow keys, are unaffected.
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
## [0.32.0] — 2026-09-08
|
|
140
|
+
|
|
141
|
+
**Setup takes seconds, not minutes — and the reviewer sees more than the diff.**
|
|
142
|
+
Installing Verity used to be two steps: `verity init`, then a Claude Code
|
|
143
|
+
session for `/verity-setup` to write your Standard, which took minutes and was
|
|
144
|
+
the step people skipped. It is now one command that takes seconds: `init`
|
|
145
|
+
builds the Standard from your codebase, or downloads the one your team already
|
|
146
|
+
registered. On the other side, the reviewer stops judging a change in a vacuum:
|
|
147
|
+
it sees the callers, tests and contracts around what you touched, and it can
|
|
148
|
+
only block on things it actually has evidence for. This entry folds in
|
|
149
|
+
0.31.2 and 0.31.4, which shipped without their own headings; 0.31.3 and 0.31.1
|
|
150
|
+
are below.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npm install -g @codacy/verity-cli
|
|
154
|
+
verity init # re-run in existing projects: heals the config, records your answers
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### ⚡ One command, no second step
|
|
158
|
+
|
|
159
|
+
- **`verity init` finishes the whole job.** It used to stop halfway and hand you
|
|
160
|
+
off to `/verity-setup` in Claude Code to synthesize the Standard — a model
|
|
161
|
+
turn that took minutes, needed a session open, and was easy to skip. `init`
|
|
162
|
+
now does it on its own, in seconds, and the gate is live on your next session.
|
|
163
|
+
`/verity-setup` still exists but is optional: it is for adding
|
|
164
|
+
project-specific rules on top, not for getting started.
|
|
165
|
+
- **If your team already has a Standard, it comes down from the cloud.** Any
|
|
166
|
+
repository registered before — by a teammate, by you on another machine, by
|
|
167
|
+
CI — already has its Standard on Verity, and `init` fetches it and writes the
|
|
168
|
+
team's configuration to disk (when you are signed in). Cloning a repo that
|
|
169
|
+
already uses Verity is: install, `init`, done.
|
|
170
|
+
- **If nobody has one yet, `init` derives it** from the codebase — see below.
|
|
50
171
|
|
|
51
|
-
### 🎯
|
|
172
|
+
### 🎯 `init` derives the Standard when there is nothing to adopt
|
|
52
173
|
|
|
53
174
|
- **`verity init` now derives the Standard itself.** When the service has nothing
|
|
54
175
|
for this repository, the CLI reads the codebase — languages counted, frameworks
|
|
@@ -117,30 +238,6 @@ rule list degrades into a filter over it (VRT-108); Semgrep's ids are deliberate
|
|
|
117
238
|
not derived yet, so the tool is dropped rather than configured empty. Every one of
|
|
118
239
|
those is now an assertion.
|
|
119
240
|
|
|
120
|
-
### ⌨️ The setup questions answer to arrow keys
|
|
121
|
-
|
|
122
|
-
- **`verity init` no longer asks you to type a number.** Analysis intensity and
|
|
123
|
-
the review moments are arrow-key lists: `↑↓` to move, `space` to toggle a
|
|
124
|
-
moment, `enter` to confirm — with the recommended answer already under the
|
|
125
|
-
cursor and the default set already ticked, so **Enter alone still gives exactly
|
|
126
|
-
what it gave before**.
|
|
127
|
-
- **Digits still work.** This prompt used to be "type a number", and muscle memory
|
|
128
|
-
should not be punished for an interface improvement — a digit moves the cursor
|
|
129
|
-
rather than confirming, so it composes with the arrows instead of being a
|
|
130
|
-
second, hidden way to answer. `j`/`k` move too.
|
|
131
|
-
- **An empty moment selection is refused, not accepted.** Unticking everything and
|
|
132
|
-
pressing Enter says so instead of installing a review tool that reviews nothing
|
|
133
|
-
— the same invariant the typed prompt held by falling back.
|
|
134
|
-
- **Three rungs of degradation, same default at each.** Arrow keys on a real
|
|
135
|
-
terminal; the typed number prompt when the terminal cannot do raw mode; the
|
|
136
|
-
default when there is no terminal at all or stdin ends. An interface improvement
|
|
137
|
-
must not become a new way for a setup to get stuck — CI, pipes and agent Bash
|
|
138
|
-
calls behave exactly as before.
|
|
139
|
-
- **The terminal is restored on every exit,** including Ctrl+C, which restores
|
|
140
|
-
before exiting: raw mode left on outlives the process and makes the user's next
|
|
141
|
-
shell prompt unusable.
|
|
142
|
-
|
|
143
|
-
|
|
144
241
|
### 🤝 Joining a project that already has a Standard
|
|
145
242
|
|
|
146
243
|
- **`verity init` now offers the Standard the service already holds** for this
|
|
@@ -169,20 +266,28 @@ those is now an assertion.
|
|
|
169
266
|
silent-forever failure: decline on Monday meaning to synthesize later, forget,
|
|
170
267
|
and every later run stays quiet while the project has no local config at all.
|
|
171
268
|
|
|
172
|
-
###
|
|
173
|
-
|
|
174
|
-
- **An upgrade no longer removes the gates you chose.** `.verity/setup.json` only
|
|
175
|
-
exists from this release, so every project configured before it had no recorded
|
|
176
|
-
answer — including everyone who picked commit/push gates through the old
|
|
177
|
-
`/verity-setup`. The first upgraded `verity init` fell back to the shipped
|
|
178
|
-
default and reconciled those gates away, turning Stop back on. What you chose is
|
|
179
|
-
still legible in `.claude/settings.json`, so init reads it.
|
|
180
|
-
- `verity doctor` names the artifact that is actually missing instead of telling
|
|
181
|
-
you to run a phase that will decline: with a Standard adopted and no analysis
|
|
182
|
-
config, `/verity-setup` would have answered "already configured".
|
|
183
|
-
- The setup skill learned the same case — Standard present, analysis config
|
|
184
|
-
missing — and writes only the config rather than re-synthesizing the Standard.
|
|
269
|
+
### ⌨️ The setup questions answer to arrow keys
|
|
185
270
|
|
|
271
|
+
- **`verity init` no longer asks you to type a number.** Analysis intensity and
|
|
272
|
+
the review moments are arrow-key lists: `↑↓` to move, `space` to toggle a
|
|
273
|
+
moment, `enter` to confirm — with the recommended answer already under the
|
|
274
|
+
cursor and the default set already ticked, so **Enter alone still gives exactly
|
|
275
|
+
what it gave before**.
|
|
276
|
+
- **Digits still work.** This prompt used to be "type a number", and muscle memory
|
|
277
|
+
should not be punished for an interface improvement — a digit moves the cursor
|
|
278
|
+
rather than confirming, so it composes with the arrows instead of being a
|
|
279
|
+
second, hidden way to answer. `j`/`k` move too.
|
|
280
|
+
- **An empty moment selection is refused, not accepted.** Unticking everything and
|
|
281
|
+
pressing Enter says so instead of installing a review tool that reviews nothing
|
|
282
|
+
— the same invariant the typed prompt held by falling back.
|
|
283
|
+
- **Three rungs of degradation, same default at each.** Arrow keys on a real
|
|
284
|
+
terminal; the typed number prompt when the terminal cannot do raw mode; the
|
|
285
|
+
default when there is no terminal at all or stdin ends. An interface improvement
|
|
286
|
+
must not become a new way for a setup to get stuck — CI, pipes and agent Bash
|
|
287
|
+
calls behave exactly as before.
|
|
288
|
+
- **The terminal is restored on every exit,** including Ctrl+C, which restores
|
|
289
|
+
before exiting: raw mode left on outlives the process and makes the user's next
|
|
290
|
+
shell prompt unusable.
|
|
186
291
|
|
|
187
292
|
**Setup is one command.** `verity init` and `/verity-setup` were two half-flows
|
|
188
293
|
that overlapped — and the overlap was not harmless: init wired the Stop hook,
|
|
@@ -195,10 +300,11 @@ with one entry point.
|
|
|
195
300
|
- **It asks the questions whose answers it acts on** — analysis intensity, when
|
|
196
301
|
to review (on stop / before commit / before push), and cost & usage telemetry.
|
|
197
302
|
On the terminal, before anything is wired to them.
|
|
198
|
-
- **Then it
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
command instead. `verity setup` is
|
|
303
|
+
- **Then it finishes the Standard itself** (adopted from the service or derived
|
|
304
|
+
from the codebase — see above). Handing off to Claude Code for `/verity-setup`
|
|
305
|
+
survives only as the fallback when neither produced one, and if you are
|
|
306
|
+
already inside a session it prints the one command instead. `verity setup` is
|
|
307
|
+
an alias for the same command.
|
|
202
308
|
- **`--yes` takes every recommended answer and asks nothing** (CI, scripts,
|
|
203
309
|
agents) — including the GitHub login, whose unattended answer is skip, so the
|
|
204
310
|
project stays local-only until you run `verity login`. `--no-setup` stops after
|
|
@@ -259,50 +365,76 @@ with one entry point.
|
|
|
259
365
|
terminal (Ctrl+D, a pty that ends) falls back to the default instead of
|
|
260
366
|
crashing init between copying the skills and wiring the hooks.
|
|
261
367
|
|
|
262
|
-
###
|
|
368
|
+
### 🚪 A project that was never set up is left alone
|
|
369
|
+
|
|
370
|
+
Every hook now asks the same question the same way. `verity analyze` used to run
|
|
371
|
+
in a project that had never seen `verity init`, create `.verity/` there, and then —
|
|
372
|
+
with no baseline to compare against — review nothing. It now stands down like the
|
|
373
|
+
other hooks always have, so installing Verity no longer scatters state through
|
|
374
|
+
directories you merely opened. On the first session in such a project, Claude is
|
|
375
|
+
told once that the gate is off here and can offer to run `verity init`. Works
|
|
376
|
+
correctly from linked git worktrees.
|
|
377
|
+
|
|
378
|
+
### 🔭 The reviewer sees the neighborhood
|
|
379
|
+
|
|
380
|
+
Measured on real commits: every change had at least one caller outside the
|
|
381
|
+
diff (median five), and more than half had a test referencing a changed symbol.
|
|
382
|
+
Verity sent none of it. Now it does — server-side, so it reached every
|
|
383
|
+
installed CLI on deploy day:
|
|
384
|
+
|
|
385
|
+
- **Context is what the agent actually read.** The files sent alongside your
|
|
386
|
+
change are the ones the agent opened this task — not the ten most recently
|
|
387
|
+
touched files in the repo, which is what it was.
|
|
388
|
+
- **Call sites of changed symbols**, one line each — including callers of a
|
|
389
|
+
function whose body you edited but whose name isn't in the diff.
|
|
390
|
+
- **At commit and push, full excerpts.** Contracts first (interfaces your change
|
|
391
|
+
implements), then tests, then callers, with real line numbers so the reviewer
|
|
392
|
+
can cite them.
|
|
393
|
+
- **Findings stay anchored on your change.** A problem your edit causes at a
|
|
394
|
+
call site is reported against the file you changed. A pre-existing problem in
|
|
395
|
+
a file Verity only glanced at is out of scope and cannot block you.
|
|
396
|
+
- **Every run says whether this fired**, and why not when it didn't (no tool, no
|
|
397
|
+
diff, no symbols, no sites, timeout) — in the evidence log and the guard's
|
|
398
|
+
coverage block. "Never fires" and "not deployed" can no longer look the same.
|
|
399
|
+
- Fixed on the way: the Stop-hook review was running at half its intended file
|
|
400
|
+
budget (20 files, not 40), and context files reached the reviewer without the
|
|
401
|
+
line numbers the prompt promised.
|
|
402
|
+
|
|
403
|
+
### ⚖️ The gate only blocks on evidence it has
|
|
404
|
+
|
|
405
|
+
Raised from a customer's production runs: 134 of 655 runs blocked, and only a
|
|
406
|
+
third of those blocks cited a line of production source. Two things decide
|
|
407
|
+
whether a finding blocks — how bad *this instance* is, and what *kind* of rule
|
|
408
|
+
it is — and both were owned by the wrong party.
|
|
409
|
+
|
|
410
|
+
- **Severity is the reviewer's.** It used to be overwritten with the Standard's
|
|
411
|
+
per-pattern constant, so a finding the reviewer rated `low` (*"appears to be a
|
|
412
|
+
fixture value"*) blocked as `critical`. The reviewer's judgement now stands.
|
|
413
|
+
- **Category comes from your Standard.** It used to be a label the model
|
|
414
|
+
re-picked every run — the same pattern landed as `security` on one run and
|
|
415
|
+
`quality` on the next, and at `high` that label *is* the block. It is now
|
|
416
|
+
derived from where the pattern sits in your Standard. Note the direction: for
|
|
417
|
+
patterns you declared under security, every high instance now blocks where the
|
|
418
|
+
coin flip previously let some through.
|
|
419
|
+
- **A secrets finding needs a witness.** `no-hardcoded-secrets` blocks only when
|
|
420
|
+
a static tool saw it too, not on the model's word alone.
|
|
421
|
+
- **`exclude_paths` in your Standard actually excludes now.** It was advertised
|
|
422
|
+
and read by nothing.
|
|
423
|
+
|
|
424
|
+
### 🔧 Fixed
|
|
425
|
+
|
|
426
|
+
- **An upgrade no longer removes the gates you chose.** `.verity/setup.json` only
|
|
427
|
+
exists from this release, so every project configured before it had no recorded
|
|
428
|
+
answer — including everyone who picked commit/push gates through the old
|
|
429
|
+
`/verity-setup`. The first upgraded `verity init` fell back to the shipped
|
|
430
|
+
default and reconciled those gates away, turning Stop back on. What you chose is
|
|
431
|
+
still legible in `.claude/settings.json`, so init reads it.
|
|
432
|
+
- `verity doctor` names the artifact that is actually missing instead of telling
|
|
433
|
+
you to run a phase that will decline: with a Standard adopted and no analysis
|
|
434
|
+
config, `/verity-setup` would have answered "already configured".
|
|
435
|
+
- The setup skill learned the same case — Standard present, analysis config
|
|
436
|
+
missing — and writes only the config rather than re-synthesizing the Standard.
|
|
263
437
|
|
|
264
|
-
- **`git add x && git commit` was never reviewed.** A Claude Code hook runs
|
|
265
|
-
*before* the command, so the index it read had not had the `add` applied:
|
|
266
|
-
no files, and the gate's "nothing staged" exit — a silent allow, with no
|
|
267
|
-
trace that a commit had gone unreviewed. It is the shape an agent writes by
|
|
268
|
-
default, so it was most of the pre-commit gate's coverage. The gate now works
|
|
269
|
-
out what the command will stage, using git's own pathspec matching, and
|
|
270
|
-
handles `git commit -a`. Where it cannot — a pathspec the shell expands,
|
|
271
|
-
`git add -p` — it says so and lets the commit through **loudly** rather than
|
|
272
|
-
reviewing a set it guessed at.
|
|
273
|
-
- **Uninstalling the Claude Code plugin left projects with no gate at all.**
|
|
274
|
-
Whether the plugin owned a project's hooks was decided by whether its install
|
|
275
|
-
directory existed — and that directory survives an uninstall. Every project
|
|
276
|
-
the plugin had ever run in went on standing its own hooks down for a plugin
|
|
277
|
-
that was gone. Ownership now comes from Claude Code's install registry, and
|
|
278
|
-
`verity init` clears the stale marker.
|
|
279
|
-
- **`verity init` now says which mode it is in before it acts on it** — the
|
|
280
|
-
plugin's hooks or this project's own — with the install it found and
|
|
281
|
-
`--no-plugin` to overrule it. It does two opposite things depending on that
|
|
282
|
-
answer, and it used to make the choice silently.
|
|
283
|
-
- **A plugin installed from a local marketplace is recognised too.** Claude Code
|
|
284
|
-
runs a marketplace served from a *directory* out of that directory while
|
|
285
|
-
recording the cache copy it made, so the two paths disagree and a live,
|
|
286
|
-
gating install read as unverified — every reporter then said "no analysis
|
|
287
|
-
moment is active" about a project the plugin was gating on every turn.
|
|
288
|
-
- **A repository can no longer switch the gate off by shipping a file.**
|
|
289
|
-
Ownership was claimed by `.verity/.plugin-active`, which is a file inside the
|
|
290
|
-
project. One naming any directory that happened to exist disabled every hook
|
|
291
|
-
in that project. An install Claude Code cannot vouch for is now ignored.
|
|
292
|
-
- **`verity status` and `verity doctor` agree with `verity hooks check`.** Under
|
|
293
|
-
the plugin both used to report "Stop: off" and tell you to install hooks that
|
|
294
|
-
were firing on every turn. All three read one answer now, and it is correct
|
|
295
|
-
from a subdirectory too.
|
|
296
|
-
- **A commit gate on a linked worktree gates what the main checkout gates.**
|
|
297
|
-
`.verity/config.json` is machine-local, so every `git worktree add` started
|
|
298
|
-
with no moments and reviewed nothing.
|
|
299
|
-
- **A verdict the client does not understand is no longer rendered as a pass.**
|
|
300
|
-
An absent or unrecognised gate decision — a truncated response, a proxy error
|
|
301
|
-
page — reached the "✓ PASS" line. It is reported as unreviewed.
|
|
302
|
-
- **`verity hooks install --moments <typo>` no longer removes every gate.**
|
|
303
|
-
Unrecognised moments were dropped, so a misspelling reconciled to nothing and
|
|
304
|
-
reported success. It is an error now; `--moments none` turns them off on
|
|
305
|
-
purpose.
|
|
306
438
|
|
|
307
439
|
## [0.31.3] — 2026-09-03
|
|
308
440
|
|
package/bin/verity.js
CHANGED
|
@@ -12927,18 +12927,27 @@ function realpathOr(p) {
|
|
|
12927
12927
|
function isWithin(want, dir) {
|
|
12928
12928
|
return want === dir || want.startsWith(dir + import_node_path7.sep);
|
|
12929
12929
|
}
|
|
12930
|
+
function entryAppliesHere(entry, here) {
|
|
12931
|
+
const e = entry;
|
|
12932
|
+
const scope2 = typeof e?.scope === "string" ? e.scope : "user";
|
|
12933
|
+
if (scope2 === "user") return true;
|
|
12934
|
+
const forProject = typeof e?.projectPath === "string" ? realpathOr(e.projectPath) : "";
|
|
12935
|
+
return forProject === here;
|
|
12936
|
+
}
|
|
12930
12937
|
function registrySays(pluginRoot) {
|
|
12931
12938
|
const plugins = readJsonFile((0, import_node_path7.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
|
|
12932
12939
|
if (!plugins || typeof plugins !== "object") return "unverified";
|
|
12933
12940
|
const want = (0, import_node_path7.resolve)(pluginRoot);
|
|
12941
|
+
const here = realpathOr(repoRoot());
|
|
12934
12942
|
const markets = marketplaceLocations();
|
|
12935
12943
|
for (const [key, value] of Object.entries(plugins)) {
|
|
12936
12944
|
const at = key.lastIndexOf("@");
|
|
12937
12945
|
const source = at > 0 ? markets.get(key.slice(at + 1)) : void 0;
|
|
12938
|
-
const
|
|
12946
|
+
const applicable = (Array.isArray(value) ? value : []).filter((entry) => entryAppliesHere(entry, here));
|
|
12947
|
+
const claims = applicable.some((entry) => {
|
|
12939
12948
|
const installPath = entry?.installPath;
|
|
12940
12949
|
return typeof installPath === "string" && (0, import_node_path7.resolve)(installPath) === want;
|
|
12941
|
-
}) || source !== void 0 && isWithin(want, source);
|
|
12950
|
+
}) || source !== void 0 && applicable.length > 0 && isWithin(want, source);
|
|
12942
12951
|
if (claims) {
|
|
12943
12952
|
if (enabledPluginSetting(key) === false) return "gone";
|
|
12944
12953
|
return (0, import_node_fs6.existsSync)(pluginRoot) ? "live" : "gone";
|
|
@@ -12998,11 +13007,7 @@ function registeredVerityPlugin() {
|
|
|
12998
13007
|
const e = entry;
|
|
12999
13008
|
const installPath = typeof e.installPath === "string" ? e.installPath : "";
|
|
13000
13009
|
if (!installPath || !(0, import_node_fs6.existsSync)(installPath)) continue;
|
|
13001
|
-
|
|
13002
|
-
if (scope2 !== "user") {
|
|
13003
|
-
const forProject = typeof e.projectPath === "string" ? realpathOr(e.projectPath) : "";
|
|
13004
|
-
if (forProject !== here) continue;
|
|
13005
|
-
}
|
|
13010
|
+
if (!entryAppliesHere(entry, here)) continue;
|
|
13006
13011
|
return { pluginRoot: installPath, version: typeof e.version === "string" ? e.version : null };
|
|
13007
13012
|
}
|
|
13008
13013
|
}
|
|
@@ -20867,7 +20872,7 @@ function channelSilence(input) {
|
|
|
20867
20872
|
// src/lib/cli-version.ts
|
|
20868
20873
|
function cliVersion() {
|
|
20869
20874
|
try {
|
|
20870
|
-
return true ? "0.32.1-experimental.
|
|
20875
|
+
return true ? "0.32.1-experimental.c640abd" : "dev";
|
|
20871
20876
|
} catch {
|
|
20872
20877
|
return "dev";
|
|
20873
20878
|
}
|
|
@@ -24615,36 +24620,50 @@ async function runGuard(opts, globals) {
|
|
|
24615
24620
|
process.exit(2);
|
|
24616
24621
|
}
|
|
24617
24622
|
resetIter(moment);
|
|
24623
|
+
const notice = verdictNotice({
|
|
24624
|
+
decision,
|
|
24625
|
+
moment,
|
|
24626
|
+
verb,
|
|
24627
|
+
covLine,
|
|
24628
|
+
covDetail,
|
|
24629
|
+
link,
|
|
24630
|
+
viewUrl,
|
|
24631
|
+
narrative: response.assessment?.narrative ?? ""
|
|
24632
|
+
});
|
|
24633
|
+
emitAllowNotice(notice.user, notice.agent);
|
|
24634
|
+
}
|
|
24635
|
+
function verdictNotice(ctx) {
|
|
24636
|
+
const { moment, verb, covLine, covDetail, link, viewUrl } = ctx;
|
|
24637
|
+
const decision = ctx.decision ?? "(unrecognised)";
|
|
24638
|
+
const report = viewUrl ? `
|
|
24639
|
+
Report: ${viewUrl}` : "";
|
|
24618
24640
|
if (decision === "FAIL") {
|
|
24619
|
-
const narrative =
|
|
24620
|
-
|
|
24621
|
-
`\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding (${covLine})${link}`,
|
|
24622
|
-
`Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}
|
|
24623
|
-
${covDetail}${
|
|
24624
|
-
|
|
24625
|
-
|
|
24626
|
-
|
|
24627
|
-
|
|
24628
|
-
`\u26A0 Verity ${moment}: WARN \u2014 proceeding (${covLine})${link}`,
|
|
24629
|
-
`Verity ${moment} review: WARN (proceeding).
|
|
24630
|
-
${covDetail}${
|
|
24631
|
-
|
|
24632
|
-
|
|
24633
|
-
|
|
24634
|
-
|
|
24635
|
-
`\u2713 Verity ${moment}: PASS (${covLine})${link}`,
|
|
24636
|
-
`Verity ${moment} review: PASS.
|
|
24637
|
-
${covDetail}${
|
|
24638
|
-
|
|
24639
|
-
);
|
|
24640
|
-
} else {
|
|
24641
|
-
emitAllowNotice(
|
|
24642
|
-
`\u26A0 Verity ${moment}: no verdict came back \u2014 ${verb === "commit" ? "committed" : "pushed"} WITHOUT a usable review (${covLine})${link}`,
|
|
24643
|
-
`Verity ${moment}: the service answered with no recognisable gate decision (${decision}); the ${verb} was allowed, but nothing reviewed it. Treat this as unreviewed, not as a pass.
|
|
24644
|
-
${covDetail}${viewUrl ? `
|
|
24645
|
-
Report: ${viewUrl}` : ""}`
|
|
24646
|
-
);
|
|
24641
|
+
const narrative = ctx.narrative ?? "";
|
|
24642
|
+
return {
|
|
24643
|
+
user: `\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding (${covLine})${link}`,
|
|
24644
|
+
agent: `Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}
|
|
24645
|
+
${covDetail}${report}`
|
|
24646
|
+
};
|
|
24647
|
+
}
|
|
24648
|
+
if (decision === "WARN") {
|
|
24649
|
+
return {
|
|
24650
|
+
user: `\u26A0 Verity ${moment}: WARN \u2014 proceeding (${covLine})${link}`,
|
|
24651
|
+
agent: `Verity ${moment} review: WARN (proceeding).
|
|
24652
|
+
${covDetail}${report}`
|
|
24653
|
+
};
|
|
24654
|
+
}
|
|
24655
|
+
if (decision === "PASS") {
|
|
24656
|
+
return {
|
|
24657
|
+
user: `\u2713 Verity ${moment}: PASS (${covLine})${link}`,
|
|
24658
|
+
agent: `Verity ${moment} review: PASS.
|
|
24659
|
+
${covDetail}${report}`
|
|
24660
|
+
};
|
|
24647
24661
|
}
|
|
24662
|
+
return {
|
|
24663
|
+
user: `\u26A0 Verity ${moment}: no verdict came back \u2014 ${verb === "commit" ? "committed" : "pushed"} WITHOUT a usable review (${covLine})${link}`,
|
|
24664
|
+
agent: `Verity ${moment}: the service answered with no recognisable gate decision (${decision}); the ${verb} was allowed, but nothing reviewed it. Treat this as unreviewed, not as a pass.
|
|
24665
|
+
${covDetail}${report}`
|
|
24666
|
+
};
|
|
24648
24667
|
}
|
|
24649
24668
|
function writeBlockMessage(moment, response, covDetail) {
|
|
24650
24669
|
const label2 = moment === "pre-commit" ? "pre-commit" : "pre-push";
|
|
@@ -26667,7 +26686,7 @@ function registerInitCommand(program2) {
|
|
|
26667
26686
|
...telemetryChoice ? { telemetry: telemetryChoice } : {},
|
|
26668
26687
|
init: {
|
|
26669
26688
|
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26670
|
-
cli_version: true ? "0.32.1-experimental.
|
|
26689
|
+
cli_version: true ? "0.32.1-experimental.c640abd" : "dev"
|
|
26671
26690
|
}
|
|
26672
26691
|
});
|
|
26673
26692
|
} catch (err) {
|
|
@@ -27347,8 +27366,8 @@ function registerTelemetryCommands(program2) {
|
|
|
27347
27366
|
}
|
|
27348
27367
|
|
|
27349
27368
|
// src/cli.ts
|
|
27350
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.32.1-experimental.
|
|
27351
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.1-experimental.
|
|
27369
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.32.1-experimental.c640abd").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) => {
|
|
27370
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.1-experimental.c640abd");
|
|
27352
27371
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
27353
27372
|
try {
|
|
27354
27373
|
await foldLegacyLocalCredential();
|
package/package.json
CHANGED