* fix: finish audit→lint rename — repair CI build, shipped hook, and docs
The `audit` command was renamed to `lint` in 2c64c62, but the rename never
reached CI, the SessionStart hook, the CLI help, or the docs — so the `check`
job ran `vigiles audit` ("Unknown command", exit 1) and the build badge went red.
- ci.yml: run `vigiles lint` (+ the action dogfood `command: lint`)
- hooks/session-start.sh: `vigiles lint --summary`
- examples/demo/run.sh: `vigiles lint` (npm run demo was broken too)
- src/cli.ts + action.yml: help text / input description say `lint`
- docs/harnesses.md: de-stale the "second harness lands" framing (Codex
already shipped), trim two over-explained sections, align Pillar 1/2 → Lint/Test
- docs/harness-testing.md: trim the demo callout, audit→lint
- README.md: paste-prompt as a copyable code block; fold init details into <details>
* docs: research core-plus-adapters API design (grounds the pillar-2 relayout)
Five-angle survey (AI SDK, unplugin, Drizzle, Knex, Testing Library, ESLint,
OpenTelemetry, dependency-cruiser, semver/exports) of how comparable tools lay out
a harness-agnostic core + per-target adapters, written to ground the pillar-2 API
relayout. Verdict: keep one package with subpath exports; move the agnostic runners
out of src/adapters/claude-code to the composition root (non-breaking — exports
hide file paths); select by import in the library, string only in the CLI (never
resolve a string to a module on a library path); add an eslint boundary rule so an
agnostic barrel can't import a specific adapter; parameterize the conformance kit
over ADAPTERS; mind the default-param tree-shaking trap.
Linked from research/code-adapter-architecture.md + research/README.md.
* refactor: route agnostic pillar-2 barrels through composition-root modules
Step 1 of the pillar-2 relayout (research/adapter-api-design.md). The harness-
agnostic public entries (vigiles/testing, /unit, /integration, /e2e) no longer
`export *` from src/adapters/claude-code/* — they route through composition-root
modules (src/{harness-test,run-hook,eval,mock-model}.ts), and a new
eslint-plugin-boundaries `agnostic-surface` element forbids them from importing
any adapter (proven to flag the old pattern). exports map repointed accordingly.
Next step relocates the runner *logic* to these composition-root modules so the
100% coverage gate (which already lists them at src/ root) actually covers it.
* refactor: relocate harness-testing library to the composition root
Step 2 of the pillar-2 relayout (research/adapter-api-design.md). Physically move
the deterministic harness-testing modules out of src/adapters/claude-code/ to the
composition root src/: harness-test, run-hook, eval, eval-cache, eval-baseline,
stats, mock-model, mock-entry, sandbox, egress(+entry/proxy), judge (+ tests). The
adapter keeps the ports + bundle + harness glue (dialect, layout, runtime,
hook-protocol, model-mock, adapter, agent-*, skill-*, plugin-loader, run-scripts).
This makes the 100% coverage gate — whose include list already named these at
src/ root — actually cover them: gated statements go 268 → 902 (the gate was
near-empty because egress/sandbox/judge were listed at root but lived in the
adapter, so the v8 include glob matched nothing). Coverage gaps the now-live gate
exposed in egress.ts were closed with real tests, not v8-ignores.
Pure relocation + import-path fixes + coverage-include (added stats, eval-cache);
no logic or public-export changes. Build, lint (boundaries clean), 863 tests, and
the 100% gate all green.
* docs: split harness-testing into agnostic core + per-harness guides; sync spec
Step 3+4 of the pillar-2 relayout.
Docs: docs/harness-testing.md is rewritten as the harness-AGNOSTIC core (retitled
"Testing your harness"): leads with select-by-import + { adapter } (default Claude
Code), the Trace/predicates/assertions model, runHook/runHarnessTest/runEval shown
agnostically, significance + regression gating, and a promoted runner-integration
section (vitest/jest is the runner-agnostic seam, and it's tested in CI). The
Claude-Code-specific walkthrough (oh-my-claudecode, ${CLAUDE_PLUGIN_ROOT}, the
Anthropic mock, sandbox/egress) moves to docs/harness-testing-claude-code.md, and
docs/harness-testing-codex.md covers the Codex path (runHarnessTest with
codexAdapter against real codex exec; subagents a deliberate non-goal, runEval a
documented follow-on). Both linked from the index + harnesses.md (orphan-docs ✓).
Spec: CLAUDE.md.spec.ts Key Files repointed to the relocated module paths
(src/adapters/claude-code/{harness-test,run-hook,eval,...} → src/...), the
architecture section updated to describe the composition-root harness-testing
library + the new agnostic-surface boundary rule, and entries added for the two
new docs + research/adapter-api-design.md. CLAUDE.md recompiled (hash valid).
* docs: fix stale `vigiles audit` references → `vigiles lint`
The audit→lint command rename left 16 user-facing docs/examples telling users to
run a command that no longer exists. Sweep them: docs/comparison, inline-mode,
markdown-mode, spec-format, skills, verifying-instruction-files, testing-matrix,
README, docs/rules/* and examples/{demo,frontmatter-CLAUDE}. `vigiles lint`
(integrity + orphan-docs + refs) stays green; no command now points at the
removed `audit`.
* fix: repoint test/example imports to the relocated runner dist paths
The step-2 relocation rewired src/ imports but missed consumers outside src/:
test/types/smoke.{vitest,jest}.ts and examples/harness/*.mjs (+ dogfood, bench,
plugin-test-demo) still imported dist/adapters/claude-code/{harness-test,eval,
run-hook,sandbox}.js, which moved to dist/*.js. The CI test:types step (TS2307)
and the harness job (ERR_MODULE_NOT_FOUND) caught it — local `npm test` doesn't
run those tiers. Repointed every dist/adapters/claude-code/<moved>.js → dist/<moved>.js.
* fix: repoint vigiles/judge export + regenerate path-union types
Two more relocation leftovers: the package.json "./judge" export still pointed at
./dist/adapters/claude-code/judge.js (judge.ts moved to src/, so the public
vigiles/judge subpath was broken), and .vigiles/generated.d.ts still listed the
old src/adapters/claude-code/<moved> paths. Repointed ./judge → ./dist/judge.js
and regenerated the type union (generate-types --check now passes).
---------
Co-authored-by: Claude <noreply@anthropic.com>
2.2 KiB
integrity
What is "integrity"?
Every compiled instruction file (CLAUDE.md, AGENTS.md) starts with a SHA-256 hash that vigiles wrote when it generated the file:
<!-- vigiles:sha256:c92312d638af04cf compiled from CLAUDE.md.spec.ts -->
The integrity rule recomputes the hash from the current file contents and compares it to the stored value. If they don't match, someone (or something) edited the compiled file directly instead of editing the .spec.ts source. The rule flags this so the change can be undone and re-expressed in the spec.
This is a tamper-detection check, nothing more. It does NOT recompile and diff (that's what vigiles compile does), it does NOT track linter config changes (that's what enforce() rules catch at compile time), and it does NOT need configuration beyond severity.
What you'll see
Integrity check:
✓ CLAUDE.md
✗ AGENTS.md — Compiled file was modified directly — edit the .spec.ts source and recompile
Configuration
{
"rules": {
"integrity": "warn"
}
}
Severity
| Value | Behavior |
|---|---|
"error" |
vigiles lint exits non-zero (CI fails) |
"warn" (default) |
Prints warning, exits 0 |
false |
Skip integrity checks entirely |
What about stale specs?
Integrity catches hand-edits, not staleness. For "did the spec change but compile wasn't re-run?", the answer is:
- Use
guard()— declare a guard rule that runsvigiles compilewhenever any.spec.tschanges. The compile step runs at edit time (Claude Code hook) or commit time (husky), so committed markdown is always fresh. - Use CI — run
vigiles compilethengit diff --exit-code. If the diff isn't empty, someone forgot to recompile. - Use
enforce()/file()/cmd()— these already catch disabled rules, missing files, and removed scripts at compile time.
The old freshness rule with strict and input-hash modes was reframed because every problem it caught is better handled by one of the above. Keep the rule narrow: it does one thing, and it does it for free.