mirror of
https://github.com/zernie/vigiles.git
synced 2026-09-14 20:53:57 +08:00
94af23a5b7
Closes #173, #174, #175, #176 and #170. #174 — decideHook read exit 2, `decision` and `permissionDecision` but not the harness's halt-the-turn field, so `assertBlocksDisasters` reported a guard that stopped every disaster as blocking none. The field is read from the PORT, not hard-coded: `continue` is documented for Claude Code and unverified for Codex. The issue's adjacent suggestion was deliberately NOT implemented — adding the halt to `hook-block-ineffective` as a block-attempt signal would fire on hooks that WORK, since a halt is not event-scoped. It reads as a suppressor instead, fixing a real false alarm. A mutation implementing the original suggestion literally is pinned by a test that fails on it. #173 — compile printed its errors, exited 1, and wrote a hash-valid CLAUDE.md anyway, so `lint` went green over refs it had just called dead. Nothing is written on a failed compile now. #176 — eight small defects, including `audit --out` outside the repo appending unreachable `../../..` entries to .gitignore (a documented read-only report editing a tracked file), an unknown command exiting 1 against a documented 2, and STABILITY.md promising the removed `scan` verb. #175 + #176.2 — three missing capabilities, each opt-in and additive: `{surface}` in testGlobs, `--single`, and `rulesDir` so `.claude/rules/**` stops being invisible. #170 — one enumeration of export forms written against the grammar, both naming checks driven off it, and `check-internal-tag.mjs` asking the TypeScript compiler instead of a regex. It immediately found the issue's open case, 13 tagged types the regex had never seen, and a live gap in the ESLint rule. BREAKING CHANGE: `HookRunResult` gains a required `haltsTurn`, so code that CONSTRUCTS one (a test fake) must set it. Reading a result is unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
58 lines
2.5 KiB
TypeScript
58 lines
2.5 KiB
TypeScript
/**
|
|
* STABILITY.md's verb list must be the REAL verb list.
|
|
*
|
|
* Reported by an adopter (#176.4): the page promising a stable CLI contract
|
|
* listed `scan`, a verb removed when it became `audit` — so the strongest
|
|
* promise in the repo named a command that answers `Unknown command`. The
|
|
* heaviest kind of doc drift, and the kind that survives longest, because the
|
|
* page is read by people deciding whether to depend on the tool at all.
|
|
*
|
|
* `self-command-refs` could not catch it and still cannot: it is deliberately
|
|
* high-precision and only inspects a `vigiles <cmd>` reference in a COMMAND
|
|
* context, while STABILITY.md names its verbs as a prose list of backticked
|
|
* words. That measured limit is documented; this test closes the one page where
|
|
* the cost of the gap is highest, without widening the detector into the
|
|
* false-positive territory that got `doc-refs` disabled.
|
|
*/
|
|
import { test } from "vitest";
|
|
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
|
|
import { VERBS } from "./cli-commands.js";
|
|
|
|
const STABILITY = resolve(__dirname, "..", "STABILITY.md");
|
|
|
|
/** The backticked words in the "the verbs (…)" clause. */
|
|
function listedVerbs(md: string): string[] {
|
|
const m = /the verbs \(([^)]*)\)/s.exec(md);
|
|
assert.ok(m, "STABILITY.md must carry a `the verbs (…)` clause");
|
|
return [...m[1].matchAll(/`([a-z-]+)`/g)].map((x) => x[1] as string);
|
|
}
|
|
|
|
test("every verb STABILITY.md promises actually exists", () => {
|
|
const listed = listedVerbs(readFileSync(STABILITY, "utf-8"));
|
|
const real = new Set<string>(VERBS);
|
|
const ghosts = listed.filter((v) => !real.has(v));
|
|
assert.deepEqual(
|
|
ghosts,
|
|
[],
|
|
`STABILITY.md promises verb(s) the CLI does not have: ${ghosts.join(", ")}`,
|
|
);
|
|
});
|
|
|
|
test("every human-facing verb is promised by STABILITY.md", () => {
|
|
// The other direction. A shipped verb missing from the stability page is the
|
|
// quieter half of the same drift — the contract silently under-promises, and
|
|
// nobody notices because nothing is wrong on screen.
|
|
const listed = new Set(listedVerbs(readFileSync(STABILITY, "utf-8")));
|
|
// `hook-runtime` is the hidden runtime umbrella — emitted into hook configs,
|
|
// never typed by a human — so it is correctly absent from a human contract.
|
|
const missing = VERBS.filter((v) => v !== "hook-runtime" && !listed.has(v));
|
|
assert.deepEqual(
|
|
missing,
|
|
[],
|
|
`these verbs ship but STABILITY.md does not mention them: ${missing.join(", ")}`,
|
|
);
|
|
});
|