Repo: motir-core. Carved out of MOTIR-2306 by THE REPLAN ACTION on 2026-08-07, which had bundled this axis with the RADIUS axis and sized both from a one-directory sample. The radius axis stays on MOTIR-2306 (62 sites, an exact token mapping, ~1h); this one is a different animal and this story is its home.
CLAUDE.md's shape rule covers three things: a surface's own radius, its own padding, and its own height. Radius routes mechanically — every raw radius has exactly one semantic role and the substitution is value-preserving. Padding and height do not, and the measurement is the reason.
Scanning every tracked components/**, app/**, lib/** and packages/design-system/src/** source for a string literal that names a radius utility AND a raw own-box p*-N / h-N (measured on origin/main @ 7ffa2ba4, comments stripped):
| literals carrying a raw own-box value | 447 in 190 files |
| of those, on a non-circular (token-radius) surface | 273 padding · 135 height, in 174 files |
on a rounded-full surface (a dot, a switch, an avatar) | 1 padding · 52 height |
That is not a sweep a single card can hold, and gate 4 (≤ 1h of agent run per coding_agent card) forbids pretending otherwise. It needs its own subtask set, planned once the decision below lands.
The instruction "route each raw value to the element-semantic token CLAUDE.md maps its surface to" is unsatisfiable today, because each surface role has exactly ONE padding value and the code uses a scale. Measured, per radius role, on origin/main:
| role | the token says | what the code actually uses |
|---|---|---|
--radius-card | --spacing-card-padding: 24px | 12px ×61 · 16px ×18 · 14px ×12 · 8px ×19 · 10px ×9 · 4px ×7 · 20px ×4 · 24px ×5 · 32/40/56px ×9 |
--radius-control | --spacing-control-x: 10px / -y: 6px | 2px ×23 · 4px ×21 · 6px ×17 · 8px ×27 · 10px ×11 · 12px ×11 · 16px ×4 |
--radius-control (height) | --height-control: 36px | 12px ×12 · 16px ×13 · 28px ×16 · 36px ×8 · 8/10/14/20/24/26/32/48px ×30 |
--radius-badge | --spacing-chip-x: 10px / -y: 2px | 1px ×8 · 2px ×12 · 6px ×14 · 8px ×3 · 4/10/12px ×3 |
--radius-btn | --spacing-btn-x: 18px / -y: 10px (sm 12/8) | 2px ×6 · 12px ×15 · 24px ×5 · 10px ×1 |
--radius-input | --spacing-input-x: 16px / -y: 12px | 2px ×2 · 8px ×3 · 10px ×3 · 12px ×2 · 16px ×3 · 28/32px ×4 |
Five of 145 card-padded surfaces are actually at the card token's 24px. Routing the other 140 to it would re-pad the entire application — a redesign nobody asked for, shipped as a "token substitution". The honest reading is that the padding/height vocabulary is INCOMPLETE: there is no token for an inner panel's 12px, for a dense list row's 4px, or for a 28px compact control, so components reached for the raw value because there was nothing to reach for.
So the first subtask is a DECISION about the token set, and the sweep can only be planned after it.
CLAUDE.md's padding-by-surface and sizing-by-surface tables name every token the sweep will route to.components/**, app/** and packages/design-system/src/** names a token from that scale, or sits in shapeSwapLint's allowlist with the reason it is legitimate.tests/theme/shapeSwapLint.test.ts — created by MOTIR-2306 with the radius check — carries a SECOND check for the own-box axis, in the same exactly-asserted-allowlist shape, and its comment states the own-box ↔ layout distinction it applies.blocked_by it.motir-core/CLAUDE.md — "Shape (radius + spacing + sizing) flows through element-semantic shape tokens", the padding-by-surface and sizing-by-surface tables the decision amends.packages/design-system/theme.css:198-223 — the @theme padding / height tokens as they stand; :360+ — the ten [data-style] blocks that must carry every new token or it will not flip.tests/theme/swapLayerLint.test.ts — the colour-axis guard whose exact-allowlist discipline both checks mirror.