vigiles 14.13.5 → 15.0.1
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/README.md +8 -4
- package/dist/adapters/claude-code/run-scripts.d.ts +55 -5
- package/dist/adapters/claude-code/run-scripts.js +105 -24
- package/dist/audit-report.d.ts +23 -3
- package/dist/audit-report.js +10 -1
- package/dist/audit-report.template.html +12 -12
- package/dist/audit-score.d.ts +45 -10
- package/dist/audit-score.js +129 -28
- package/dist/check-count.d.ts +40 -0
- package/dist/check-count.js +136 -0
- package/dist/cli-flag-check.d.ts +73 -0
- package/dist/cli-flag-check.js +198 -0
- package/dist/cli.js +236 -34
- package/dist/core/lethal-trifecta.d.ts +31 -2
- package/dist/core/lethal-trifecta.js +32 -7
- package/dist/core/skill-resources.js +41 -9
- package/dist/coverage-evidence.d.ts +115 -0
- package/dist/coverage-evidence.js +268 -0
- package/dist/eval.js +6 -0
- package/dist/harness-assert.d.ts +7 -2
- package/dist/harness-assert.js +34 -6
- package/dist/harness-test.js +4 -0
- package/dist/plugin-declaration.d.ts +81 -0
- package/dist/plugin-declaration.js +139 -0
- package/dist/run-hook.d.ts +55 -134
- package/dist/run-hook.js +19 -373
- package/dist/run-script.d.ts +168 -0
- package/dist/run-script.js +398 -0
- package/dist/scan-core.js +56 -2
- package/dist/scan-files.js +9 -1
- package/dist/scan.d.ts +27 -0
- package/dist/scan.js +24 -2
- package/dist/skill-reachability.d.ts +68 -0
- package/dist/skill-reachability.js +205 -0
- package/dist/test-coverage-files.d.ts +7 -3
- package/dist/test-coverage-files.js +48 -15
- package/dist/test-coverage.d.ts +70 -5
- package/dist/test-coverage.js +128 -22
- package/dist/testing.d.ts +3 -0
- package/dist/testing.js +16 -2
- package/dist/unit.d.ts +2 -0
- package/dist/unit.js +5 -1
- package/package.json +1 -1
- package/skills/test-harness/SKILL.md +41 -0
package/README.md
CHANGED
|
@@ -130,13 +130,14 @@ It's free and open-source, runs entirely on your machine, and never bills per to
|
|
|
130
130
|
<img src="vigiles-audit.png" width="760" alt="vigiles audit report for my-plugin: a verdict header reading 'Two one-line fixes away from a B.' next to a C (77/100) grade, a five-category strip (Truthfulness, Triggering, Structure, Safety, Tested), ranked fix cards with '+N pts' impact badges, broken-reference findings — and, lower down and badged experimental, a 'Your rules → enforced' preview mapping a prose rule the config silently turns off" />
|
|
131
131
|
</p>
|
|
132
132
|
|
|
133
|
-
**Like Google's Lighthouse, but for your agent harness.** One command grades it A–F across
|
|
133
|
+
**Like Google's Lighthouse, but for your agent harness.** One command grades it A–F across six categories, leads with a plain-English verdict — _"two one-line fixes away from a B"_ — and ranks every fix by the points it buys back:
|
|
134
134
|
|
|
135
135
|
- **Truthfulness** — do the references resolve?
|
|
136
136
|
- **Triggering** — do skills fire, without colliding?
|
|
137
137
|
- **Structure** — are tool contracts and configs valid?
|
|
138
138
|
- **Safety** — any way for the agent to leak your data?
|
|
139
|
-
- **Tested** — does the harness ship tests?
|
|
139
|
+
- **Tested** — does the harness ship deterministic tests?
|
|
140
|
+
- **Evaluated** — has anything measured whether your skills actually _fire_? (Distinct from `0`: if nothing asked, it says **not measured**.)
|
|
140
141
|
|
|
141
142
|
And it closes the loop from prose to enforcement: **your rules → enforced** maps each rule you wrote to the lint rule that actually enforces it — already on, one config line away, or silently turned off (below).
|
|
142
143
|
|
|
@@ -208,8 +209,10 @@ Or run it yourself:
|
|
|
208
209
|
|
|
209
210
|
```bash
|
|
210
211
|
npx vigiles init # sets up the typed spec for structural rules (non-destructive — eject reverses), adds CI,
|
|
211
|
-
# installs vigiles's skills + hooks as a Claude Code plugin
|
|
212
|
-
# ~/.claude/,
|
|
212
|
+
# installs vigiles's skills + hooks as a Claude Code plugin. The plugin
|
|
213
|
+
# CONTENT goes to ~/.claude/, never your repo; init also commits a two-key
|
|
214
|
+
# reference to it in .claude/settings.json so teammates get prompted to
|
|
215
|
+
# install it rather than silently missing it. Codex: skills install globally.
|
|
213
216
|
```
|
|
214
217
|
|
|
215
218
|
**Already have a harness, or a non-JS repo?** `npx vigiles init --ci-only` sets up just the CI integrity gate — nothing installed, zero conflict. **[When to use gate vs full →](docs/agent-setup.md#non-interactive-setup-agents--ci)**
|
|
@@ -230,6 +233,7 @@ The **hooks** keep it honest in-loop — nudging the agent to tag a linter-rule
|
|
|
230
233
|
- **Both lint and test** by default; scope with `--lint` / `--test`.
|
|
231
234
|
- **Already have a CLAUDE.md / AGENTS.md, skills, or subagents? `audit` and `lint` read them as-is** — nothing is moved or rewritten. For the structural rules that want a typed spec, `init` sets one up **non-destructively** (`eject` undoes it).
|
|
232
235
|
- Adds `vigiles` to `devDependencies`; installs the Claude Code plugin (skills + hooks) via the marketplace — globally, never vendored.
|
|
236
|
+
- Declares that plugin in your repo's `.claude/settings.json` (`extraKnownMarketplaces` + `enabledPlugins`, merged into whatever is already there). This is a **reference, not content** — nothing is vendored, and it does **not** install the plugin for a teammate: an external-source plugin declared project-level [doesn't load until each person installs it](https://code.claude.com/docs/en/discover-plugins#configure-team-marketplaces). What it buys is that Claude Code **prompts** them with the install command, instead of a fresh clone silently having the npm package and none of its skills.
|
|
233
237
|
- Wires CI as a `zernie/vigiles@v1` workflow (needs only read + PR-comment permissions) that posts a sticky PR comment + a `valid` output.
|
|
234
238
|
|
|
235
239
|
Targets Claude Code and Codex out of the box, or [your own harness](docs/authoring-an-adapter.md). Prefer to write tests yourself? JS **or** TS (`*.harness.{mjs,ts}`) — run with `npx vigiles test`.
|
|
@@ -1,8 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The outcome of running one script.
|
|
3
|
+
*
|
|
4
|
+
* `"vacuous"` — the script exited 0 and reported that it made ZERO checks. It
|
|
5
|
+
* neither passed nor failed: nothing was verified. See {@link statusFor}.
|
|
6
|
+
*/
|
|
7
|
+
export type ScriptStatus = "pass" | "skip" | "fail" | "vacuous";
|
|
2
8
|
export interface ScriptRunResult {
|
|
3
9
|
readonly file: string;
|
|
4
10
|
readonly code: number;
|
|
5
11
|
readonly status: ScriptStatus;
|
|
12
|
+
/**
|
|
13
|
+
* How many checks the script reported making, or `undefined` when it reported
|
|
14
|
+
* nothing at all — a script that never imports `vigiles/testing` has no way to
|
|
15
|
+
* report, and that silence is NOT a claim about it. `0` is a claim: the script
|
|
16
|
+
* loaded the library and used none of it.
|
|
17
|
+
*/
|
|
18
|
+
readonly checks?: number;
|
|
6
19
|
}
|
|
7
20
|
/**
|
|
8
21
|
* Exit code a harness/eval script uses to report itself SKIPPED (e.g. the
|
|
@@ -11,6 +24,32 @@ export interface ScriptRunResult {
|
|
|
11
24
|
* skip never fails the run. Scripts call `skip()` (vigiles/testing) to emit it.
|
|
12
25
|
*/
|
|
13
26
|
export declare const SKIP_EXIT_CODE = 77;
|
|
27
|
+
/**
|
|
28
|
+
* Classify one script's run from its exit code and its reported check count.
|
|
29
|
+
*
|
|
30
|
+
* 🔴 THE FOURTH STATE, AND WHY. Exit codes answer "did it fail?", never "did it
|
|
31
|
+
* do anything?". Measured 2026-08-08: a file whose whole body is
|
|
32
|
+
* `export default { "never runs": () => assert.equal(1, 2) }` imports fine,
|
|
33
|
+
* exits 0, and printed `✓ … 1 passed` — a false assertion, never called,
|
|
34
|
+
* reported as a pass. A consumer repo hit exactly that and now hand-copies a
|
|
35
|
+
* warning into every new harness header, because the runner could not enforce
|
|
36
|
+
* it: eight harnesses resting on a comment.
|
|
37
|
+
*
|
|
38
|
+
* So a run that ends clean having recorded ZERO checks is `"vacuous"` — its own
|
|
39
|
+
* visible state, not folded into `passed`, the same way a skip is not.
|
|
40
|
+
*
|
|
41
|
+
* NOT A FAILURE, deliberately. Harnesses in the wild predate the counter, and a
|
|
42
|
+
* tool that turned CI red on the release that taught it a new word would be
|
|
43
|
+
* punishing people for upgrading. It is loud and it is not fatal.
|
|
44
|
+
*
|
|
45
|
+
* AND SILENCE IS NOT ZERO. `checks === undefined` means the script never
|
|
46
|
+
* reported — it may not import `vigiles/testing` at all — so it stays a plain
|
|
47
|
+
* `pass`, exactly as before. Only a script that loaded the library and used
|
|
48
|
+
* none of it says zero. This is the `undefined`-vs-`[]` distinction
|
|
49
|
+
* `assertNoWrite` already draws: "nobody looked" must not read as "nothing
|
|
50
|
+
* happened".
|
|
51
|
+
*/
|
|
52
|
+
export declare function statusFor(code: number, checks: number | undefined): ScriptStatus;
|
|
14
53
|
/** Filename extensions accepted for harness/eval scripts (JS and TS). */
|
|
15
54
|
export declare const SCRIPT_EXTS: readonly ["mjs", "cjs", "js", "mts", "cts", "ts"];
|
|
16
55
|
/** Glob suffix matching every accepted script extension, e.g. `harness`. */
|
|
@@ -41,10 +80,20 @@ export declare function discoverScripts(patterns: readonly string[], defaultGlob
|
|
|
41
80
|
/**
|
|
42
81
|
* Run each script as `node <file>`, inheriting stdio so the script's own report
|
|
43
82
|
* streams to the console. `env` is merged over `process.env` for every child
|
|
44
|
-
* (e.g. `VIGILES_TRIALS`). Returns the per-file exit codes.
|
|
83
|
+
* (e.g. `VIGILES_TRIALS`). Returns the per-file exit codes + check counts.
|
|
84
|
+
*
|
|
85
|
+
* Each child is handed its OWN scratch path in `VIGILES_CHECK_COUNT_ENV`, which
|
|
86
|
+
* `vigiles/testing` writes its check count to on exit — the channel that makes
|
|
87
|
+
* "ran nothing" distinguishable from "ran and passed" (see check-count.ts). It
|
|
88
|
+
* has to be a file: stdio is inherited so the script's report streams live,
|
|
89
|
+
* which leaves no stream to parse.
|
|
45
90
|
*/
|
|
46
91
|
export declare function runScripts(files: readonly string[], cwd: string, env?: NodeJS.ProcessEnv): ScriptRunResult[];
|
|
47
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Whether any script FAILED. Neither a skip nor a vacuous run counts: the first
|
|
94
|
+
* declined to run, the second ran and verified nothing, and neither is evidence
|
|
95
|
+
* that anything is broken. Both are visible in the summary instead.
|
|
96
|
+
*/
|
|
48
97
|
export declare function anyFailed(results: readonly ScriptRunResult[]): boolean;
|
|
49
98
|
/**
|
|
50
99
|
* What a `test`/`eval` invocation should do about actually RUNNING the discovered
|
|
@@ -87,7 +136,8 @@ export interface RunScriptsEnv {
|
|
|
87
136
|
* CLI.
|
|
88
137
|
*/
|
|
89
138
|
export declare function decideRunScripts(o: RunScriptsEnv): RunScriptsDecision;
|
|
90
|
-
/** One line per file + an explicit pass/skip/fail tally. Skips
|
|
91
|
-
* folded into "passed" — a `⊘ SKIPPED` is loud,
|
|
139
|
+
/** One line per file + an explicit pass/skip/vacuous/fail tally. Skips and
|
|
140
|
+
* vacuous runs are SHOWN, never folded into "passed" — a `⊘ SKIPPED` is loud,
|
|
141
|
+
* not a silent green, and so is a file that verified nothing. */
|
|
92
142
|
export declare function formatScriptSummary(results: readonly ScriptRunResult[]): string;
|
|
93
143
|
//# sourceMappingURL=run-scripts.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SCRIPT_EXTS = exports.SKIP_EXIT_CODE = void 0;
|
|
4
|
+
exports.statusFor = statusFor;
|
|
4
5
|
exports.scriptGlob = scriptGlob;
|
|
5
6
|
exports.interpreterArgs = interpreterArgs;
|
|
6
7
|
exports.detectNodeCaps = detectNodeCaps;
|
|
@@ -24,7 +25,9 @@ exports.formatScriptSummary = formatScriptSummary;
|
|
|
24
25
|
const node_child_process_1 = require("node:child_process");
|
|
25
26
|
const node_path_1 = require("node:path");
|
|
26
27
|
const node_fs_1 = require("node:fs");
|
|
28
|
+
const node_os_1 = require("node:os");
|
|
27
29
|
const glob_1 = require("glob");
|
|
30
|
+
const check_count_js_1 = require("../../check-count.js");
|
|
28
31
|
/**
|
|
29
32
|
* Exit code a harness/eval script uses to report itself SKIPPED (e.g. the
|
|
30
33
|
* deterministic tier when `claude` isn't installed) — the autotools convention.
|
|
@@ -32,12 +35,37 @@ const glob_1 = require("glob");
|
|
|
32
35
|
* skip never fails the run. Scripts call `skip()` (vigiles/testing) to emit it.
|
|
33
36
|
*/
|
|
34
37
|
exports.SKIP_EXIT_CODE = 77;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Classify one script's run from its exit code and its reported check count.
|
|
40
|
+
*
|
|
41
|
+
* 🔴 THE FOURTH STATE, AND WHY. Exit codes answer "did it fail?", never "did it
|
|
42
|
+
* do anything?". Measured 2026-08-08: a file whose whole body is
|
|
43
|
+
* `export default { "never runs": () => assert.equal(1, 2) }` imports fine,
|
|
44
|
+
* exits 0, and printed `✓ … 1 passed` — a false assertion, never called,
|
|
45
|
+
* reported as a pass. A consumer repo hit exactly that and now hand-copies a
|
|
46
|
+
* warning into every new harness header, because the runner could not enforce
|
|
47
|
+
* it: eight harnesses resting on a comment.
|
|
48
|
+
*
|
|
49
|
+
* So a run that ends clean having recorded ZERO checks is `"vacuous"` — its own
|
|
50
|
+
* visible state, not folded into `passed`, the same way a skip is not.
|
|
51
|
+
*
|
|
52
|
+
* NOT A FAILURE, deliberately. Harnesses in the wild predate the counter, and a
|
|
53
|
+
* tool that turned CI red on the release that taught it a new word would be
|
|
54
|
+
* punishing people for upgrading. It is loud and it is not fatal.
|
|
55
|
+
*
|
|
56
|
+
* AND SILENCE IS NOT ZERO. `checks === undefined` means the script never
|
|
57
|
+
* reported — it may not import `vigiles/testing` at all — so it stays a plain
|
|
58
|
+
* `pass`, exactly as before. Only a script that loaded the library and used
|
|
59
|
+
* none of it says zero. This is the `undefined`-vs-`[]` distinction
|
|
60
|
+
* `assertNoWrite` already draws: "nobody looked" must not read as "nothing
|
|
61
|
+
* happened".
|
|
62
|
+
*/
|
|
63
|
+
function statusFor(code, checks) {
|
|
38
64
|
if (code === exports.SKIP_EXIT_CODE)
|
|
39
65
|
return "skip";
|
|
40
|
-
|
|
66
|
+
if (code !== 0)
|
|
67
|
+
return "fail";
|
|
68
|
+
return checks === 0 ? "vacuous" : "pass";
|
|
41
69
|
}
|
|
42
70
|
/** Filename extensions accepted for harness/eval scripts (JS and TS). */
|
|
43
71
|
exports.SCRIPT_EXTS = ["mjs", "cjs", "js", "mts", "cts", "ts"];
|
|
@@ -104,35 +132,73 @@ function discoverScripts(patterns, defaultGlob, cwd) {
|
|
|
104
132
|
}
|
|
105
133
|
return [...found].sort();
|
|
106
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The count a script left behind, or `undefined` if it left none (it never
|
|
137
|
+
* imported `vigiles/testing`, or died before its exit handler). Anything that
|
|
138
|
+
* isn't a non-negative integer is treated as no report — a corrupt scratch file
|
|
139
|
+
* must not invent a verdict.
|
|
140
|
+
*/
|
|
141
|
+
function readCheckCount(path) {
|
|
142
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
143
|
+
return undefined;
|
|
144
|
+
let raw;
|
|
145
|
+
try {
|
|
146
|
+
raw = (0, node_fs_1.readFileSync)(path, "utf8").trim();
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
if (!/^\d+$/.test(raw))
|
|
152
|
+
return undefined;
|
|
153
|
+
return Number(raw);
|
|
154
|
+
}
|
|
107
155
|
/**
|
|
108
156
|
* Run each script as `node <file>`, inheriting stdio so the script's own report
|
|
109
157
|
* streams to the console. `env` is merged over `process.env` for every child
|
|
110
|
-
* (e.g. `VIGILES_TRIALS`). Returns the per-file exit codes.
|
|
158
|
+
* (e.g. `VIGILES_TRIALS`). Returns the per-file exit codes + check counts.
|
|
159
|
+
*
|
|
160
|
+
* Each child is handed its OWN scratch path in `VIGILES_CHECK_COUNT_ENV`, which
|
|
161
|
+
* `vigiles/testing` writes its check count to on exit — the channel that makes
|
|
162
|
+
* "ran nothing" distinguishable from "ran and passed" (see check-count.ts). It
|
|
163
|
+
* has to be a file: stdio is inherited so the script's report streams live,
|
|
164
|
+
* which leaves no stream to parse.
|
|
111
165
|
*/
|
|
112
166
|
function runScripts(files, cwd, env = {}) {
|
|
113
167
|
const caps = detectNodeCaps(cwd);
|
|
114
168
|
const results = [];
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
argv
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
169
|
+
const countDir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "vigiles-checks-"));
|
|
170
|
+
try {
|
|
171
|
+
files.forEach((file, i) => {
|
|
172
|
+
let argv;
|
|
173
|
+
try {
|
|
174
|
+
argv = interpreterArgs(file, caps);
|
|
175
|
+
}
|
|
176
|
+
catch (e) {
|
|
177
|
+
console.error(`✗ ${file}: ${e.message}`);
|
|
178
|
+
results.push({ file, code: 1, status: "fail" });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const countFile = (0, node_path_1.join)(countDir, `${String(i)}.count`);
|
|
182
|
+
const res = (0, node_child_process_1.spawnSync)("node", argv, {
|
|
183
|
+
cwd,
|
|
184
|
+
stdio: "inherit",
|
|
185
|
+
env: { ...process.env, ...env, [check_count_js_1.CHECK_COUNT_ENV]: countFile },
|
|
186
|
+
});
|
|
187
|
+
const code = res.status ?? 1;
|
|
188
|
+
const checks = readCheckCount(countFile);
|
|
189
|
+
results.push({ file, code, status: statusFor(code, checks), checks });
|
|
129
190
|
});
|
|
130
|
-
|
|
131
|
-
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
(0, node_fs_1.rmSync)(countDir, { recursive: true, force: true });
|
|
132
194
|
}
|
|
133
195
|
return results;
|
|
134
196
|
}
|
|
135
|
-
/**
|
|
197
|
+
/**
|
|
198
|
+
* Whether any script FAILED. Neither a skip nor a vacuous run counts: the first
|
|
199
|
+
* declined to run, the second ran and verified nothing, and neither is evidence
|
|
200
|
+
* that anything is broken. Both are visible in the summary instead.
|
|
201
|
+
*/
|
|
136
202
|
function anyFailed(results) {
|
|
137
203
|
return results.some((r) => r.status === "fail");
|
|
138
204
|
}
|
|
@@ -165,24 +231,39 @@ const MARK = {
|
|
|
165
231
|
pass: "✓",
|
|
166
232
|
skip: "⊘",
|
|
167
233
|
fail: "✗",
|
|
234
|
+
vacuous: "∅",
|
|
168
235
|
};
|
|
169
|
-
/** One line per file + an explicit pass/skip/fail tally. Skips
|
|
170
|
-
* folded into "passed" — a `⊘ SKIPPED` is loud,
|
|
236
|
+
/** One line per file + an explicit pass/skip/vacuous/fail tally. Skips and
|
|
237
|
+
* vacuous runs are SHOWN, never folded into "passed" — a `⊘ SKIPPED` is loud,
|
|
238
|
+
* not a silent green, and so is a file that verified nothing. */
|
|
171
239
|
function formatScriptSummary(results) {
|
|
172
240
|
const lines = results.map((r) => {
|
|
173
241
|
if (r.status === "skip")
|
|
174
242
|
return ` ⊘ ${r.file} — SKIPPED`;
|
|
175
243
|
if (r.status === "fail")
|
|
176
244
|
return ` ✗ ${r.file} (exit ${String(r.code)})`;
|
|
245
|
+
if (r.status === "vacuous") {
|
|
246
|
+
return ` ${MARK.vacuous} ${r.file} — 0 CHECKS (it ran clean and verified nothing)`;
|
|
247
|
+
}
|
|
177
248
|
return ` ${MARK.pass} ${r.file}`;
|
|
178
249
|
});
|
|
179
250
|
const n = (s) => results.filter((r) => r.status === s).length;
|
|
180
251
|
const parts = [`${String(n("pass"))} passed`];
|
|
181
252
|
if (n("skip") > 0)
|
|
182
253
|
parts.push(`${String(n("skip"))} skipped`);
|
|
254
|
+
if (n("vacuous") > 0)
|
|
255
|
+
parts.push(`${String(n("vacuous"))} with 0 checks`);
|
|
183
256
|
if (n("fail") > 0)
|
|
184
257
|
parts.push(`${String(n("fail"))} failed`);
|
|
185
258
|
lines.push(`\n${parts.join(", ")}.`);
|
|
259
|
+
// Name the remedy where it's read, once — the usual cause is a file that
|
|
260
|
+
// DEFINES tests and never calls them, and the usual second cause is a harness
|
|
261
|
+
// asserting some other way, which the runner cannot see.
|
|
262
|
+
if (n("vacuous") > 0) {
|
|
263
|
+
lines.push(` ∅ = the file loaded vigiles/testing and used none of it. Either nothing ran ` +
|
|
264
|
+
`(an exported test object nobody calls), or it asserts another way — in which ` +
|
|
265
|
+
`case call recordCheck() from vigiles/testing so those count.`);
|
|
266
|
+
}
|
|
186
267
|
return lines.join("\n");
|
|
187
268
|
}
|
|
188
269
|
//# sourceMappingURL=run-scripts.js.map
|
package/dist/audit-report.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* timestamp is attached by the CLI at write time, never by this pure builder, so
|
|
12
12
|
* the embedded-in-HTML form stays deterministic).
|
|
13
13
|
*/
|
|
14
|
-
import { type AuditScore } from "./audit-score.js";
|
|
14
|
+
import { type AuditScore, type AuditScoreOptions } from "./audit-score.js";
|
|
15
15
|
import { type Recommendation } from "./optimize.js";
|
|
16
16
|
import { type Verdict } from "./audit-verdict.js";
|
|
17
17
|
import type { LedgerSummary } from "./observe.js";
|
|
@@ -20,6 +20,7 @@ import type { ScanReport, MarketplaceInfo } from "./scan.js";
|
|
|
20
20
|
import type { PluginScore } from "./score-core.js";
|
|
21
21
|
import type { RuleInventoryItem } from "./rule-inventory.js";
|
|
22
22
|
import type { RuleRouting } from "./rule-routing.js";
|
|
23
|
+
import type { EvidenceCounts } from "./coverage-evidence.js";
|
|
23
24
|
/**
|
|
24
25
|
* The current schema version. Bump only on a BREAKING change to the shape.
|
|
25
26
|
* v2 (2026-07-15): the rule-map `mechanism` enum value for an unrouted rule
|
|
@@ -55,7 +56,26 @@ export interface AuditInventory {
|
|
|
55
56
|
readonly hooks: number;
|
|
56
57
|
readonly commands: number;
|
|
57
58
|
readonly mcp: boolean;
|
|
59
|
+
/** Surfaces covered by NEITHER tier — the union count (unchanged). */
|
|
58
60
|
readonly untested: number;
|
|
61
|
+
/**
|
|
62
|
+
* The two tiers, carried SEPARATELY so a consumer can tell "has deterministic
|
|
63
|
+
* coverage, no evals" from "has neither" — a distinction the single `untested`
|
|
64
|
+
* count erased. `untestedHarness` is free-and-every-push work; `unevaluated` is
|
|
65
|
+
* paid real-model work. Additive/optional — schema version unchanged.
|
|
66
|
+
*/
|
|
67
|
+
readonly untestedHarness?: number;
|
|
68
|
+
readonly unevaluated?: number;
|
|
69
|
+
/**
|
|
70
|
+
* HOW the covered surfaces were decided to be covered — `declared` (an explicit
|
|
71
|
+
* `vigiles:covers` marker), `colocated` (a test placed at the surface), or
|
|
72
|
+
* `mention` (the surface's path/namespace appears in a test's code). Carried in
|
|
73
|
+
* the product boundary because a coverage count without its derivation is not
|
|
74
|
+
* auditable: a repo whose coverage is entirely `mention` looks, in a bare
|
|
75
|
+
* number, exactly like one with real tests. Additive/optional — schema version
|
|
76
|
+
* unchanged.
|
|
77
|
+
*/
|
|
78
|
+
readonly coverageEvidence?: EvidenceCounts;
|
|
59
79
|
}
|
|
60
80
|
/**
|
|
61
81
|
* A surface (skill / subagent / instruction file) that EXISTS but doesn't yet
|
|
@@ -86,7 +106,7 @@ export interface Adoptable {
|
|
|
86
106
|
*/
|
|
87
107
|
export interface AuditReport {
|
|
88
108
|
readonly meta: AuditReportMeta;
|
|
89
|
-
/** The
|
|
109
|
+
/** The six category rings + the weighted overall + grade. */
|
|
90
110
|
readonly score: AuditScore;
|
|
91
111
|
/**
|
|
92
112
|
* The one-line verdict + per-recommendation `pointsIfFixed`, both derived by
|
|
@@ -147,7 +167,7 @@ export interface AuditReport {
|
|
|
147
167
|
*/
|
|
148
168
|
readonly ruleRouting?: RuleRouting;
|
|
149
169
|
}
|
|
150
|
-
export interface BuildAuditReportOptions {
|
|
170
|
+
export interface BuildAuditReportOptions extends AuditScoreOptions {
|
|
151
171
|
readonly harness: string;
|
|
152
172
|
readonly vigilesVersion: string;
|
|
153
173
|
/** The flight-recorder summary from the local ledger (omit when empty). */
|
package/dist/audit-report.js
CHANGED
|
@@ -53,7 +53,7 @@ function buildAdoptable(surfaces) {
|
|
|
53
53
|
*/
|
|
54
54
|
function buildAuditReport(report, opts) {
|
|
55
55
|
const adoptable = buildAdoptable(opts.adoptableSurfaces);
|
|
56
|
-
const score = (0, audit_score_js_1.auditScore)(report);
|
|
56
|
+
const score = (0, audit_score_js_1.auditScore)(report, { firingMeasured: opts.firingMeasured });
|
|
57
57
|
const recommendations = (0, optimize_js_1.optimize)(report).recommendations;
|
|
58
58
|
const verdict = (0, audit_verdict_js_1.computeVerdict)({ report, score, recommendations });
|
|
59
59
|
return {
|
|
@@ -78,6 +78,15 @@ function buildAuditReport(report, opts) {
|
|
|
78
78
|
commands: report.commands,
|
|
79
79
|
mcp: report.mcp,
|
|
80
80
|
untested: report.untested,
|
|
81
|
+
...(report.untestedHarness !== undefined
|
|
82
|
+
? { untestedHarness: report.untestedHarness }
|
|
83
|
+
: {}),
|
|
84
|
+
...(report.unevaluated !== undefined
|
|
85
|
+
? { unevaluated: report.unevaluated }
|
|
86
|
+
: {}),
|
|
87
|
+
...(report.coverageEvidence
|
|
88
|
+
? { coverageEvidence: report.coverageEvidence }
|
|
89
|
+
: {}),
|
|
81
90
|
},
|
|
82
91
|
...(report.danglingRefs.length
|
|
83
92
|
? { brokenReferences: report.danglingRefs }
|