mirror of
https://github.com/zernie/vigiles.git
synced 2026-09-14 20:53:57 +08:00
7693c71b96
Two constructions, and one live bug that a red-team of #186/#187 turned up. **THE BUG: #173 was fixed in one compiler out of five.** `compileClaudeToFile` stopped writing on error. The identical three lines — write, THEN check errors — sat unchanged in the skill, subagent, railway and generator compilers, and all four stamped unconditionally. Worse than the original: `spec-refs` skips non-`claude` specs, so there was no lint-side backstop either. Reproduced on the built CLI: $ vigiles compile skills/demo/SKILL.md.spec.ts ✗ [stale-file] File not found: "docs/never-existed.md" skills/demo/SKILL.md — 256 bytes, stamped $ vigiles lint . ✓ skills/demo/SKILL.md — hash valid # exit 0 So the comment closing #173 was right about `CLAUDE.md` and wrong about scope. Guarding four more write sites would leave the class writable — the sixth compiler gets written the same way. `addHash` now mints a branded `StampedMarkdown`, `seal()` hands it out only when `errors` is empty, and `writeArtifact` accepts nothing else. `markdown` stays a plain string because `adoptDiff` legitimately wants the draft to diff against even when a ref is stale. An erroring compile has no stamp to give the writer. **Two constructions from the earlier analysis, kept:** `RepoRelativePath` — an out-of-repo `.gitignore` entry (#176.8) cannot be constructed, so the write-site guard is deleted rather than duplicated. `Record<BlockMechanism, …>` — the block mechanisms are a closed table. `decideHook` was three `||` terms while `HookOutput` declared a fourth nothing read (#174); a `||` chain has no shape that can be incomplete. `blockedBy` reports what fired and `haltsTurn` derives from it, so the next mechanism needs no new boolean. Also removed a stale `continue` in `rule-meta.test.ts` — `orphan-docs` was exempted as "built-in, not in RulesConfig", true when written and false since it moved into `RulesConfig`, and it excused the rule from the severity comparison. **A third construction was written, measured, and REMOVED.** A textual check for "every declared rule's severity is read somewhere" — the #181 class. Five iterations, each fixing what the last missed, and the third is why it is gone: with every real read of `orphan-docs` deleted, the suite stayed GREEN, kept alive by an unrelated `new Set(["orphan-docs"])` in `linters.ts`. It was green-and-dead for the rule that motivated it. The fourth iteration's own comment-stripper then ate real code, because `cli.ts` holds glob strings whose slash-star opens a comment that closes somewhere else. The reasoning is recorded in the file, along with the structural fix that belongs in its own PR: the ~30 `check*` functions already share one signature and are a registry nobody wrote down. `Record<RuleName, RuleRunner>` makes a rule without a runner a tsc error and collapses `lintExitCode`'s hand-maintained 31-line `||` list — which no textual check can see anyway. BREAKING CHANGE: `addHash` returns `StampedMarkdown` rather than `string`, and the four compile results gain a required `artifact`. Reading a result is unaffected; code that constructs one must supply it. Four mutations, each landing and failing on its own assertion: write-before-check restored in the skill compiler; `seal` handing out a stamp on errors; `repoRelative` no longer refusing escapes; a mechanism dropped from the table. ⚠️ The first of those came back green on its first run — it landed on the first of four identical blocks rather than the skill compiler. Third time this session that "prove the patch landed" earned its keep. Claude-Session: https://claude.ai/code/session_01PrS34j2M8XR12P2QHaBer4 Co-authored-by: Claude <noreply@anthropic.com>