Agent docs

18 · Defect vocabulary

When something in an app looks or behaves wrong, name it before fixing it: what kind of defect it…

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 column
  • none · data · default · blocks · saved settings lost, value stored as JSON · useLocalStorage
  • L6 narrow · visual · missing · blocks · nav unreachable at 390px · whole app
  • L3 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.

KindMeans
visualhow it looks; a law applies
behaviourit does the wrong thing (a crash, an ignored prop, a lost value)
copythe words are wrong: voice, length, an error code shown to a person
a11ykeyboard reach, focus, hit-target size, screen reader
perfslow; say whether you measured where the time went
datastale, lost or wrong data shown
safetya 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.

IdNameThe law
L1clipFit, then truncate, never clip
L2quietShow only what has something to say
L3onceOne signal, said once
L4alignThings beside a label share its line
L5exactMachine values stay exact, human text stays human
L6narrowEvery composite declares its narrow behaviour
L7answerEvery action answers
L8themeBoth themes are first-class
L9hoverControls are visible without hover
L10anatomyThe 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.

  1. 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.
  2. Is there a written rule for this? Cite the canon or kit doc line.
    • None: missing.
    • Two rules disagree: conflict.
  3. 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.
  4. 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.

RootThe fix belongs in
defaultthe kit default, after the owner rules
bugthe kit, now
missinga new kit component or canon rule
conflictthe canon, reconciled
uncovereda new or wider kit-standard check
softthe check's severity, or the app's habit of running it
misusethe app
productthe 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>, plus inferred if it was not measured. A CANON-NOTES line in an app carries the same Class: text, so it moves to the kit's inbox unchanged.
  • kit-standard: every check names its law and kind, so every finding in --json carries 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#

@versable-git/ui · reference, canon, and method, read in place