Files
zernie__vigiles/docs/markdown-mode.md
T
zernie 18d7cfcae4 docs: honest versioning, README scannability, and docs restructure (#67)
Fix the version claim (v12, not 0.x; the 0.0.0-semantically-released placeholder was the source), reframe the eval section around cost measurement, trim the README for scannability (Diátaxis link buckets, tighter reconciliation + proofs), reorganize the docs index into Guides/Reference/Explanation and add six missing docs, and consolidate overlapping docs (related-tools→comparison, inline-mode→markdown-mode, agent-setup+agent-workflows). Relocate the eval-architecture design-of-record ADR from docs/ to research/ per the doc-tiers rule, and fix the library entry points (vigiles/linting is the compiler surface) plus the Codex setup path.
2026-07-09 02:31:19 +05:00

11 KiB

Markdown mode

The README has the pitch; this is the full guide to verifying rules in plain markdown — no TypeScript, no build step.

vigiles meets you in the markdown you already have. You don't need a TypeScript build step, and you don't need to port your CLAUDE.md into a .spec.ts to get verified rules. Declare enforce rules in the instruction file itself and vigiles lint verifies them against your real linter config — existence check, closest-match typo suggestions, disabled-rule detection, and GitHub Actions annotations, exactly like a typed spec.

Two on-ramps, one tool

There are two ways to author rules, and each adds value without requiring the other:

On-ramp Where rules live Build step When
Plain markdown <!-- vigiles:enforce ... --> in the prose none Try a rule on any CLAUDE.md, zero ceremony
Typed spec CLAUDE.md.spec.ts → compiled markdown vigiles compile You want compiler-grade guarantees + enforcement

Plain markdown is the on-ramp; the typed spec is the source of truth when you want full type checking, programmatic rule composition, and generate types. Your agent writes the spec, and vigiles eject hands it back to plain markdown anytime — so graduating to a spec is never a one-way door.


Inline enforce comments

The minimum-commitment path: add a single HTML comment per rule, anywhere in your existing markdown. It's the vigiles equivalent of // eslint-disable-next-line — maximum incrementalism, zero new files.

<!-- vigiles:enforce eslint/no-console "Route output through logger.ts" -->

Three pieces, all required:

  1. vigiles:enforce — only enforce is supported inline. The prose around the comment is the guidance, so a guidance comment would be a tautology.
  2. <linter>/<rule> — the same reference format as enforce() in spec mode. All seven catalogs (ESLint, Stylelint, Ruff, Clippy, Pylint, RuboCop, Cedar), scoped plugin names (eslint/@typescript-eslint/...), and the vigiles-internal namespace (vigiles/orphan-docs) work here.
  3. "<why>" — a double-quoted string shown to the agent as context. No newlines or embedded quotes; if you need either, move to a spec.

A fuller example:

# My Project

<!-- vigiles:enforce eslint/no-console "Route output through logger.ts" -->
<!-- vigiles:enforce eslint/@typescript-eslint/no-floating-promises "Await or explicitly void" -->
<!-- vigiles:enforce ruff/F401 "No unused imports" -->

## Logging

All application output must go through the shared logger module.

What vigiles lint catches

  • Verifies each rule reference against your real linter config.
  • Emits closest-match suggestions on typos: "no-consol"did you mean "eslint/no-console"?
  • Flags rules that exist but are disabled in your linter config.
  • Emits ::error annotations under GitHub Actions; exits code 2 on any failed rule, so CI fails fast.

What it does not do (vs a typed spec)

  • No edit-time type safety. A .spec.ts gets editor squiggles because rules are a type union; inline strings surface typos only at vigiles lint time (still before CI).
  • No programmatic composition — each comment stands alone.
  • No NCD duplicate detection — that runs on spec-mode files.

That's all fine for the adoption on-ramp. When you outgrow it, graduate to a spec.

Mixing with a spec — don't

A file is checked for inline rules only when it isn't managed by a spec (no sibling <file>.spec.ts, no vigiles:sha256 … compiled from … header). If both exist, the compiler overwrites the markdown on the next compile and your inline comments vanish. Pick one per file.

Graduating to a spec

When a dozen inline rules start crowding the prose:

npx vigiles init --target=CLAUDE.md

That scaffolds a CLAUDE.md.spec.ts beside your CLAUDE.md. Copy the enforce rules into the rules: block, delete the inline comments, and run vigiles compile — the markdown is rebuilt with a sha256 header, and future edits flow through the spec. vigiles eject reverses it anytime.


require-instructions-spec

The built-in require-instructions-spec rule asks for a .spec.ts sibling for every CLAUDE.md / AGENTS.md. It is narrow: only a .spec.ts satisfies it. Inline <!-- vigiles:enforce --> comments and vigiles: frontmatter are still valid plain-markdown on-ramps, but they do not satisfy require-instructions-spec — so a markdown-mode user simply keeps the rule off (it is off by default; turning it on is a workflow-tier opt-in for teams that want to require a typed spec). No vigiles-disable require-instructions-spec comment needed unless you have enabled the rule.

A file is checked for inline rules only when it is not managed by a spec — i.e. it has no sibling <file>.spec.ts and no vigiles:sha256 … compiled from … header. The typed spec is the source of truth; it overwrites the markdown on the next compile, so don't mix a spec with hand-authored markers in the same file.

See also