Project rules

Project rules are markdown files in .dsc/rules/ that the model loads on demand or by convention. Use them for: codebase conventions, architectural constraints, gotchas a brand-new contributor would need.

The system is modeled after Cursor's rule engine — same frontmatter shape, same three-way loading semantics — so existing rule files port over with no changes.

Three categories

How a rule loads is decided by its YAML frontmatter. There are three categories, in order of how aggressively they get pulled in:

  • Always-on — frontmatter alwaysApply: true. Loaded into the system prompt every session, every turn. Use sparingly; every always-on rule is a tax on your context window.
  • Glob-triggered — frontmatter globs: ["src/**/*.ts"]. Auto-attached when a matching file is touched. Pattern syntax is gitignore-style (via pathspec) so src/api/**/*.py does what you'd expect.
  • Request-only — neither alwaysApply nor globs. The model uses the request_rule tool to load by name when relevant. The rule's description is what shows up in the "available" list — make it good, that's the model's only signal.

File format

Markdown with optional frontmatter:

---
description: "How to add a new model to the rate card"
globs: ["src/lib/pricing.ts"]
---
# Adding a new model

When you add a model to `src/lib/pricing.ts`:

1. Add the rate card object with `source: "deepseek_published_full_retail"`.
2. Run `npm run sync-pricing` to mirror to the Worker.
3. Commit BOTH `src/lib/pricing.ts` and `proxy-worker/src/pricing.ts`.

The CI guard will fail your PR if the two files drift.

Frontmatter is parsed with python-frontmatter (standard YAML between two --- fences). All fields are optional. If you omit name:, the file stem becomes the rule ID — so pricing.md exposes a rule named pricing.

Where they live

  • .dsc/rules/ — project rules. Walk-up search: Fred starts at your cwd and climbs toward $HOME, taking the first .dsc/rules it finds. Stops at home, never picks the user-global dir as a "project."
  • ~/.dsc/rules/ — user-global rules. Always loaded, layered behind project rules.

Both load. Project wins on name collision — your repo's style.md shadows your user-global style.md, which is the right default for shared codebases.

Listing rules

Inside the REPL, /rules shows two sections:

  • Active — rules loaded right now (always-on + any glob-matched + any explicitly requested by the model this session).
  • Available — rules with a description that aren't currently active. The model can pull these in via request_rule when they look relevant.

If /rules prints nothing, you don't have any rules yet. Drop a markdown file into .dsc/rules/ and re-run.

Authoring tips

  • Title with # H1 — the model uses it as the rule's display name when rendering the active block.
  • Keep each rule focused — one concept per file. A 12-section monolith is harder for the model (and you) than a directory of small named rules.
  • Lead with the rule itself, then a "Why" line, then "How to apply." The model reads top-down; put the prescriptive bit first.
  • Resist the urge to write the rule "for an AI." Write it like an onboarding doc for a junior engineer; the model handles either fine but humans only handle the latter — and you'll be the one re-reading these in six months.
  • Use description for request-only rules even if you also use globs. The description is what the model sees in the "available" list; an empty description means the rule never gets surfaced for explicit pickup.

Use cases

The kinds of things that pay for themselves the moment you write them down:

  • pricing.md — "Always run npm run sync-pricing after editing pricing.ts; the CI guard will fail you otherwise."
  • money-types.md — "We use Drizzle's bigint for money columns, never floats. Microcents is the storage unit; convert at the edges."
  • auth-audit.md — "Auth flows must update the audit_log. Look at the existing magic-link handler for the pattern."
  • migrations.md — "Migrations live in src/db/migrations/ and are numbered. Don't edit a migration that's already shipped."
  • worker-imports.md — "The Worker can't import from src/. If you need shared code, mirror it via scripts/sync-pricing.ts."
Tip

Treat .dsc/rules/*.md as a living onboarding doc. If you find yourself explaining a sharp edge to a teammate three times, write a rule. The next person to hit it (human or model) will thank you.

Disabling rules

Set DSC_RULES=0 to skip the rule system entirely — useful for testing without the always-on injection. The default is on.

See /docs/configuration for the broader .dsc/ directory and how settings, hooks, and rules fit together. See /docs/slash-commands for the full /rules command surface.