When something in an app looks or behaves wrong, name it before fixing it: what kind of defect it is, which law it breaks if any, why it happened, and how bad it is. The "why" says where the fix belongs, which is the part people get wrong. A defect fixed in the wrong place comes back on the next screen.
How to write one#
One line for people:
<law or none> · <kind> · <root> · <severity> · <what you see> · <where>
L1 clip · visual · bug · wrong · chip label cut, no tooltip · Jobs, Module columnnone · data · default · blocks · saved settings lost, value stored as JSON · useLocalStorageL6 narrow · visual · missing · blocks · nav unreachable at 390px · whole appL3 once · visual · misuse · wrong · two search boxes on one table · History page
Add inferred at the end when you read code but did not render or measure
it. The same fields, as keys, go in an inbox entry and a kit-standard
finding (see the last section).
Kind#
What sort of defect it is. Always set it.
| Kind | Means |
|---|---|
| visual | how it looks; a law applies |
| behaviour | it does the wrong thing (a crash, an ignored prop, a lost value) |
| copy | the words are wrong: voice, length, an error code shown to a person |
| a11y | keyboard reach, focus, hit-target size, screen reader |
| perf | slow; say whether you measured where the time went |
| data | stale, lost or wrong data shown |
| safety | a destructive action with no gate, or no way back |
A defect with no law is still filed, with its kind and a plain sentence. Nothing waits for a law.
Law#
For a visual defect, the law it breaks. The id is stable; the short name is the label.
| Id | Name | The law |
|---|---|---|
| L1 | clip | Fit, then truncate, never clip |
| L2 | quiet | Show only what has something to say |
| L3 | once | One signal, said once |
| L4 | align | Things beside a label share its line |
| L5 | exact | Machine values stay exact, human text stays human |
| L6 | narrow | Every composite declares its narrow behaviour |
| L7 | answer | Every action answers |
| L8 | theme | Both themes are first-class |
| L9 | hover | Controls are visible without hover |
| L10 | anatomy | The same thing looks the same |
The ids continue the numbering in docs/design-language/00-overview.md, which holds the full text of every law. L9 and L10 are in its "ninth and tenth laws" section.
Tie-breaks:
- Seen only at 390px: L6 narrow, even if it is also a clip.
- A signal shown twice is L3 once; a control or value that should be absent is L2 quiet.
- L5 exact cuts both ways: a value recased or reformatted is fixed by showing
it as the machine wrote it, and a raw token shown where a person reads
(
single_select) is fixed by translating it. Say which in the line. - A row inserted above the content that pushes it down (a bulk bar) is L7 answer: feedback must not move the target.
- Two looks for one kind of thing (a table that misses the card edge, a card header unlike its siblings, a pill that looks like a button) is L10 anatomy, even when each look is fine alone.
- A hit target under about 24px is L9 hover, even when the control is always visible.
Root#
Why it happened, which is where the fix goes. Ask in order and stop at the first yes. Each question is answered with a grep or a screenshot, so whoever files the defect can answer it without knowing what the app's agent read.
- Does a kit default or code path produce it with no app override? Cite
the kit file and line.
- It does what its own doc says, and the design is wrong: default. The owner rules on the change.
- It contradicts its own doc or the canon: bug. Fix it now.
- Is there a written rule for this? Cite the canon or kit doc line.
- None: missing.
- Two rules disagree: conflict.
- A rule exists and the app diverged. Is there a kit-standard check?
- No check, or one that does not cover this case: uncovered.
- A check exists but is only a note, or the app never ran it: soft.
- A check ran at fix severity and the app shipped anyway: misuse.
- Is it a wording or behaviour choice no kit rule could settle, or is the app under a product freeze? product, and say which.
Then one flag. off-path: the rule exists but is not in USAGE.md, the first-run brief, or the doc of any component the app imported. This is what "buried" meant, made checkable.
A second root is common, and goes after a plus: default + uncovered means
fix the default and add the check so the next one is caught.
| Root | The fix belongs in |
|---|---|
| default | the kit default, after the owner rules |
| bug | the kit, now |
| missing | a new kit component or canon rule |
| conflict | the canon, reconciled |
| uncovered | a new or wider kit-standard check |
| soft | the check's severity, or the app's habit of running it |
| misuse | the app |
| product | the app's product owner, not the kit |
Severity and reach#
- blocks: a person cannot use or reach it (a phone layout with no nav, settings that vanish).
- wrong: visible and usable, but wrong.
- polish: noticed only on a close look.
A defect seen in a second app is the same entry with its app count raised, never a second finding. Triage sorts by severity, then by how many apps hit it, then puts default, bug and conflict first, because one kit fix covers every app.
Where the fields go#
- Inbox entry (
docs/plan/INBOX.md): one line under Reading,- Class: <law> · <kind> · <root> · <severity> · apps: <n>, plusinferredif it was not measured. A CANON-NOTES line in an app carries the sameClass:text, so it moves to the kit's inbox unchanged. - kit-standard: every check names its law and kind, so every finding in
--jsoncarries them. A finding never carries a root: a check that fires proves the rule is checked, so the root is the app's (misuse) or, if the playground itself fails, the kit's (default or bug). The run ends with a count of findings by law and severity, and marks a finding that was already a note in the baseline. - An agent building an app names the defect first. If the root is misuse, it fixes the app. Otherwise it works around it in the app and files the line, so the kit hears about it.
Sources#
- The plan draft this page came from:
docs/plan/87-defect-vocabulary.md. - A review against 47 real findings:
.claude/output/20261023-feedback-run/seats/defect-vocabulary-review.md. - Root classes first used in the gcp-ingest proposal and the schema-builder
POC audit. Laws L1 to L8 are in
docs/design-language/00-overview.md, L9 was ruled at sitting 1 (D8) and L10 on 2026-10-24 (E1), both in plan 86.