Option (a) with riders: the ::before slop stands for bare markers (the
Material mdc-touch-target / Android touch-delegate / iOS hit-inset lineage).
Rider 1: dense coarse stacks must keep pitch >= --touch-target (WCAG excludes
overlapped area; bare boxes still hold AA via 2.5.8 Spacing worst-case).
Rider 2: the Field labeled-row task stays open as the ADDITIVE improvement —
the row becomes the real AAA target (2.5.5 Equivalent) and the slop remains a
harmless net. Chronicle updated in changelog §37; standing in reference §37;
handoff block resolved.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both were type-only: the assertions read morfo-declared keys (data-editing,
role) that DO reach the provider props at runtime through the renderProps()
spread, but the static type of runtimePart.assert erases them. Read them
through an explicit indexed cast instead; suites stay green (6/6). Baseline
drops 59 -> 57 — protocol number updated in the handoff, with the remaining
out-of-mandate errors inventoried (9 src/: file-upload x4, navigation-menu,
arts/connection x4; ~48 web/ demos).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mark S6 done (16 fields compose field.css + 199 orphaned recipe tokens pruned +
date/time pickers de-aliased onto the Field public tokens, commits 5eb92132..
d1670caa). Rewrite "lo que queda" to start at S8 calendar-surface; record the
two non-blocking follow-ups (TimeRangePicker endpoint data-size threading
task_d66356de, PickerProvider deferValue migration) and the S6 method lessons
(run the recipe-css-contract vitest scope after CSS refactors; --field-segment-
height only exists under [data-field][data-size]).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mark the workstream done in the continuation handoff: orca translated (A2),
active-app promoted to an architecture chapter (B1), historical DESIGN docs
moved to decisions/ (B2), runtime-artifact shortcut rerouted (B3). Guards green
at close (arts:check 0/0/22, docs:check 0 err).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "I want to… use a runtime artifact" row in docs/README.md now routes
through the composition root (architecture/active-app.md) → the arts map
(arts/README.md) → the per-artifact README. Map/aliases were already complete
(9e48b80b). docs:check 0 errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the three superseded arts design-phase records into the E3 corpus
(already indexed in docs/decisions.md), verbatim-as-historical:
- docs/decisions/design-connection.md (from src/arts/connection/DESIGN_CONN.md)
- docs/decisions/design-session.md (from src/arts/session/DESIGN.md)
- docs/decisions/design-timer.md (from src/arts/timer/DESIGN_TIMR.md, kept Spanish)
Each gets a `status: historical` frontmatter block; the current reference for
each subsystem is its README.md. A stub at every old src/arts/* path keeps the
~30 source provenance citations (`DESIGN_TIMR §12.8`, `DESIGN.md §7.Q1`, …)
resolving. Bodies kept verbatim (no reformat). docs/decisions.md index + naming
note updated to the new paths. docs:check 0 errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the active-app reference into the book tree as the arts composition root,
the App-side analogue of active-uix.md:
- new docs/architecture/active-app.md (frontmatter + fixed relative paths); the
dated Spanish "Handoff 2026-05-13" heading becomes a timeless "Quick start".
- extract that Handoff block verbatim (ES) to docs/process/handoffs-2026-05.md,
alongside the other extracted arts/uix handoffs.
- stub src/arts/active-app/README.md pointing at the chapter (active-uix pattern).
- wire the chapter into the docs/README.md E1 map.
docs:check 0 errors (11 pre-existing warnings unrelated).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the provider→recipe direction of the CSS-var drift stumble the
architecture-faithful way: the morfo is the source of truth. A component now
declares the functional custom properties its soma provider writes and its eidos
recipe reads:
cssVars: [{ name: 'progress', … }, { name: 'angle', … }] // → --knob-progress, --knob-angle
- types: `MorfoCssVar` + optional `cssVars` on `Morfo` (additive; the 133
existing morfos are unaffected).
- schema: validates `cssVars` (array of `{ name, description? }`).
- compile: exposes `contracts.cssVars` as full names `--{kebab}-{name}`.
- knob morfo declares its two contract vars.
- guard (recipe-css-contract.test.ts): enumerates morfos via `import.meta.glob`
and verifies BOTH sides honour each declaration — the provider writes
`--{kebab}-{name}` AND the recipe reads it — so renaming one side without the
other fails loudly. Proven non-vacuous (a bogus cssVar is flagged on both
sides).
Why A1 and not a grep cross-check (A2): in the CSS a provider-written functional
var and a consumer-override alias are indistinguishable, and providers publish
hook vars the recipe doesn't consume — a grep gives ~17 false positives (proven
earlier). The morfo declaration is what disambiguates.
npm run check 59 (baseline, 0 in touched files); eidos recipe-css-contract 24/24;
morfo suite green (only the pre-existing dialog-role test fails); docs:check 0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a '### Authoring notes' subsection to soma.md §6 grounded in the real
defs — state<T>() vs $state, role optionality on a Provider, Without<>/
PrimitiveDivAttributes, and gesture pointermove/up ownership. Closes STUMBLES
#9; only #7 (soma->eidos CSS-var contract, design-open) remains.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
User decided (mirroring F7's corpus-shape call): arts stay
E5-in-place-reconciled with only active-app promoted to an architecture
chapter; orca stays E5 for now; the DESIGN docs move to docs/decisions/
translated. B1/B2 locked; A1/A2 (drift + language) unaffected.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The docs reconciliation (F6 drift-fix + F7 corpus-libro) covered
active-uix (morfo/soma/sema/eidos) but left the active-app side — the 22
arts/ runtime artifacts — as E5-in-place, un-reconciled. This plan does
for arts what F6+F7 did for the UIX layers, grounded in a 22-agent
per-art audit (every drift verified against the code): ~40 doc-vs-code
mismatches across 15 arts, 12 READMEs needing ES->EN, ethereal missing a
README, 3 historical DESIGN docs, dead route/demo cross-refs. Batches
A0 (guard) / A1 (drift sweep) / A2 (ES->EN) / A3 (ethereal README) / B1
(active-app -> architecture chapter, the missing analogue of
active-uix) / B2 (DESIGN docs -> decisions/) / B3 (index+TOC). Three
structural open questions for the user (arts E5 vs book; orca chapter;
DESIGN docs move) mirror F7's corpus-shape decision.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The emit contract explains discrete events (one gesture → one emit → one
hold) but says nothing about direct-manipulation surfaces (Slider, Drawer,
future knob/swipe) where the pointer moves at sample rate while the value
updates continuously. Add a `## Continuous components (drag, swipe, hold)`
section after the emit contract, grounded entirely in the real Slider/Drawer
code (adversarially fact-checked, zero discrepancies):
- One door: `runtime.trigger()` is the sole path (there is no `emitEvent`).
Its Promise represents the whole occurrence incl. the visual hold — so
`void trigger()` is fire-and-forget (correct for high-frequency emits) and
`await trigger()` blocks until the hold ends (correct when a structural
change must observe the resolved signal, e.g. `close`).
- `coincident` vs `post` for a moving value: the move (handle-drag,
drag-progress) is `coincident` (signal + mutation indivisible); the commit
(commit-set) is `post` (handler settles, then the signal celebrates).
Slider is the worked example.
- Throttle continuous POINTER emits, not keyboard: pointer emits are coalesced
to animation frames + floored to a component-tuned interval via ActiveDom's
requestFrame (Slider DRAG_SIGNAL_MS, Drawer DRAG_PROGRESS_SIGNAL_MS);
keyboard commit-set fires once per keydown (already human-paced). Tuning ms
point to the code, never hard-coded here.
- The per-emit `overrides` payload owns pitch/gain/contour; a continuous
event's cascade rule sets only `channels` and MUST NOT override those
primitives, or it would clobber the live payload every frame.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Completes the runed + tabbable removal (F1+F2 in d0f41fb2). Ports runed's 11
DOM-reactive utilities into $adom, window-correct by resolving the window from
the node (getWindow) or an optional window/document — not runed's defaultWindow:
AnimationFrames, ActiveElement (+ activeElement singleton), IsDocumentVisible,
IsFocusWithin, IsInViewport, onClickOutside, PressedKeys, ScrollState,
TextareaAutosize, ElementRect, IsIdle.
Follows the committed ElementSize precedent (raw observers/listeners live in the
arts/adom implementation layer) rather than threading a `dom` param.
PersistedState is intentionally NOT ported: the $storage art already supersedes
it (its README names runed/PersistedState as the thing it replaces) — same call
as boolAttr being covered by boolToEmptyStrOrUndef.
`npm run check` = 59 (baseline, 0 in ported files); server tests unchanged
(pre-existing failures only). The runed + tabbable port is now complete.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The common ARIA pattern "emit aria-label only when there is no Label part"
was not declarable. Add `{ when: 'part-absent', part }` as the logical
inverse of `part-present`, plumbed through the four condition sites:
- types.ts: MorfoCondition union member + JSDoc.
- schema.ts: discriminated-union member (structural accept) + extend the
checkCondition unknown-part guard to cover part-absent (throws on unknown
part, same as part-present — the prior guard only discriminated part-present).
- compile.ts: collectConditionDeps adds the referenced part to partRefs, so
the runtime re-evaluates when the part's presence toggles.
- resolver.ts: shouldEmitMorfoEntry returns !bindings.parts?.[part] — emit
when the part is absent/falsy, suppress when present.
Runtime + keyboard paths delegate to these helpers (evalAttrPlan /
shouldEmitMorfoEntry), so no runtime change is needed. Correct by symmetry
with part-present, which reads the same presence-accurate bindings.parts.
Tests: schema accepts a part-absent condition on a known part and throws on
an unknown part; the compiler collects the part dep; the resolver emits the
value when the part is absent and undefined when present.
Docs: morfo.md condition list gains the part-absent form.
Non-breaking: no existing morfo uses it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Removes the two runtime dependencies that broke the "framework depends on
nobody" doctrine, porting them into our own code (same move as
floating-ui -> $ethereal).
F1 (dropped the deps):
- tabbable -> src/libs/dom/tabbable-core.ts (getComputedStyle/CSS.escape
resolved from the node's own window, iframe-safe); consumed by the
existing tabbable.ts wrapper and surfaced via $adom.
- runed Context + watch/watch.pre/watchOnce -> $libs/reactive.
- runed ElementSize -> $adom/element-size.svelte.ts, rebuilt window-correct
on the node's own window (not defaultWindow).
- Rewired ~33 watch + 3 Context imports; floating (ElementSize) and
focus-scope (tabbable) now consume $adom.
F2 (rest of the pure reactive runes -> $libs/reactive):
- Previous, IsMounted, onCleanup, interval, debounce/Debounced,
throttle/Throttled, StateHistory, FiniteStateMachine, resource.
- Dropped runed's React-style use* prefix; reused framework toValue
(=extract) + MaybeActiveOrGetter; added Setter<T>.
runed + tabbable removed from package.json. `npm run check` = 59 (baseline,
0 in ported files); tests unchanged (pre-existing failures only).
F3 (remaining DOM-reactive runes -> $adom on ActiveDom, + PersistedState
-> $storage) is pending: see docs/process/continue-runed-tabbable-port.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Continuation doc for tomorrow: full session state (C7 + eidos triage +
onion-menu/surface-subtle + functional flags + C10 reframe + S1
vocabularies), plus the three high-confidence implementation plans the
scope-stumbles-s2-s6 workflow verified against the real code (S3
part-absent condition, S2 continuous-components doctrine, S4
Gesture.rotate) with exact file:line edit maps. S5/S6 flagged for
re-scope. Foreign runed/tabbable WIP in the tree left untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An agent building a component from the docs-book alone could not assign
archetypes / holds / haptic kinds: the closed sets live only in code consts
and the corpus (correctly) forbids copying them into prose, so they were
invisible from the docs. STUMBLES #1, the top stumble.
Fix: docs/canon/vocabularies.md is GENERATED from the consts by
'npm run docs:vocabularies' — the ONE sanctioned place the lists are spelled
out (generated = no drift objection), which every other doc links to. It
covers part archetypes (with a one-line role each), sema families with their
default hold/persistence + per-intent overrides, verbs by family, intents,
the perceptual-duration scale, haptic kinds, the 33 palette scales, sizes,
variant archetypes, and the shared common.* strings.
- scripts/docs-vocabularies.ts imports the consts (robust — no fragile
JSDoc/regex parsing) and formats them; the generation is an exported
function so docs-check can compare.
- Added ARCHETYPE_DESCRIPTIONS to morfo/types.ts (co-located with
ARCHETYPE_VOCABULARY, satisfies Record<MorfoArchetype,string>) as the
authoritative one-liners the appendix reads — descriptions become
first-class data instead of inline union JSDoc a tool would have to parse.
- docs-check gains I7: it regenerates in-memory and fails if the committed
file drifts from the consts (negative-tested: corrupt -> error, regen ->
green). The appendix's own counts pass I1, so I1 doubles as a second guard.
- Linked from README (E2 canon row + two 'I want to' shortcuts), CANON.md
(doctrine here, enumerated lists there), architecture/morfo.md (pick a
part's archetype), and the component-audit minimum-package list (STUMBLES
#8).
docs:check 0 errors; npm run check unchanged in my files.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cleared the error-severity flags that were mature components missing a
documented exception, not real gaps:
- 4 service components (format-date, format-number, relative-time, trans)
get their E-2.2 exception: they render text via $format/$langs with no
visual recipe — no {name}.css by design.
- alert-dialog gets its R-1.1 exception: it composes Dialog (soma reuses the
Dialog runtime, data-dialog-* markers reach the DOM, dialog.css paints the
chrome) — a [data-alert-dialog] root rule would style nothing.
- card gets its missing A-1.3 texts.label (+ langs entry, registered) —
card had no label at all.
C10 (maturity tiers) reframed after the classification exercise showed the
audit VERDICT is the maturity oracle, and 'experimental' is nearly empty
once exceptions are documented: stable = PASS (82); preview = NEEDS-WORK on
docs/demo only (E-2.3/F-1.x/D-*) — functionally sound but underdocumented,
which includes mature components like button/table/listbox (a README gap is
not immaturity); experimental/WIP = a real FUNCTIONAL gap or foreign track.
C10 folds into C8 (document the tier + exceptions when writing READMEs).
Real gaps registered, NOT silenced: float-panel A-3.7 (28 mutating keyboard
actions, 6 events — a morfo-completeness question), card-group (no morfo),
natural-time-picker A-3.1; announce/clipboard R-1.5 (live-region /
composed-focus) await their READMEs in C8 to host the exception.
npm run check at the 59 baseline; alert-dialog -> PASS.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Accumulated test debt (the eidos suite had been red across several feature
landings — THEME-SYS-1 named z-index, palette-first static donor library,
spin-field extraction, icon stroke-map refinement — none of them regressions;
baselined against HEAD). Each triaged stale-vs-bug by confirming current
behavior in code before touching a line.
Stale tests (code correct/improved, assertion not updated):
- z-index '76' -> 'var(--z-index-overlay-tooltip)' (the code is guarded by
THEME-SYS-1 in contracts.test.ts — the raw integer is forbidden).
- frost '80%' -> 'var(--depth-overlay-translucency, 80%)' (parameterized).
- C7 size-bundle coordinate ('--control-height-md' -> '--size-md-control-height').
- icon visual-stroke ratios 1.5->1.125, 1->0.923 (stroke map md 2->1.5, xxl
icon 48->52px; the arithmetic follows).
- delegate/CSS-only purple-9: the slim foundation legitimately emits the
role-referenced donor scales (purple=primary) as overridable :root defaults;
the real delegation guarantee is 'no baked [data-theme] block', which the
assertion now checks.
Test over-flagging (fixed the test, not the code):
- CSS-only shared layer: spin-field (no wrapper/index) excluded from the
wrapper/barrel/namespace contracts (CSS_ONLY_LAYERS).
- soma-runtime vars: 'var(--x, fallback)' is runtime-optional (the STUMBLES #7
soma->eidos surface) — only bare 'var(--x)' requires a recipe declaration.
- orphan check made corpus-wide: composition means a sibling consumes a
component's tokens (date-picker/month-grid consume calendar-control-*).
- natural-time-picker fixed-tone sky colors documented-exempt (Words-rail
doctrine).
- dialog drift-detector converted to a synthetic fixture (dialog.css no longer
ships the dead rule it self-tested against).
Real code fixes:
- declare '--pin-input-cell-readonly-bg' (I consumed it in C2b without
declaring it).
- remove 5 truly-dead tokens (drawer content-bg + 4 color-field
format-select/segment — 0 consumers anywhere in eidos/soma/web).
- clamp time-picker's 'size' to PopoverSize before forwarding (xs->sm):
TimePickerSize has xs, PopoverSize doesn't — a latent type bug my recipe
change surfaced via a complexity-budget flip. The fix dropped check 61->59,
also clearing navigation-menu-content of the same class.
card-group flagged as a REAL gap (KNOWN_MISSING_MORFO): it ships parts + chrome
but no morfo (morfo-first violation) — tracked, not silenced.
Verified: vitest src/uix/eidos 23 files / 289 tests green; npm run check 59
(better than the 61 baseline); component:audit 0 BROKEN.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The ⚠️ 'canon + guard, not consumed' asterisk is retired: the C7 sweep
made all 34 size-bearing recipes consume the bundle coordinate, so the
row is ✅ (canon + enforcement + consumption). The footnote also drops
the stale control/compact/dense archetypes (superseded by the 2026-06-17
1:1 override) — control text follows the typographic scale 1:1.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The size bundle (--size-{k}-control-height / -font-size / -icon-size,
theming/reference §5) was emitted + guarded but consumed by ONE recipe
(the toggle pilot). This sweep points every recipe at the canonical
size COORDINATE instead of the raw primitive of the same coordinate:
366 refs across 34 recipes (--control-height-{k} -> --size-{k}-control-
height, and the font-size / icon-size coordinates likewise). Retuning a
size's bundle now reaches every consumer; a recipe that maps a key to a
DIFFERENT size's coordinate stays visible as a deliberate deviation.
The bundle vars are pure aliases of the primitives, so this is
computed-value identical — verified in a live browser: the accordion
chain --accordion-trigger-min-height-md = --size-md-control-height =
--control-height-md all resolve to calc(36px * 1 * 1). The generated
diff is 366-for-366 pure name swaps, nothing else moved. base/xxxl have
no size bundle and correctly stay on the typographic primitive.
Guard: recipe-css-contract forbids the raw size-coordinate primitive in
recipe token values (var(--control-height-{k}) etc.) — the drift can't
creep back. This is the ⚠️->✓ that theming/notes' comparison table flags
as the size canon's remaining asterisk (canon + guard, now + consumption).
component:audit 0 BROKEN; npm run check at the 61-error baseline; the
size-bundle guard + palette-cascade guard both green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two user findings from the Knob build exercise (an agent building a new
component from the docs alone — STUMBLES.md).
Quarantine: the superseded fossils no longer share shelf space with the
live corpus. docs/old-deprecated/ (with an index README explaining what
lands there and pointing readers at docs/README.md) now holds the
executed audits and fix plans: fable_audit, fable-eidos-audit,
inherit_audit + inherit_fix_plan, ARCHETYPE_COHERENCE_AUDIT_2026-06-19
(still citable — the component-guide banner and the eidos components
README repoint to it), COMPONENT_COHERENCE_AUDIT. pendiente.md (a live
pending list, not a fossil) moved to docs/process/. docs-check treats
the folder as sealed chronicle (I1/I2 exempt; I6 skips its internal
links, as its README promises). Root-level *.md is now: README, CLAUDE,
AGENTS + the user's own working files.
STUMBLES fixes applied on the spot (the doc-class ones):
- #2 kind drift: the REAL enum is 'public' | 'private' | 'virtual'
(MorfoPartKind, 671/9/14 uses) — morfo.md omitted 'private', the
checklist invented 'internal' (0 uses). Both fixed; I2 gains the
phantom-'internal' guard. A-2.1's row now says what the audit script
actually checks (kebab only — the archetype may vary, the Toggle
provider-IS-trigger doctrine).
- #6: the langs catalog SHAPE (flat keys, per-language leaves, named
export, index registration) is now shown in morfo.md instead of only
its location.
- #8: component-audit s0 defines the minimum brief package as an
explicit 8-file list.
The engineering-class stumbles are registered as plan batches S1-S6
(generated vocabularies appendix, continuous-gesture trigger doctrine,
part-absent condition, Gesture.rotate, the soma->eidos CSS-var
contract, minor frictions). docs:check 0 errors, 11-warn baseline.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
palette-{slot} is now RESERVED recipe vocabulary: normalizeRecipeTokens
(render-css) appends the full per-scale color:{scale} cascade for every
donor scale in PALETTE_SCALES to any token named palette-track/element/
border/solid/solid-hover/text/contrast — author-declared color:*
declarations win, only absent scales are appended. This is the
structural end of the hand-maintained-subset era: a recipe opts into
the per-instance palette by naming the token, and the generator
guarantees all scales (the 31-vs-33 drift class cannot recur at this
layer). Applies to app-config recipes too, since the hook sits where
every RecipeTokenSet is normalized.
Button's seven manual spreads are gone from base.ts (the helpers moved
into the generator); its generated output is byte-identical. Toggle —
which already exposed palette-* tokens — universalized itself: +330
generated lines, and <Toggle color="teal"> / color="steel" verified
resolving in a live browser (steel and fuchsia are exactly the two
scales the hand-kept lists used to miss). eidos-lint now classifies
donor-scale values on data-color as sanctioned eidos-only vocabulary
(the TSC color:* axis extension) instead of invalid — the morfo enum
keeps declaring the semantic roles, per the pilot's deliberate
runtime-open design. Guard: recipe-css-contract pins the LAST scale of
PALETTE_SCALES for every palette-bearing recipe in the generated css.
Also: the stale '31 physical color scales' test TITLE says 33 (its body
already asserted 33). Recorded in the plan: the eidos suite carries 15
pre-existing failures in 6 files (baselined against HEAD before this
change) — their triage is its own batch. npm run check at the 61-error
baseline; lint toggle baseline green again.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 30 eventful morfos that never declared expression: ALL have a
per-component pack in src/uix/sema/components/ — so the classification
(book-deviations D.4: pack / family-default / delegated / none) is
unambiguous: expression: 'pack' across the board, placed after scope
per the switch/button idiom. The sema surface of the contract is now
explicit on all 82 eventful morfos instead of inferred from pack-file
existence. morfo:vocabulary clean (the one naming warn is media-player,
foreign WIP); npm run check at the 61-error baseline. Additional
persistence/a11ySemantic annotations deliberately NOT mass-applied —
per D.9 the canonical table is reference, authors declare per real
consumer (the six that need them already do).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every interactive morfo now either anchors its W3C APG pattern or
documents that none exists. Real patterns (20): the picker family +
command + search-field anchor combobox (field/input + popup, the
pattern's own popup taxonomy); date-field mirrors time-field's
spinbutton (per-segment contract); rating-group -> radio;
navigation-menu -> disclosure (the APG's own site-nav guidance);
splitter -> windowsplitter; tag-group/tags-input/virtual-grid -> grid;
clipboard/file-upload -> button (the trigger IS the widget);
announce -> alert (live region); form -> the form landmark;
image-adjustments -> slider; table -> table.
The A-1.4 valve (documented exception) is wired like the R-x rules,
and five components whose widget has NO APG pattern say so in their
README instead of faking an anchor: cropper (2-D manipulation),
pagination (nav + composed Buttons), stepper, timeline (display list),
image-picker (media picker). Remaining warns are the four expected:
card/drag-drop/virtual-list host their note when C8 writes their
READMEs, media-player is foreign WIP.
Catalog: 83 PASS / 47 NEEDS-WORK / 0 BROKEN (was 75/50/5 at the
audit's start). npm run check at the 61-error baseline.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two classes. Real fixes: 8 numeric font-weights tokenized to
var(--font-weight-medium/semibold/regular) in command,
date-range-picker, link-preview, listbox, menubar, textarea,
time-range-picker x2 — same rendered weight, but the foundation now
propagates (verified in a live browser: the token resolves to 500).
Honest annotations: the proportional/identity literals get their
/* literal: <reason> */ valve instead of a fake token — avatar
initials/badge scale with the avatar diameter (physically
proportional), the calc(1em - (md - sm)) family is the Field
label-one-step-down doctrine with a token-derived delta, 1em is
context identity (fab glyph, password toggle), the x0.85 captions are
proportional to their field token, and combobox's -1px is an optical
adjustment on a token-derived size. component:audit R-2.7: 16/16
clean; npm run check at the 61-error baseline.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The R-1.2/R-1.3/R-1.4 sweep, same method as C2a (evidence first): each
offender is either a REAL missing state or a shared-layer/composed case
that must not duplicate rules.
Real fixes, all verified in a live browser (computed-style probes, with
the transition-mid-flight trap from the demo guide s13 accounted for):
switch/toggle/rating-group get the family invalid tint
(--color-threat-element, the shipped checkbox/pin-input precedent);
toggle gets its readonly cursor; pin-input cells get the readonly
muted-surface treatment mirroring date-field segments; the SHARED
spin-field layer gains the readonly treatment it lacked (one fix covers
css-field + number-field); natural-time-picker gets the disabled
opacity/pointer rule mirroring date-field.
Exception valve wired into R-1.3/R-1.4 (was only R-1.1/1.2/1.5/E-2.2)
and cited exceptions added: css-field/number-field (states live in the
shared spin-field layer), dialog (data-disabled sits on the VIRTUAL
provider — defaultElement none — and the pressable surfaces are
composed Buttons), color-picker (color-field owns the invalid rules),
date-range-field (the runtime overlays data-date-field-input on the
same elements, per its own recipe header), time-picker (composed
time-field chrome). natural-time-picker keeps its two warns for its C8
pass rather than guessing paint targets.
All 10 audited components now PASS with 0 R-1.x pending; npm run check
at the 61-error baseline. css-field/number-field READMEs fixed on disk
but unstaged (unrelated local modifications).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The audit's focus check grepped only for :focus-visible, blind to how
the framework actually shows focus: the universal archetype ring
(archetypes.css [data-archetype]:focus-visible with its documented
exclusions), the field shell's :focus-within, and the canonical
state-attr ring ([data-focused] + focus-ring tokens, soma-written — the
spin-field/search-field pattern). Blindly adding :focus-visible rules
to the 10 flagged recipes would have recreated the exact 'double
border' anti-pattern archetypes.css documents against. The check now
accepts the four sanctioned evidences (textarea and search-field pass
on their own css; date-picker passes via exception) and the remaining
non-ring cases carry cited R-1.5 exceptions: css-field/number-field
(ring lives in the shared spin-field layer), dialog (content panel is
the documented foundation exclusion; close/trigger are composed
Buttons), date-picker (foundation ring + content exclusion),
metrics/timeline (no focusable part — interactive only by events).
clipboard/announce host their exception when C8 writes their READMEs.
css-field/number-field READMEs fixed on disk but left unstaged (they
carry unrelated local modifications).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 5 BROKEN components (cascade, gradient-builder, menu-dial, motion,
qr-code) are BROKEN no more: cascade/motion/menu-dial now PASS,
gradient-builder/qr-code drop to NEEDS-WORK with only demo-phase (D-*)
and C8 items left.
Framework-level piece: component-audit gains the documented-exception
valve the checklist already used for A-2.3/R-1.7 — a greppable
'R-x.y exception: reason' line in the component README turns the rule
into a PASS that reports the reason. Wired for R-1.1, R-1.2, R-1.5 and
E-2.2; the checklist rows say the same. This separates deliberate
design (cascade and motion deliberately ship NO recipe — they ride the
foundation stagger + state presets; menu-dial's focus/disabled states
live in the composed Fab/Button recipes) from plain omission, which
stays an error.
Mechanical fixes: texts.label + langs entries for cascade/motion (new
files, registered) and gradient-builder (label added to its existing
entry); menu-dial's missing default export. README contract sections
(Baseline/Comparativa/Decisiones/Gaps/Passive justification + Audit
exceptions) added to cascade, motion, menu-dial and qr-code — mostly
re-heading content those docs already argued; comparativas grounded in
M3 speed dial/MUI SpeedDial/PrimeVue, Framer Motion/AnimatePresence/
Svelte transitions, ark-ui/qr-code-styling per component.
Verified: component:audit 130 -> 78 PASS / 52 NEEDS-WORK / 0 BROKEN;
npm run check at the 61-error pre-existing baseline (0 own).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extending the I1 count guard to color roles and role slots surfaced
four more stale counts the palette fix predicted: the RFCs still said
'13 slots' and enumerated borderHover8 (the slot was retired 2026-07-02,
zero consumers — COLOR_ROLE_SLOTS is 12), and two component READMEs
(css-field, float-panel) still said '8 roles' from before tertiary made
it 9. The guard itself had the same disease it polices: extractConstArray
counted the quoted 'borderHover' inside the comment documenting its own
retirement, so expected=13 and the stale mentions passed — comments are
now stripped before extraction. css-field's README is fixed on disk but
left unstaged (it carries unrelated local modifications).
docs-check now verifies families=8, intents=6, archetypes=26,
palette=33, roles=9, slots=12 against their consts on every run.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
User-caught drift, and it was live in the UI, not just prose: the demo
harness's hand-copied PALETTE_GROUPS enumerated 31 scales — fuchsia and
steel (the two non-Radix scales) never appeared in any demo's
PalettePicker even though PALETTE_SCALES (the const, 33) and the recipe
cascade (paletteScaleDecls maps the const) resolve them fine. Fixed the
two missing entries, typed the groups as PaletteScale[], and added a
dev-time completeness assertion so growing the const without grouping
throws at module load.
Prose swept (the numbers were inherited verbatim from the pre-book
sources, which contradicted their own '33-scale library' headers):
decisions.md color-model row, demo-authoring x4 (incl. the '31-scale
palette' lede), rfc-color-engine x4 (ground-truth + templates
open-question). Where the count was incidental the text now points at
PALETTE_SCALES instead of restating a number.
Guard: docs-check I1 gains the palette count (EN + ES patterns,
~-approximation and type/space-scale exclusions; s-mark added to the
section-number skip). The check line now prints palette=33. This was
the exact class I1 already covered for families/intents/archetypes and
had not been extended to the palette.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Consolidated audit (2026-07-02): 130 components -> 75 PASS / 50
NEEDS-WORK / 5 BROKEN; palette-31 wired in 7 recipes, size-bundle in 1,
focus-visible missing in 11, 17 literal-typography recipes, 41/82
eventful morfos without expression:, 33 without APG anchor, 28 without
README. Demos (D-*) explicitly deferred to the next phase (component
info web). Batches C1-C10 with per-batch verification.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CLAUDE.md 981 -> 350 lines. The 12 session hand-offs (2026-05-08 ->
2026-06-05, ~513 L) moved verbatim to docs/process/handoffs-claude-md.md
(chronicle frontmatter). Reference Documents now point into the book
tree, and the long-stale authority claim is fixed: the semantic
vocabulary source is docs/CANON.md (+ decisions/book-deviations.md),
with the Spanish guide correctly framed as a historical seed at its new
decisions/ home — the correction the guia's own banner had been
announcing since Fase 6. The four sema/eidos doctrine sections (~170 L
of copied tables, cascade diagrams and examples — the copy class
docs:check bans in the corpus) compact to the operational MUSTs
(semaSelector mandatory, packs never replace intent primitives,
channels split by owner, types-over-lint) each with its pointer to
CANON.md / architecture/sema.md / theming/channels.md /
architecture/eidos.md. Two head refs swept (eidos-motion ->
theming/motion.md, COMPONENT_GUIDE s4 -> guides/component-guide.md).
Approved by the user after diff review. docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>