CLAUDE.md · git:20260901.17901d2 · 2026-09-01 · sha256 03f7c825338eaee4

CLAUDE.md git:20260901.17901d2A

Immutable. This exact content is served forever at /api/v1/blob/03f7c825338eaee4.

# Working in this repository

A skill and the documents describing it drift apart silently: the skill runs either way, and the
document only fails when somebody trusts it. So changing a skill is not finished when the skill
works — it is finished when the sentences that describe it are still true.

## After changing anything under `plugins/`

1. **Changelog and version together.** A line in `plugins/<p>/CHANGELOG.md` saying what changed and
   why, and the version in `plugins/<p>/.claude-plugin/plugin.json` raised. `verify.py` fails if a
   version has no entry — an installed version nobody can read the reason for is the worst of the two.
2. **Open the documents that carry a claim about what you changed.** Which ones, by what you touched:

   | Changed | Open |
   |---|---|
   | What a skill does | its row in `plugins/<p>/README.md`, and in both root READMEs — the skills table **and** the paragraph that describes it |
   | What a skill needs — a tool, a token, a connector | root READMEs: *Prerequisites* and *What each skill needs* |
   | A check `/fig:lint` runs | root READMEs: *What `/fig:lint` looks at* |
   | Anything crossing `fig` ↔ `pm` | root READMEs: *Where the two meet*, and `.github/two-plugins.html` if the picture names it |
   | A default that ships on or off | wherever a README states that default — a promise turned off in code stays a promise in prose |
   | A config key a document names | `_common/conventions.example.yaml`, whose comments are the schema's own guide |

3. **Both root READMEs, every time.** `README.md` is the original and `README.ko.md` is the same
   document, not a summary of it. Write the English first, then carry the change over.
4. **Generalize it before it ships.** Whatever prompted the change is one team's instance of it,
   not its definition. A rule goes in as the general shape — a canonical page and an archive of it,
   a tracker that may be any tool, a state list each team writes for itself — and the value that
   made you think of it goes in `conventions.example.yaml` as an example, or nowhere. Where a
   convention cannot be assumed at all, the key ships `null` and the check is skipped, rather than
   firing on every file that does it differently. This is the step with no checker behind it: the
   string check below sees names, never framing.
5. `bash tools/verify-all.sh` — every check CI runs.

## What the checker catches, and what it cannot

`tools/verify.py` catches the mechanical half: a skill missing from a README's skills table, a
command in one language's README and not the other, a section count that has drifted apart, a config
key or a skill reference that does not exist, a version with no changelog entry, a team's own value
left in a shipped file.

It cannot read a sentence and tell whether it is still true. Every drift found by hand on
2026-09-01 was of that kind — a README still promising a Figma write that had turned out to be
impossible, a token described as one skill's when a second had started needing it, a lint table
naming one component check when there were three. Nor can it see framing: a rule written around the
way one team happens to work passes every string check there is. A green `verify-all` means nothing
was left undeclared — steps 2 and 4 are what make it true, and true for somebody else's team.

## Conventions

- Commit messages are Korean, in the form `type(scope): what changed, in a sentence`; the body says
  why, and what was tried. Documents and code comments are English.
- The READMEs are read by designers and PMs, not by engineers. A term gets three or four words of
  explanation the first time it appears — `null`, a token, a tracker — and a command that shows up in
  the prose is one the reader can hand to Claude instead of typing. Deep material (troubleshooting
  entries, the repository tree, anything aimed at whoever edits the plugins) sits inside `<details>`
  so the page stays short for the person reading it for the first time.
- Prose never carries a skill count — it goes stale the day a skill is added, and `verify.py` fails
  on one. List the skills, or say "the skills".
- The example config is a schema **and** a guide: a new key arrives with the comment explaining what
  it governs and what happens when it is `null`.
- `_common/scripts/lib/resolve-config.py` and `preflight.py` are copies in each plugin, byte for
  byte. Change one, copy it across — `verify.py` compares them.
- Anything belonging to one team — a brand colour, a product name, a document id — never ships.
  `tools/team-strings.local.txt` is what the check reads; it is gitignored, so a fresh clone skips
  that check and says so. It covers the files inside a plugin, and the ones outside it that are just
  as public: this file, `tools/`, and the diagram sources under `.github/`.