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
An import crossing an architecture boundary (layers & fences) core/depcruise
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.yml. 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 at:. Native scripts default to at: [write, commit] — leave it off. A whole-graph check (tool-gate, depcruise) sets at: [commit] (too heavy to run pre-emptively); an end-of-turn check sets at: [turn]. See the moments. Rules live in their bundle’s block.