- name
- local-config
- description
- Defines the yellow-plugins.local.md per-project config file schema. Use when authoring commands that should accept per-project overrides for review pipeline behavior, reviewer set narrowing, depth controls, or focus-area filtering.
- user-invocable
- false
# yellow-plugins.local.md — per-project configuration
`yellow-plugins.local.md` is an **optional** per-project config file that
lets a project override defaults for yellow-plugins commands. Place it at
the repo root (next to `CLAUDE.md`). The file uses YAML frontmatter for
structured settings and an optional markdown body for human-readable
notes.
## What It Does
- Provides a single, discoverable location for per-project overrides to
yellow-plugins command behavior.
- Doubles as the **rollback escape hatch** for the Wave 2 review-pipeline
rewrite — projects experiencing high false-positive review noise can
set `review_pipeline: legacy` and the pre-Wave-2 adaptive selection is
used instead.
- Supports optional `reviewer_set.{include,exclude}` for narrowing or
expanding the persona dispatch table without forking commands.
## When to Use
- A project wants to skip specific reviewer personas
(`reviewer_set.exclude: [adversarial-reviewer]`)
- A project wants to force the legacy pipeline during the Wave 2
dogfooding period (`review_pipeline: legacy`)
- A project wants to narrow review focus to specific areas
(`focus_areas: [security, correctness]`)
- A project wants a custom `review_depth` regardless of diff size
(`review_depth: large` to always invoke `adversarial-reviewer`)
When the file is **absent**, all commands use their built-in defaults.
The file is purely additive — no command behavior depends on its
existence.
## Usage
### File location and shape
```
<repo-root>/yellow-plugins.local.md
```
Frontmatter-only is the typical shape; a markdown body is optional and
used only by humans (commands ignore the body).
### Schema
```yaml
---
# review:pr / review:all behavior overrides (Wave 2 keys)
review_pipeline: persona | legacy # default: persona (Wave 2 default)
review_depth: small | medium | large # default: auto-detect from diff size
focus_areas: [security, correctness, ...] # default: all areas
reviewer_set:
include: [<agent-name>, ...] # additional agents to spawn beyond defaults
exclude: [<agent-name>, ...] # agents to skip
# Wave 3 keys (documented; consumer adoption tracked per-key below)
stack: [ts, py, rust, go] # default: auto-detect from repo
agent_native_focus: true | false # default: false
confidence_threshold: 0..100 # default: 75
resolve_pr:
cluster_line_distance: 10 # default: 10 (positive integer)
cluster_cap: 20 # default: 20 (positive integer)
verify_command: "pnpm test" # default: unset (no verification)
verify_unattended: false # default: false (opt in for --non-interactive)
verify_timeout_seconds: 540 # default: 540 (integer 1–540)
repass_wait_seconds: 120 # default: 120 (integer 0–480; 0 disables)
resolve_human_threads: evidence # evidence | never | all
---
```
**Consumer adoption status (Wave 3 keys):** the keys are valid frontmatter
today and parsers do not warn on them, but the commands that act on them
land in separate Wave 3 PRs:
| Key | Acted on by | Status |
|------------------------|------------------------------------|--------|
| `stack` | `polyglot-reviewer`, `review:pr` Step 4 dispatch | Pending W3 polyglot scoping. Until then: documented but ignored. |
| `agent_native_focus` | `review:pr` Step 4 dispatch (forces W3.5 reviewers) | Pending W3.5 (`agent-native-reviewers` branch). Until then: documented but ignored. |
| `confidence_threshold` | `review:pr` aggregation gate, `audit-synthesizer` | Pending W3.13b (`yellow-debt-confidence-calibration` branch). Until then: documented but ignored. |
| `resolve_pr.cluster_line_distance` | `review:resolve` Step 3d cluster threshold | Acted on by `review:resolve` (W3.3). Invalid values (non-integer, ≤ 0) emit a warning to stderr and fall back to the default 10. |
| `resolve_pr.cluster_cap`, `verify_command`, `verify_unattended`, `verify_timeout_seconds`, `repass_wait_seconds`, `resolve_human_threads` | `review:resolve` Steps 4–8 | Acted on by `review:resolve`. See the field reference for defaults and fallbacks. |
Authors may set Wave 3 keys today without breaking Wave 2 consumers — the
graceful-degradation rule (unknown keys emit a warning but do not abort)
means the file remains valid forward-and-backward.
### Field reference
| Field | Type | Default | Effect |
|-------|------|---------|--------|
| `review_pipeline` | `persona` \| `legacy` | `persona` | `legacy` falls back to pre-Wave-2 adaptive selection (no learnings pre-pass, no confidence rubric, no new personas). Use as escape hatch only. |
| `review_depth` | `small` \| `medium` \| `large` | auto | Forces a depth tier regardless of computed diff size. `large` always invokes `adversarial-reviewer`; `small` skips it even on large diffs. |
| `focus_areas` | array of strings | empty (= all) | Narrows reviewer set to those whose `category` matches one of the listed areas. Recognized areas: `security`, `correctness`, `reliability`, `performance`, `maintainability`, `project-compliance`, `project-standards`, `architecture`, `testing`, `documentation`, `types`, `adversarial`. Always-on personas (`project-compliance-reviewer`, `correctness-reviewer`, `maintainability-reviewer`, `project-standards-reviewer`) survive the filter regardless of `focus_areas` — filtering them out would defeat the always-on contract. |
| `reviewer_set.include` | array of agent names | empty | Additive — agents are spawned even if their conditional triggers don't fire. This is also the only way to reach `thermonuclear-reviewer` (yellow-review): an opt-in structural-quality persona that appears in neither dispatch table, so it runs only when named here. Two silent drops apply to `include`, both visible in the precedence pseudo-code under "Reading the config from a command". First, `review_pipeline: legacy` branches before overrides are read at all, so `include` has no effect there. Second, `focus_areas` filters the merged set afterwards, and `thermonuclear-reviewer` is not always-on (its category is `maintainability`), so an included `thermonuclear-reviewer` survives only when `focus_areas` is unset or lists `maintainability`. Neither drop emits a diagnostic. |
| `reviewer_set.exclude` | array of agent names | empty | Subtractive — agents are skipped even if always-on or their triggers fire. Applied after `include`. |
| `stack` | array of `ts` \| `py` \| `rust` \| `go` | auto-detect | Forces language-specific reviewer behavior. When set, `polyglot-reviewer` (when triggered) scopes to listed languages and skips non-matching files. Auto-detect uses repo root signals: `package.json` → `ts`, `pyproject.toml`/`requirements.txt` → `py`, `Cargo.toml` → `rust`, `go.mod` → `go`. Multi-stack repos may set this explicitly to scope review to a subset. Acted on by W3-pending consumers (see status table). |
| `agent_native_focus` | boolean | `false` | When `true`, always invokes the W3.5 agent-native reviewer triplet (`cli-readiness-reviewer`, `agent-cli-readiness-reviewer`, `agent-native-reviewer`) regardless of whether the diff touches `plugins/*/agents/`, `plugins/*/skills/`, or `plugins/*/commands/`. Useful for repos that author Claude Code plugins but house plugin code outside the standard `plugins/` layout. Acted on by W3.5 (pending). |
| `confidence_threshold` | integer 0–100 | `75` | Override the Wave 2 confidence aggregation gate used by `review:pr` and `audit-synthesizer`. Values below `75` surface more findings (more false positives, fewer missed issues); values above `75` suppress more (fewer false positives, more missed issues). Set above `100` to suppress all findings (effectively a dry-run). Acted on by W3.13b (pending). |
| `resolve_pr.cluster_cap` | positive integer | `20` | Maximum resolver clusters `review:resolve --non-interactive` dispatches (Step 4). Extra clusters are reported as blocking `not attempted (cluster cap)`. Invalid values warn and use `20`. |
| `resolve_pr.verify_command` | string | unset | Shell command `review:resolve` runs from the repo root before committing resolver fixes (Step 6). On failure it attempts to save a patch under the git common dir, reverts the resolver files and holds `fixed` threads open; a patch the credential screen flags or cannot screen is withheld. Interactive runs show it with `git diff --stat` and ask first. Unset means no verification. The command runs test and script files the resolvers may just have edited from untrusted review comments, with your credentials. |
| `resolve_pr.verify_unattended` | boolean | `false` | Lets `--non-interactive` runs (`/review:sweep`, `/review:resolve-stack`) execute `verify_command`. When `false`, unattended runs behave as if `verify_command` were unset: nothing runs and threads resolve normally. When `true`, verify still does not run if `yellow-plugins.local.md` is tracked by git (`verify skipped (tracked config)`) or the fixes touch runner files — code or config a verify command, package manager or git hook would execute, such as `package.json`, lockfiles, `.npmrc`, `Makefile`, `conftest.py`, `pyproject.toml` or anything under the repo-root `scripts/` or any `.husky/` (full list in yellow-review's `references/resolve/dispositions.md`) — (`verify skipped (runner files changed)`); in both cases `fixed` threads are held open. Keep this file untracked and ignored: add `yellow-plugins.local.md` to `.git/info/exclude` (or a global or repo `.gitignore`), because an untracked file that is not ignored shows as `?? yellow-plugins.local.md` and stops `/review:resolve` at its clean-tree check. The runner gate refuses only a deny list of known entry points (manifests, lockfiles, hooks, root `scripts/`, test setup files), so the command can still execute any source file the test suite imports: opt in only for repositories whose review comments you trust. |
| `resolve_pr.verify_timeout_seconds` | integer 1–540 | `540` | Timeout for `verify_command`; the command is killed 10 s after it. Capped so the run fits Claude Code's 600 s Bash tool limit. Invalid or larger values warn and use the default or cap. |
| `resolve_pr.repass_wait_seconds` | integer 0–480 | `120` | How long `review:resolve` Step 8 polls (every 20 s) for new threads after a successful push before its single re-pass. `0` disables the re-pass. Capped so the poll fits the Bash tool limit. Out-of-range or non-integer values warn and use `120`. |
| `resolve_pr.resolve_human_threads` | `evidence` \| `never` \| `all` | `evidence` | Which human-reviewer threads `review:resolve` may resolve. `evidence`: resolve `fixed` (verified push) and `addressed` (verified pointer); `oos` and `disagree` reply and stay open. Dropped non-actionable threads are also resolved, except under `never`. `never`: reply only and never resolve. `all`: same as bot threads. A thread counts as a bot thread only when every comment you did not write is from a `Bot` account (and all its comments were fetched); one human reply makes it human. Other values warn and use `evidence`. |
| `resolve_pr.cluster_line_distance` | positive integer | `10` | Cluster threshold for `review:resolve` Step 3d. Adjacent threads on the same file with line distance ≤ this value merge into a single resolver task (transitive merge). Larger values cluster more aggressively (fewer resolvers, broader edits per agent); smaller values keep clusters tighter. Invalid values (non-integer, ≤ 0) emit a warning to stderr and fall back to the default. |
### Example: tighten review for a security-critical project
```yaml
---
review_pipeline: persona
review_depth: large
focus_areas: [security, correctness, reliability]
reviewer_set:
include: [security-reviewer, adversarial-reviewer]
exclude: [comment-analyzer, type-design-analyzer]
---
```
### Example: rollback escape hatch during Wave 2 dogfooding
```yaml
---
review_pipeline: legacy
---
# Notes for humans (commands ignore this body)
We hit unexpected false-positive noise from `correctness-reviewer` on
2026-04-29; reverting to legacy pipeline until upstream lands a fix.
Re-enable persona pipeline by removing `review_pipeline: legacy` (or
flipping it to `persona`).
```
### Reading the config from a command
Commands that honor this config (today: `review:pr`, `review:all`,
`review:resolve`) read the file from the project root and merge values with
their built-in defaults. `review:resolve` reads only the `resolve_pr.*` keys;
`review:resolve-stack` and `review:sweep` do not read the file themselves but
run `review:resolve` per PR, so those keys apply to them too.
`review:sweep-all` also reads `resolve_pr.repass_wait_seconds` itself, to
print the worst-case re-pass wait before it starts. The merge precedence is:
1. Command argument overrides (e.g., explicit flags) — highest
2. `yellow-plugins.local.md` frontmatter
3. Command built-in defaults — lowest
Pseudo-code:
```text
config = load_yaml_frontmatter("yellow-plugins.local.md")
review_pipeline = config.review_pipeline ?? "persona"
review_depth = command_arg.depth ?? config.review_depth ?? auto_detect()
# Precedence step 1: review_pipeline branches BEFORE any reviewer override is
# read. The legacy path dispatches its own fixed persona list and consults
# neither reviewer_set nor focus_areas — see
# plugins/yellow-review/references/review-pr/legacy-fallback.md.
if review_pipeline == "legacy":
use_legacy_dispatch(LEGACY_FIXED_PERSONA_LIST)
return
# Precedence step 2 (persona path only): merge reviewer overrides.
focus_areas = config.focus_areas ?? [] # empty = no filter
include = config.reviewer_set.include ?? []
exclude = config.reviewer_set.exclude ?? []
# After computing the default reviewer set per Step 4 of review-pr.md:
reviewer_set = (defaults ∪ include) \ exclude
# Precedence step 3: focus_areas filters the merged set, so an included agent
# is still dropped when its category is not listed.
if focus_areas:
# Always-on personas survive the filter regardless of focus_areas; filtering
# them would defeat the always-on contract documented in
# plugins/yellow-review/commands/review/review-pr.md Step 4.
always_on = {project-compliance-reviewer, correctness-reviewer,
maintainability-reviewer, project-standards-reviewer}
reviewer_set = always_on ∪ filter_by_category(reviewer_set \ always_on, focus_areas)
use_persona_dispatch(reviewer_set)
```
### Validation
- Unknown top-level keys → emit a warning to stderr but do not abort.
Forward-compatibility matters more than strictness for an optional
config.
- Unknown reviewer names in `reviewer_set.include` → emit a warning naming
the unrecognized name. The graceful-degradation guard in `review:pr`
Step 4 handles missing agents at dispatch time anyway.
- Mutually exclusive entries (same name in both `include` and `exclude`)
→ `exclude` wins; emit a warning naming the conflicting agent.
- `review_pipeline` values other than `persona` / `legacy` → fall back to
`persona` and emit a warning.
- `stack` entries other than `ts` / `py` / `rust` / `go` → drop the
unknown entry, emit a warning naming it. An empty array after dropping
unknowns falls back to auto-detection.
- `agent_native_focus` non-boolean values → fall back to `false` and
emit a warning. Common mistake: quoting the value (`"true"`) — YAML
parses that as a string, not a boolean.
- `confidence_threshold` outside `0..100` → clamp to the range and emit
a warning. Non-integer values fall back to the default (`75`).
- `resolve_pr.*` keys with an invalid type or range → emit a warning naming
the key and fall back to its default (see the field reference). An empty
`verify_command` counts as unset. `review:resolve` reads these keys once
before any agent runs and ignores later edits to the file.
### Example: TypeScript-focused plugin repo with strict gating
```yaml
---
review_pipeline: persona
review_depth: large
focus_areas: [security, correctness, project-compliance]
stack: [ts]
agent_native_focus: true
confidence_threshold: 60
---
```
Effect, once Wave 3 consumers land: `polyglot-reviewer` scopes to
TypeScript only, the W3.5 agent-native reviewer triplet is always
invoked, and the confidence gate fires earlier (60 vs. default 75) so
borderline findings surface for human review.
## Migration
Projects that previously relied on hard-coded defaults need no migration —
the absence of `yellow-plugins.local.md` keeps existing behavior. The
file is opt-in.
## Related
GitHubで見る