Closes #181, closes #182, closes #183, closes #184, closes #185, and the residual half of #173. #185 — every per-surface check read ONE root, so a monorepo with `skills/` plus `plugins/*/skills/` scored only the first: 4 skills over budget, 2 reported, exit 0. The default now NAMES the bundles it did not score; `--bundles=all` scores them in one pass with one exit code. Root-only stays the default so a vendored third-party corpus is not scored as if it were yours. #181 — `orphan-docs` had a documented severity nothing read, and the duplicate-rules check had no id at all, so neither could be tiered. Both are real rules now and the exit code respects their severity. #183 — the log and `--json` disagreed (21 lines against 88 findings) because some checks print per finding and others per group. One computed total, printed and carried in `--json`. #182 — `--json-out=<file>` writes the JSON while stdout stays human-readable, so a CI job stops scanning twice. #184 — `vigiles test` resolves a harness's bare `vigiles` import from the CLI's own install, so `npx vigiles@<version> test` needs nothing in the project. NODE_PATH was measured, not assumed: Node ignores it for ESM. #173 residual — `lint` verified the integrity HASH, which says nothing about whether the things a file NAMES still exist, so an artifact committed while its refs were live stayed green after the target was deleted. The new `spec-refs` rule re-derives them. It immediately found four dead references in this repo's own `examples/CLAUDE.md.spec.ts`, left behind by the hexagonal refactor. BREAKING CHANGE: `orphan-docs` and `duplicate-rules` now default to "warn" — a repo relying on either to fail CI must set "error" explicitly. Registering them surfaced that both are heuristic proxies, and this repo's calibration rule is that a heuristic never defaults to error. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2.1 KiB
spec-refs
A compiled instruction file whose .spec.ts references a file, script or symbol
that no longer exists.
{ "rules": { "spec-refs": "error" } }
| Default | error |
| Surface | instruction files compiled from a spec |
| Bucket | external-decidable |
| Detector | compileClaude |
What it checks
For every <file>.spec.ts whose compiled <file> exists, the spec's references
are re-derived and validated exactly as compile validates them: file() paths
against the filesystem, cmd() against package.json scripts, enforce()
against the real linter catalogs.
Why the integrity hash is not enough
They answer different questions, and reading them as one is what hid this:
| check | answers |
|---|---|
integrity |
is this file still what the spec compiled to? |
spec-refs |
do the things it NAMES still exist? |
An artifact committed while its references were live stays hash-valid forever.
Delete the referenced file afterwards and compile errors while lint reports
✓ hash valid — All compiled files intact and exits 0 — over a file that names a
path the agent will not find.
Severity
"error"(default) — matchescompile. A dead reference is decidable from the filesystem, not a proxy, so it sits in the hard tier."warn"— reported, exit code untouched.false/"off"— skipped.
Cost
Only specs whose compiled output already exists are loaded, so a repo with no
specs does no extra work. A spec that fails to LOAD is not reported here — that
is compile's finding, and reporting it twice would blame the wrong command.
See also
integrity— the hash half.- The validation-rule matrix in verifying instruction files.