Writing signs
A sign is just-in-time guidance for an area of your codebase. It steers; it never blocks. You write signs as type: sign entries in a bundle’s signposts: list.
Recap from concepts: a sign is delivered the moment the agent reads a file in its area, and re-injected as the context grows, so it stays present without bloating the agent’s starting context. Think flashlight, not rulebook.
The shape
Signs live in a bundle’s signposts: list, each tagged type: sign (a sign and the rule it explains sit together in that one list). Your own ad-hoc signs live in the local bundle; a shared set lives in its own bundle.
local:
title: Local guardrails
summary: Ad-hoc signs and rules for this repo.
signposts:
- type: sign
id: db-area # a short unique name
at: [touch] # the moment it fires
on: ["src/db/**"] # the area this lights up for
text: |-
Queries go through the repository layer, never inline in a route.
Migrations are append-only — never edit a shipped migration.
| Field | What it does |
|---|---|
| bundle | The top-level block it lives in (local, fcis, …) — a title, a summary, and a signposts: list. A bundle is what install and propagate move as a unit. |
type |
sign (steers) or rule (blocks). |
id |
A short unique name for the sign. |
at |
The moment it fires — touch (on the area) or session (orientation, below). Always explicit. |
on |
The path pattern(s) that trigger a touch sign. When the agent reads/touches a matching file, it’s injected. |
text |
The guidance itself — kept short and specific to this area. |
file |
Instead of text:, inject the contents of a repo file (handy for a longer project brief on a session sign). |
Orientation — the composed CLAUDE.md (at: [session])
Every bundle may carry one at: [session] sign — a few terse lines of orientation. At session start (and after resume, /clear, and a compaction) the engine injects the concatenation of them all. The opening context an agent sees becomes the sum of the bundles the project adopted: install the security bundle and “the .env files are encrypted, read them freely” appears in every session; drop the bundle and the line goes with it. Nothing to keep in sync by hand, and it travels on cherry-pick — the reason this beats writing a managed block into CLAUDE.md.
core:
title: Core
summary: Cross-cutting orientation and guards.
signposts:
- type: sign
id: orientation
at: [session]
text: |-
docs/arch/ is the system prior — read before structural work.
Repeated commands live in justfile recipes, never package.json scripts.
Discipline: one session sign per bundle, a few lines each — orientation says where and what regime; depth stays in the area signs that fire just-in-time. The report card tracks the composed size so it can’t creep silently.
The re-injection cadence
How often a sign is refreshed as the conversation grows is set once, in config::
config:
drift_tokens: 200000 # re-inject a sign after ~this many tokens since it was last seen
Lower it if your agent drifts quickly on long sessions; raise it if signs feel repetitive. A single sign can override the global cadence with its own drift_tokens.
Overlapping areas — both apply
Areas can overlap, and that’s by design. When a file matches more than one sign — a broad one on a parent folder and a finer one on a child — both are injected, in the order they appear in signposts.yml. There’s no “most specific wins”: the agent gets the area’s general guidance and the specific sign layered on top.
local:
title: Local guardrails
summary: House-style signs.
signposts:
- type: sign
id: src-area
at: [touch]
on: ["src/**"]
text: "House style: small functions, no default exports."
- type: sign
id: db-area
at: [touch]
on: ["src/db/**"]
text: "Queries go through the repository layer; migrations are append-only."
| Agent touches… | Injected |
|---|---|
src/app/page.ts |
src-area |
src/db/users.ts |
src-area + db-area — both, in YAML order |
Each sign keeps its own re-injection clock — it reappears on first touch, then only after its drift_tokens have passed since it was last shown — so overlapping signs don’t all re-fire together. Two consequences worth knowing:
- Layer deliberately. Put broad house-style on a parent area and specific guidance on child areas; a deep file usefully accumulates both.
- Keep signs short. Because a deep file can pick up several at once, length adds up — scope each sign tightly to its area.
Writing good signs
- One area, one sign. Keep each scoped to the folder it’s about — that’s what makes delivery just-in-time instead of a wall of text.
- Shape and judgement, not bans. “Mind the shape here” belongs in a sign. “This is never allowed” belongs in a rule — where a block can carry the same message at the moment it’s needed.
- Short. A few lines. It’s competing for the agent’s attention with the task.
- Let irrelevant ones sleep. A sign for an area the agent never touches costs nothing — no need to prune for projects that don’t use that area.
Sign or rule? If the agent must not do something — it’s costly, irreversible, or has one correct form — reach for a rule instead (or as well). Signs give the chance to get it right; rules guarantee it.