Codify a convention
A design convention that lives only in a decision log is a convention the next PR will break. This skill is
the procedure behind doc 18's rule that a convention change lands with its codification in the same PR.
Rationale and the four-artefact model live in
../../docs/18-design-governance.md — this file is the checklist.
When to use this
Right after a convention is decided: in a critique's priority list ("four grammars for time"), in review
("we say Time here and Timestamp there"), or in a design pass. Not for a one-off visual fix that changes no
rule.
Procedure
State the convention in one sentence with its unit of application: "An instant is a UTC stamp
2026-07-03 11:57Z in mono, in the cell; the relative phrase lives in its tooltip." If it does not fit
in a sentence it is two conventions; split it.
Find every site. Grep for the old grammars, not the new one — the drift is in the variants:
grep -rnE 'toLocale(Date|Time)?String|Intl\.DateTimeFormat|formatDistance|<th[ >]|rounded-(sm|md|lg|xl)' src
List the sites in the PR description. A convention applied to three of five sites is a fourth grammar.
Give the convention one home in code. A formatter in the time module, a primitive in ui/, a class
in the dress file. Every site imports it; no site re-implements it. Delete the variants.
Write the sentence into .impeccable.md under the matching heading (Time, Table heads, States, …).
Keep the file short: the sentence and, when useful, the name of the module or primitive that owns it.
Write the paragraph into the field manual (docs/design/<date>-<direction>.md): what was decided,
what was rejected and why, which sites changed, the gate results at the landing commit.
Add the mechanical rule if the convention has a signature. Open the product's
eslint-design-contract.config.mjs: if the old grammar is an AST-visible call, import, element or class
string, add a selector to SELECTORS (never a new config object — see the flat-config trap in doc 18)
and, if a legitimate exception exists, register the file with a reason. Run the gate; it must be red
before your code change and green after. If the rule cannot tell the legitimate case apart, add the
convention to the design skill's review-time guardrails instead and say so in the PR.
Re-copy the skill references if a token or dress class moved (arche-design/skills/<language>/ references/tokens.css / dress.css), commit that in arche-design with the same date stamp.
Run the gates (verify, or at least lint · typecheck · token-contract · design-contract · unit ·
the e2e specs that read the changed surfaces) and put the results in the field-manual paragraph.
Output
One PR containing: the code, the .impeccable.md sentence, the field-manual paragraph, the lint selector
(or the stated reason there is none), and the site list in the description.
1---2name: codify-convention3description: Use when a design convention has just been decided (a time grammar, a head style, a title size, a state word, a colour rule) — lands the code, the .impeccable.md sentence, the field-manual paragraph and, when the rule has a signature, the design-contract lint, in one PR.4---56# Codify a convention78A design convention that lives only in a decision log is a convention the next PR will break. This skill is9the procedure behind doc 18's rule that **a convention change lands with its codification in the same PR**.10Rationale and the four-artefact model live in11[`../../docs/18-design-governance.md`](../../docs/18-design-governance.md) — this file is the checklist.1213## When to use this1415Right after a convention is decided: in a critique's priority list ("four grammars for time"), in review16("we say Time here and Timestamp there"), or in a design pass. Not for a one-off visual fix that changes no17rule.1819## Procedure20211. **State the convention in one sentence** with its unit of application: *"An instant is a UTC stamp22 `2026-07-03 11:57Z` in mono, in the cell; the relative phrase lives in its tooltip."* If it does not fit23 in a sentence it is two conventions; split it.24252. **Find every site.** Grep for the old grammars, not the new one — the drift is in the variants:2627 ```bash28 grep -rnE 'toLocale(Date|Time)?String|Intl\.DateTimeFormat|formatDistance|<th[ >]|rounded-(sm|md|lg|xl)' src29 ```3031 List the sites in the PR description. A convention applied to three of five sites is a fourth grammar.32333. **Give the convention one home in code.** A formatter in the time module, a primitive in `ui/`, a class34 in the dress file. Every site imports it; no site re-implements it. Delete the variants.35364. **Write the sentence into `.impeccable.md`** under the matching heading (Time, Table heads, States, …).37 Keep the file short: the sentence and, when useful, the name of the module or primitive that owns it.38395. **Write the paragraph into the field manual** (`docs/design/<date>-<direction>.md`): what was decided,40 what was rejected and why, which sites changed, the gate results at the landing commit.41426. **Add the mechanical rule if the convention has a signature.** Open the product's43 `eslint-design-contract.config.mjs`: if the old grammar is an AST-visible call, import, element or class44 string, add a selector to `SELECTORS` (never a new config object — see the flat-config trap in doc 18)45 and, if a legitimate exception exists, register the file with a reason. Run the gate; it must be red46 before your code change and green after. If the rule cannot tell the legitimate case apart, add the47 convention to the design skill's review-time guardrails instead and say so in the PR.48497. **Re-copy the skill references if a token or dress class moved** (`arche-design/skills/<language>/50 references/tokens.css` / `dress.css`), commit that in `arche-design` with the same date stamp.51528. **Run the gates** (`verify`, or at least lint · typecheck · token-contract · design-contract · unit ·53 the e2e specs that read the changed surfaces) and put the results in the field-manual paragraph.5455## Output5657One PR containing: the code, the `.impeccable.md` sentence, the field-manual paragraph, the lint selector58(or the stated reason there is none), and the site list in the description.