The single home for the bugs plugin's config-key contract. The surface is
.claude/bugs.md, layered per the marketplace's config-cascade convention. It is read by
/bugs:scan for lane selection and rotation, plus filing posture, and verified/written by
/bugs:setup. All layers are optional: zero config is a fully working state, because
rotation falls through to the bundled generic default lanes.
Three layers, resolved in this order, where a later layer refines an earlier one:
| Order | Layer | Path | Version control |
|---|---|---|---|
| 1 | user-global | ~/.claude/bugs.md |
outside the worktree, so no git verdict applies |
| 2 | team (tracked) | ${CLAUDE_PROJECT_DIR}/.claude/bugs.md |
must be tracked, the only layer teammates receive |
| 3 | local overlay | ${CLAUDE_PROJECT_DIR}/.claude/bugs.local.md |
must be gitignored, never staged |
Resolution anchors at the repo root, never at the CWD: ${CLAUDE_PROJECT_DIR} when set, otherwise
git rev-parse --show-toplevel. Every layer that exists is read and merged;
reading one layer and stopping is not resolution. A malformed layer degrades soft: surface the error,
name the layer, resolve as if that layer were absent. Unknown keys are inert. Whenever the effective
config is surfaced to a human, report which layer supplied each value. /bugs:scan and /bugs:write
also concatenate each layer's ## Gotchas section (see below) via
scripts/concat-gotchas.sh, which applies the special-root rule: a root that is home (or an
ancestor of it) or outside a git work tree has no team or overlay layer.
Declared here per the cascade convention, beside the keys they govern:
lanesentries concatenate across layers, in layer order, deduplicated by lanename(a later layer's entry for an existing name replaces that one entry only). A layer that declareslanes: [], an explicit empty list rather than an absent key, is an opt-out that drops every lane inherited from earlier layers and from the bundled defaults; it is reported as an opt-out, not as a broken layer. When no layer declareslanesat all, the bundled generic default lanes apply.filing_postureis a scalar, resolved nearest-wins: the last layer that declares it supplies the value; a layer that omits it keeps the earlier value.
Wholesale replacement of an earlier layer is forbidden.
output_dir stays a native Claude Code userConfig option and is never duplicated into this
cascade file. The two surfaces answer different questions and have different owners: output_dir is
one operator's personal report destination on one machine, set through Claude Code's own plugin
configuration prompt and stored in user settings; lanes and filing_posture are team policy about
this repository, which structurally cannot ride a per-user, per-machine setting. Declaring
output_dir here would create two sources of truth for one value with no defined precedence between
them. It is therefore not a recognized key, and a layer that sets it is reported as an inert unknown key.
Markdown with a fenced YAML block (human-readable, shell-greppable). Prose outside the block is
the consumer's own commentary, except a ## Gotchas section which /bugs:scan and /bugs:write
concatenate as the local cascade tier.
A ## Gotchas section in this file (outside the YAML fence) is the local tier for
repo-specific lines the model should see when /bugs:scan or /bugs:write loads. Layers
concatenate in cascade order after the bundled gotchas. Each line records a failure the model
actually hit in this repository, the same failure-driven rule the bundled gotchas follow. Generalizable lines belong in the shipped
skill via an issue to this marketplace; see
docs/conventions/config-cascade/consumer-gotchas.md.
# bugs config
```yaml
lanes:
- name: entrypoints
globs:
- 'src/api/**/*.ts'
- 'src/cli/**/*.ts'
- name: billing-core
globs:
- 'src/billing/**/*.ts'
filing_posture: manual-only
```| Key | Type | Default | Meaning |
|---|---|---|---|
lanes |
list of lane entries (sub-keys below) | bundled generic default lanes | The rotation set /bugs:scan walks on a bare invocation, in declaration order. Concatenating merge with an empty-list opt-out (above). |
filing_posture |
manual-only | allowed |
manual-only |
Team policy for the explicit filing argument. manual-only means --track files nothing and prints why. A standing autonomous lane must not file into this tracker. allowed permits --track to file. Neither value ever makes a bare invocation file: filing always needs the explicit argument as well. Nearest-wins scalar merge. |
| Sub-key | Required | Type | Meaning |
|---|---|---|---|
name |
yes | string, kebab-case | The lane's identity: what --lane <name> selects, what the cursor records, and what the merge deduplicates on. Must be unique within the resolved set. |
globs |
yes | list of glob patterns | The files this lane hunts, relative to the repo root. A lane whose globs match no files is skipped and recorded as skipped, never as exhausted. |
A lane entry missing name or globs is malformed: report it, name its layer, and resolve as if that
one entry were absent. Do not discard the whole layer.
One recursive line covers the overlay here and every other cascade surface:
.claude/**/*.local.*/bugs:setup recommends this line; no plugin writes the consumer's .gitignore.