The core scripts

A rule names a script with use: and carries its config inline. Signposts ships a handful of core scripts that cover the ground; pick the one that matches what you’re stopping.

Everything is a script — no special cases, nothing auto-magic. use: is always a path: a core one (core/<name>) or your own (<namespace>/<name>).

Pick by what you’re stopping

You want to stop… use:
A banned code shape in TS/TSX (e.g. ?? new Date()) core/ast-grep
A file that’s missing a required companion (e.g. its test) core/sibling-exists
An exported symbol never referenced in its sibling test core/symbols-in-sibling
A structured file breaking a shape rule (e.g. package.json) core/json-invariant
A banned word or pattern in text/prose core/text-ban
A dangerous shell command core/command-guard
Any hand-edit of a protected path (generated, vendored) core/protected-path
A whole-project tool failing (import graph, coverage, types) core/tool-gate

Two families

Native (in-process)

All but the last. Signposts owns the logic; the config is in your signposts.yaml. They run in-process, so they fire pre-emptively on edit and at commit. (This is why ast-grep is a core script, not a tool-gate — it can block before the write lands.)

Tool-delegated

core/tool-gate only. The tool owns its config file (e.g. dependency-cruiser, tsc); Signposts just runs it and blocks on failure. Commit/push only — you can’t run a whole-project build on every keystroke.

On when:. Native scripts default to [edit, commit] — leave it off. A tool-gate must set when: [commit] (it’s too heavy to run pre-emptively). That’s the only place when: earns its keep. Rules are grouped by namespace in your config.