Writing signs

A sign is just-in-time guidance for an area of your codebase. It steers; it never blocks. You write signs in the signs: section of signposts.yaml.

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 are grouped by namespace (which pack they belong to). Your own ad-hoc signs live under local; a shared set lives under its pack name.

signs:
  local:
    - id: db-area                 # a short unique name
      globs: ["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
namespace The group key (local, neon, …). It’s what install and propagate move as a unit.
id A short unique name for the sign.
globs The path pattern(s) that trigger it. When the agent reads/touches a matching file, the sign is injected.
text The guidance itself — kept short and specific to this area.

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.yaml. There’s no “most specific wins”: the agent gets the area’s general guidance and the specific sign layered on top.

signs:
  local:
    - id: src-area
      globs: ["src/**"]
      text: "House style: small functions, no default exports."
    - id: db-area
      globs: ["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.