82 KiB
| title | type | audience | authority | status | created |
|---|---|---|---|---|---|
| Next features — initiative registry | registry | human + agent | backlog of user-decided future initiatives — records scope, sequencing and dependencies; it does NOT record verdicts (those live in the RFCs / changelog / book-deviations when executed) | living — items enter by explicit user decision during audits and sessions; remove an item when it ships (and link where it landed) | 2026-07-06 |
Next features — initiative registry
Rules of this file: one entry per initiative, dated, with its origin (which audit/session raised it), its scope in one paragraph, and its dependencies. Entries are registered, not started — starting one is a session decision. Detail lives in the linked doc, never copied here.
1. Tonal-ramp contrast parity (measure, then construct) — 2026-07-06 · Stage 1 DONE 2026-07-19 · Stage 2 CLOSED 2026-07-20
Stage 1 SHIPPED (2026-07-19). Measured with
scripts/contrast-audit.ts(33 scales × 2 modes); drift + user verdicts ratified as doctrine intheming/reference.md §40+changelog.md §44. Outcome:text·11= secondary tier,text-strong= AA-guaranteed; the border vocabulary is Radix-subtle by design (onlysolid·9clears 3:1 by construction) with a decorative-vs-load-bearing split; the focus ring is a config axis (primitives.focusRing+color.focus, singleoutlineper §32) whose shipped default is soft — hardening is a config-value choice, kept as-is. Stage 2 CLOSED (2026-07-20) — no solver needed. The 6 decisions were locked (D1 band · D2 measured-pass · D3 flip-only · D4 opt-in · D5 runtime-first · D6 now), then execution disproved the premise: the template morph does not compute text contrast, it inherits it (steps 11/12 copy the donor's L-curve verbatim; L-driven contrast is ~chroma-invariant under gamut-mapping), so any scale generated from a §40-compliant donor library clears the ratified text floors by construction. Verified on three banks — authored base, leave-one-out regeneration, and 45 out-of-distribution seeds — 0 hard-gate failures (min WCAG 9.7:1). The luminance solver would have had nothing to resolve for realistic inputs → dropped as speculative. Delivered instead: the ratified pair table as shared data ($color→CONTRAST_PAIRS), the audit refactored to consume it + a morph-generated regression bank (scripts/contrast-audit.ts), and a CI guard that locks the inheritance (eidos/lib/contrast-invariant.test.ts— fails if a future donor/generator breaks it). With D2, the base stays verbatim ground-truth; no base→seeds migration. Plan + decision + outcome record:process/contrast-stage2-plan-2026-07.md; chroniclechangelog.md §45.
Origin: A.7 follow-up (theming audit F). The on-solid pair is now computed
at generation with a single criterion (rfcs/rfc-color-engine.md
§8, eidos/lib/on-solid.ts). M3 still guarantees more: contrast by
construction across its whole tonal ramp (container/on-container pairs at
fixed tone deltas — ΔT 40 ≈ 3:1, ΔT 50 ≈ 4.5:1). Our equivalent promises
(text-11 on bg-2/element-3, border-7 vs bg-2 at the 3:1 non-text floor, text
over composited soft surfaces a2/a3) rest on Radix-style curation, unmeasured.
Stage 1 — pair-contract verification (the A.7 pattern generalized): a
canonical table of slot pairs + floors (a user-decided doctrine session),
a measurement pass over pairs × 33 scales × 2 modes at generation
(on-solid.ts generalizes; the compositing math exists in $color), and the
resulting drift list goes to user verdicts (retune vs annotated exception).
Infrastructure is small; the fallout is the unknown.
Stage 2 — by-construction generator (CLOSED 2026-07-20, solver dropped). The
plan proposed a luminance solver that resolves each step to satisfy Stage 1's
pair table for a seed. Execution disproved its premise: the morph inherits
text contrast from the donor (steps 11/12 copy the donor's L-curve verbatim;
L-driven contrast is ~chroma-invariant under gamut-mapping), so every scale from
a §40-compliant donor library clears the ratified floors by construction. Measured
across three banks (authored base, leave-one-out regeneration, 45 out-of-distribution
seeds): 0 hard-gate failures, min WCAG 9.7:1. The solver had nothing to
resolve for realistic inputs → dropped (speculative). What shipped: the pair
table as shared data ($color CONTRAST_PAIRS), the audit consuming it + a
morph-generated regression bank, and a CI guard (eidos/lib/contrast-invariant.test.ts)
that locks the inheritance. The BASE stays verbatim ground-truth (D2); no
base→seeds migration.
Sequence: Stage 1 first (it is the spec for Stage 2). No M3-style on-*
vocabulary expansion — our slots already encode the pairs; the table
formalizes them without inflating the namespace.
2. Per-theme palette-contrast cascade emission — 2026-07-06
Origin: A.7 execution. The per-instance palette-contrast cascade lives
in the STATIC foundation — one polarity for all themes. Today polarity is
computed per configured theme and unanimous (33/33 scales agree across
base-light/base-dark); on disagreement the *-light theme wins
(computeLightSolidScales, documented in
rfcs/rfc-color-engine.md §8). Trigger: the
day a real configured theme disagrees on a scale's polarity, emit the cascade
per-theme instead of statically. Not before — no speculative machinery.
3. Component audit (post-system) — 2026-07-06
Origin: user decision during the theming audit — "de momento no tocamos los componentes, merecen una auditoría adicional una vez que se haya resuelto estas". Gate: the system-level audit (F/G) closes first. Registered items so far, to seed its scope:
- Named-styles adoption by controls (from A.3): controls/recipes that
hand-declare typography instead of consuming
--style-*/ size-bundle typography — likely by error, to be confirmed per component. - Letter-spacing bundle adoption: ~34 recipes consume
font-size/line-heightfrom the size bundle but notletter-spacing; plus ~7 raw letter-spacing literals in component CSS. - Tier-A
data-depthstamping coverage: overlays that should stamp their plane (dialog / drawer / tooltip / menubar / combobox) perrfcs/rfc-depth.md§5. - Calendar family → shared layer/archetype (user design verdict,
2026-07-07): the calendar-grid vocabulary (accent set, day geometry, the
holiday/event marks fixed in audit B.2) becomes a FIRST-CLASS shared layer
— today the family borrows it informally (measured: range-calendar
consumes 79
--calendar-*tokens, month-grid 65, year-grid 65, date-range-picker 38, date-picker 4 — ~250 cross-component borrows), and chronos will draw from it too when its track lands. Materialization to decide at execution: a morfo part-archetype for the day cell (the 26-set grows) and/or acalendar-surfaceshared recipe à la list-surface (--list-item-*precedent). This supersedes "per-component composition repairs" for the B.2 borrowers: the marks reach the whole family through the shared layer instead. - Field family composition — segments under
[data-field](user design verdict, 2026-07-07, audit B.1): segment height derives from the SIZE axis at the FIELD family level —fieldrecipe declaressegment-heightwith size-scoped declarations (calc(var(--field-control-height-{k}) - var(--space-2)); the TSCsize:scope axis exists, unused so far), and the per-component copies die (each x-field today re-declares the wholeheight-{xs..xl}family the field recipe already has). MANDATED by the B.1-(i) verdict: date/time/color-field MUST incorporate the Field wrapper (segments under[data-field][data-size]— probed live: today they don't) as part of the component audit; the three broken per-componentsegment-heighttokens and their consumers were removed (they had computedautosince birth — zero visual change). - Whatever F/G registers as "component-level adoption gap" in
docs/audit/theming-audit.mdwhen that report lands (deliverable G of the 2026-07 theming audit).
4. architecture/sema.md hold-sync — 2026-07-06
Origin: A.4/A.5 execution (holds single-source). SEMA_HOLDS_BY_INTENT
(src/uix/sema/holds.ts) is now THE only hold table (resolver + visual
channel consume it; sema-map.ts's per-family hold column died). The chapter
architecture/sema.md still narrates per-family
holds in ~34 places. Blocked: the file is the author's WIP (uncommitted
edits) — sync it when the author's pass lands, not before.
6. THM-2 rollout — per-instance palette to all surfaces — SHIPPED 2026-07-12; scope boundaries REVERTED by the open-color-cage initiative (2026-07-18/19)
Shipped in 05f0031c (media-player Batch-4 + 14 surfaces + vocab + 4 props)
and cc7e9a16 (avatar root). Every per-component palette cascade now collapses
into the shared --palette-* layer (size win, behavior-preserving for roles);
the vocabulary gained the alpha surface/surface-hover + hover slots.
REVERSED (2026-07-18/19) — "abrir la jaula del color". THM-2 originally kept semantic controls (checkbox/radio/stepper/editable/select/file-upload) and
Avatar.Badgerole-restricted "by design". A later user decision reverses that:coloraccepts the FULL system (role / intent / 33 scales / raw CSS =ComponentColorProp) on every component, no exceptions — content-ink primitives keep their axis as an additive union. Enforced by two guards inrecipe-css-contract.test.ts. Full record + the closed queue (incl. the chart SVG resolver and the Card/Avatar custom-color fix):process/open-color-cage-2026-07.md. Onlyqr-codestays out (itscoloris the QR module ink, not a scale).
5. Book editorial note — "el hold es suelo, no tijera" — 2026-07-06
Origin: A.4/A.5 verdict. The mechanism (hold = floor for the expression,
never a scissor; expression waits animation-finish after the hold, capped by
an absolute MAX_EXPRESSION_WAIT_MS, never derived from the hold) is recorded
in decisions/book-deviations.md D.12 as a
candidate editorial appendix for the book (Diseñando lo que ocurre,
HOMOGENEIZADO edition) — the book gives qualitative regions and this is the
materialization doctrine worth feeding back to the source.
7. Chat block v2 — 2026-07-18
Origin: the chat-* v1 initiative (comparative study + plan, session
2026-07-18; user scoped v1 to the room core). v1 shipped chat-log
(Feed + end-anchored VirtualList), chat-message, chat-composer
(Textarea + FileUpload composition) and chat-typing, plus the
$libs/chat helpers and the VirtualList anchor: 'end' extension. The
deferred surface, registered with disposition (each item also lives in its
component README §Gaps):
✅ SHIPPED 2026-07-19 — composeschat-list— the conversation-list pane (rooms, unread badges, typing-in-list).Listbox(single-select + keyboard) with an avatar/title/preview/meta row; unread bold +Badge, typing-in-list composesChatTyping. Virtualization for very long lists deferred (pairs with the VirtualList bidirectional item).✅ SHIPPED 2026-07-21 — composesemoji-picker— a real picker component (color-picker scale: categories, search, skin tones).Popover+Command(search + grid keyboard + combobox/listbox a11y, its documented "grid mode") + twoToggleGroups (categories + skin tone). Data is the vendored$libs/emoji(baked from emojibase, MIT —scripts/generate-emoji-data.ts): ~1,900 emoji, 316 tone-capable. The grid double-registers Command's List/Item so it keeps the a11y but wears its own grid-cell styling. Delegated (no own events). Full-grid virtualization + per-emoji tone + image emoji (Twemoji/Noto) deferred. The quick-set Popover inChatMessage.ReactionAddstays as the inline tapback bar (different UX).- Mention autocomplete (
@) — combobox-in-textarea forchat-composer; pairs with the futurewords/palabrasintegration. - Threads UI — the side-panel composition over the already-shipped
Feed.Threadsemantics. Jump-to-message highlight —✅ SHIPPED 2026-07-19 —signal-emphasize-targetonchat-message(the morfo event was deliberately left out of v1).emphasizedprop →data-emphasizedrow flash (visual-only, no sema event: reference apps flash silently; the app scrolls via a reply'sonJumpthen togglesemphasizedfor ~1.4s).Read-by list — "seen by N" avatar row (Teams/Messenger pattern) on✅ SHIPPED 2026-07-19 —chat-message.ChatMessage.ReadBypart (the app composesAvatarGroup; the sender'sonMessageSeenfeeds it).- VirtualList chat-mode refinements — bidirectional sparse ranges for jump-into-history far from the loaded window.
Typing avatars — faces next to the✅ SHIPPED 2026-07-19 —chat-typingdots (Messenger).ChatTyping.Avatarsslot (composeAvatarGroup).- Delegate-family ↔ agent chat — the Aura reservation (§F6 of the scene/pack initiative) once agent conversations land.
Deps: none to start chat-list/emoji-picker; mentions depend on the
words integration decision. The transport stays app-land ($connection).
8. blocks tier — reusable page-function compositions — 2026-07-21
Origin: ecosystem analysis session 2026-07-21; user decision: a new tier
for blocks — named compositions of canon components performing a page
function (sticky site header, hero, footer, app shell, docs shell…). Scope:
tier infrastructure (src/uix/blocks/ + $blocks, doctrine at
architecture/blocks.md, B contract, blocks-check guard), 7 canon base
components first (sticky · empty-state · result · callout ·
prose · anchor-nav · sidebar — full 9-phase route each), then the
block catalog (10 site + 10 app + 3 docs). Admission rule mirrors packs:
any behavior with contract surface is promoted to canon BEFORE the block
composes it. Detail + per-item fiches: the execution plan at
process/PLAN-blocks.md.
Status: D-BLK decisions SIGNED 2026-07-21 (D-BLK.1 amended by the user:
the tier lives at src/uix/blocks/, not src/blocks/). F0 SHIPPED
2026-07-21 (069759604: doctrine + $blocks + blocks:check + gallery).
Reference study SHIPPED 2026-07-21 (aff42fee0:
process/RESEARCH-blocks-references.md,
6 research tracks; every phase-0 now checks against it). Amendments E-1…E-5
SIGNED 2026-07-21: F1 = 8 canon components (+nav-tree, data-driven docs
navigation tree); parity-floor = v1 scope policy; F2 = 14 site blocks
(+banner · team · contact · content-section); distribution registered as §9;
⌘K stays app-land.
F3 STARTED 2026-08-19 — app-shell shipped with a signed phase 0 (four
decisions: the block wires no services, two scroll models by prop, the top bar
inside the inset, generated bypass links) and a new canon component in front of
it, SkipLink (WCAG 2.4.1). It closes ledger row A-95, the one thing the
tier could not fix from inside any block: with the shell owning the page's
heights, an in-flow notice and a pinned header overlap by 0px (measured;
before: 49px, with the hit test landing on the strip).
Deps: scheduler block gated on chronos landing; chat-room block
explicitly NOT here (canon chat-* family, §7 above).
9. Blocks distribution — registry + llms.txt + MCP — 2026-07-21
Origin: the blocks reference study (E-4 signed 2026-07-21; dossier
process/RESEARCH-blocks-references.md
§P6). The ecosystem's 2026 table stakes for component/block distribution are
agent-enumerable catalogs: a registry protocol (shadcn registry.json /
registry-item.json), llms.txt (+ llms-full.txt / .md content
negotiation) over the docs corpus, and an MCP server so agents can list and
install blocks (sv-blocks precedent). Scope when started: (a) llms.txt
generation from the docs corpus (cheap — the corpus is already Markdown,
pairs with F4 docs blocks); (b) a blocks/components registry manifest; (c)
an MCP server exposing catalog + morfo contracts (pairs with the
contract-introspection superation angle and the palabras F-B agentive
track). Registered, not started.
Deps: F2 catalog existing (something to distribute); F4.3 props-table work shares the morfo-introspection substrate.
10. Barcode — deferred symbologies + GS1 profiles — 2026-07-29
Origin: the Barcode component (shipped 2026-07-29; design record +
dispositions in
eidos/components/barcode/README.md
§Gaps). v1 covers the Retail + Logistics set — Code 128 (A/B/C auto), EAN-13/8,
UPC-A/E, Code 39, ITF/ITF-14 — from their published ISO standards, in the
zero-dependency $libs/barcode encoder.
Shipped the same day, out of this registry: the ISBN input profile. An ISBN
barcode IS an EAN-13 (ISO 2108, Bookland 978/979), so it entered as a profile
over ean13 — separators tolerated in the numeric symbologies, ISBN-10 read and
converted after validating its own base-11 check digit — and explicitly NOT as a
ninth symbology value, which would make data-symbology claim a standard that
does not exist. The doctrine is recorded in the component README §ISBN.
Four dispositions stay diferir, each a self-contained follow-up over the same encoder:
- Codabar · MSI · Pharmacode — the niche tail JsBarcode still ships. Pure table work: each is a symbology module + its vectors.
- GS1-128 (FNC1 + Application Identifier parsing) — a profile of Code 128, not a new encoder: the FNC1 code value plus an AI grammar so the HRI can print the bracketed form. The natural pair for a logistics-facing app, and the same shape the ISBN profile already established.
- EAN-2 / EAN-5 add-ons + the upper
ISBN 978-…line — the periodical / book supplements and the book-cover convention; they extend the EAN geometry (a second text band, a composed add-on symbol) rather than replacing it. Hard constraint recorded with them: we do not hyphenate. The hyphen positions are not computable from the number — they need the International ISBN Agency's prefix-range tables, versioned data with an expiry date, which is a maintenance clock a zero-dependency library must not swallow. The hyphenated string comes from the consumer's catalogue. - 2D — DataMatrix · PDF417 · Aztec — explicitly NOT this component: each is a
whole different encoder (own Reed–Solomon, matrix layout). They belong beside
QrCodeas their own components, sharing the display-primitive shape.
Deps: none — the encoder's BarcodeResult (module lattice + guards + HRI
groups in module space) already generalises; the 2D entries would instead reuse
the $libs/qr shape.
11. Background — parallax, the scrim scale, and the pack seam — 2026-08-18
Origin: the Background component (design record, measurements and
dispositions in
eidos/components/background/README.md;
plan and signed decisions in
process/PLAN-background.md). It ships as the
HOST of a surface's background layers — a child that pins itself behind its
parent's content, with the parent adopted by a foundation rule rather than
wrapped. Patterns, gradients, scrims, images and video are in; so is the WCAG
2.2.2 pause control.
Scope of this registry entry — what the component deliberately does NOT own yet, each a self-contained follow-up:
- Parallax and the scroll seam —
animation-timeline: view()with aScrollProgressfallback,attach='fixed', the pointerdepthaxis. The infrastructure landed with the component ($adom'sScrollProgressandprefersReducedData); the layers that consume it did not. It is the one follow-up with a mechanism already chosen and measured against browser support, so it is also the one most likely to move first. - The scrim
strengthscale is not ordered by weight — measured:subtleveils MORE thanoverlay, andoverlayandmutedresolve to the same number. The names come from the semantic opacity scale, which says how opaque an ELEMENT is; borrowing them for a VEIL imports an ordering that means something else. Renaming or renumbering is an API change, not a fix in passing. - A bright photograph is illegible at every scrim weight — white copy over
the heaviest veil measures below AA. Either the scale gains a step above
--opacity-subtle, or the doctrine becomes "the answer is a graded scrim", stated once and demonstrated. - A shared
<video>state port — the framework has a load-state contract for<img>(ImageProviderinsoma/layers) and none for<video>:ScrollFrameslistens by hand and so doesBackground.Video. Two consumers is the threshold this codebase uses to extract a layer. - The
Ambientpack honouring the pause — the canon publishes the context (paused,reduced,seen); the pack reads it. A task OF the pack, because the dependency runs pack → framework and never back.
Deps: the parallax entry needs no new service — the two $adom ports it
consumes already shipped. The scrim entries are the author's call on the token
vocabulary, so they block on a decision, not on code.
12. El contrato de cascada del velo de estado — 2026-08-20
Origen: el eje theme-reach (F2-A, sesión del 2026-08-20). Al tokenizar
componente a componente aparecieron, medidas una a una, seis incidencias que
son la misma: nadie fijó nunca cómo compone el velo de archetypes.css con
las reglas de una receta. No se tocan dentro del eje de theming — mueven píxel
y son decisión aparte — pero cada una está medida y localizada.
La raíz: archetypes.css pinta el velo neutro como
background-image: linear-gradient(var(--state-hover), var(--state-hover)), y
lo hace con DOS pesos distintos según el arquetipo — :where(...) (0,0,0) para
trigger, y especificidad PLENA (0,5,0) para item / option, «INTENTIONAL
cross-component accent», dice su comentario. Una receta que declare fondo sobre
el mismo nodo entra en una relación que nunca se escribió.
Las seis, con su medición
-
El atajo
background:cancela el velo — 131 declaraciones en 45 componentes. El atajo fijabackground-image: none; sobre un arquetipo velado por:where()eso lo mata para siempre, no sólo en hover. Medido conscripts/__statelayer-analysis.ts, que comprueba contra el morfo compilado que el atajo cae sobre el nodo VELADO y no sobre cualquiera. Por clase de regla: 66 BASE (mecánicas: pasar abackground-colorno mueve el reposo), 32 ACENTO (selected/checked/open: §38 pidebackground-colorpara que el velo COMPONGA encima), 23 HOVER (la migración de la firma 3) y 10 OTRA (disabled, dragging, variantes). Cero valores con gradiente, así que el único riesgo técnico del paso a longhand no existe en este catálogo. El píxel que se mueve: 32 nodos velados que hoy no reaccionan al ratón pasarían a hacerlo. Esto explica la adopción 21/135 que midió la auditoría del 2026-07-01: el velo estaba escrito y estructuralmente derrotado. -
La prop
hoverabledetableno suprime nada. El velo deitemes incondicional y a (0,5,0), así que una fila se ilumina al pasar el ratón aunquehoverableesté apagada — verificado con hover real, y ocurre desde antes de tocar la receta. O el arquetipo decide y la prop sobra, o la fila no es unitempara el sistema. Decisión de MORFO. Precisión medida 2026-08-21 (revisión adversarial,elementsFromPoint): entableel arquetipoitemestá declarado en la fila Y EN LA CELDA (morfo/components/table.ts:132y:155), y el nodo que recibe el velo bajo el puntero es la CELDA — la decisión de morfo debe contemplar los dos portadores, no sólo la fila. -
En
tree-gridel empate lo decide el ORDEN DE CARGA. El velo deitemy el hover de la receta pesan EXACTAMENTE lo mismo (0,5,0), así que gana la hoja que cargue después — y en el dev server varía entre recargas: seis corridas de la misma configuración dieron cuatro «sin velo» y dos «con velo». Un empate resuelto por orden es la misma fragilidad que el canon ya documenta para las bandas dez-index. -
En
tree-gridla banda mata el hover. La regla destripedpesa (0,8,0) — siete atributos más el:nth-of-type(la cifra (0,7,0) que se anotó primero contaba uno de menos; recontada 2026-08-21) — contra los (0,5,0) del hover y usa el atajo: en las filas pares no ocurre NADA al pasar el ratón. El hover del componente es inconsistente entre filas. -
Un token de receta no puede ganar al arquetipo. La regla de
itemfija tambiéncoloren el estado seleccionado, a (0,4,0) contra los (0,3,0) de la receta (la cifra (0,5,0) anotada primero era la de la variante:hoverde la misma lista; recontada 2026-08-21): untable.selected-row-fgno podía mover nada nunca. Se retiró con su declaración muerta (un token que no mueve nada es un token que miente). Mientras el arquetipo pinte tinta, ningún componente puede tematizar la suya en ese estado. -
scroll-area.thumb-bg-hoverno tiene velo al que migrar. Su thumb llevaarchetype: 'thumb', quearchetypes.cssno vela, así que la firma 3 no puede ejecutarse ahí sin cambiar el arquetipo — decisión de MORFO. Es el único knob de los seis de la cola que sigue sin resolver. -
En
grid-listel hover pinta DOS VECES sobre el MISMO nodo (medido 2026-08-21 al tokenizarlo). A diferencia detable/tree-grid—donde el velo cae en la celda y el plano en la fila, nodos distintos— aquí el plano de la receta (background:atajo, (0,3,0) →background-color) y el velo del arquetipo ((0,5,0) →background-image) caen ambos en[data-grid-list-row]: el atajo fijabackground-image: nonepero pierde, así que se ven los dos. Por eso el commit degrid-listno acuñóhover-row-bg: sería un token que no puede ganar (mismo criterio quelistbox.highlighted). Y como entable, fila Y celda llevanarchetype: 'item'(morfo/components/grid-list.ts:118y:141), así que bajo el puntero se apilan velo de celda + velo de fila + plano — tres capas. El par de portadores del punto 2 aplica igual aquí. -
En
tree-viewel velo DERRAMA sobre el subárbol entero (medido y capturado 2026-08-21 al tokenizarlo).archetype: 'item'está declarado en el<li>branch (morfo/components/tree-view.ts:103), que contiene elbranch-controlY elbranch-contentcon todos los descendientes — así que al pasar el ratón por la fila de una carpeta abierta se tiñe la carpeta entera: el nodo velado mide 336 px (la rama completa) frente a los 36 px de la fila que el usuario señala. Un branch anidado acumula además el velo de sus ancestros, así que sus filas se ven más oscuras que las hermanas. Es la tercera variante de la misma familia:tablelo tiene en fila Y celda (punto 2),grid-listen fila Y celda sobre el MISMO nodo (punto 7), y aquí en un ANCESTRO del nodo señalado. Mover el arquetipo del<li>al control es morfo, y mueve píxel en toda la demo.Ojo al tokenizar la familia: en
tree-viewelbranch-controlno lleva archetype, así que ahí el plano de la receta es la única pintura y suhover-row-bgSÍ es un token legítimo — al revés que engrid-list. La misma regla CSS cubre los dos nodos. -
El plano de profundidad
overlayimpone TIPOGRAFÍA, no sólo elevación — medido con CDP el 2026-08-21 al tokenizarlink-preview.[data-depth='overlay'](generated/base.css:7088) declarafont-family: var(--style-label-font-family)yline-height: var(--leading-ui)junto al fondo, el borde y la sombra. Un componente que estampe el plano para su elevación —veinte lo hacen— hereda además esa tipografía, y su propia declaración empata a (0,1,0) y pierde por orden de carga (la hoja generada va después del chunk de la receta). Enlink-previewel parfont-family/line-heightde la receta NO pinta: el plano impone 1.25 donde ella pide--leading-normal(1.5). Cuarta ocurrencia de la clase «empate resuelto por orden de carga» (§12.3, y las dos del calendario y detree-grid).⚠ El par se deja DECLARADO a propósito. Se retiró primero —daba 0 diffs, porque no pintaba— y se RESTAURÓ el 2026-08-22 al revisar la sesión: retirarlo no es neutral, fija el empate a favor del plano y con él el 1.25, que es justo la decisión que este punto tiene pendiente. Un empate por orden de carga se registra, no se resuelve dentro de un commit de tokenización. Los dos tokens quedan adjudicados en el ledger de R-5.4 con esa razón, y la medición del orden se hizo en DEV (estable en cinco recargas); en un bundle de producción está sin verificar.
Lo que hay que decidir: si un PLANO DE PROFUNDIDAD tiene competencia sobre la tipografía o sólo sobre la superficie. Si sólo sobre la superficie, esas dos líneas salen del plano y cada componente recupera las suyas; si el plano manda, entonces ningún overlay del catálogo debe declarar familia ni interlineado y hay que barrer los que lo intentan. Mueve píxel en los veinte en cuanto se toque.
Qué habría que decidir
En una frase: quién manda cuando el arquetipo y la receta pintan el mismo
eje. Las tres piezas concretas: si el velo de item baja a :where() como
el de trigger (y entonces la receta manda, y hoverable vuelve a significar
algo); si las recetas pasan a longhand en bloque (66 BASE + 32 ACENTO, mecánico
y sin gradientes de por medio); y si un arquetipo puede fijar color sobre un
componente que quiere tematizarlo.
Deps: ninguna de código. Es decisión del autor sobre píxel, y el
instrumental para medirla ya existe (__statelayer-analysis.ts +
__theming-probe.ts + __tg-rowhover.ts).
13. Huecos de instrumento y de demo del eje theme-reach — 2026-08-20
Origen: la misma sesión. Cosas que hicieron que una medición mintiera o no existiera. Se anotan porque el eje entero se apoya en esas mediciones.
El centinela da falsos negativos sobre una propiedad transicionada.CAUSA ENCONTRADA Y ARREGLADA 2026-08-21 (revisión adversarial): no era la transición. El paso «abrir lo que se pueda abrir» hacía CLIC en[data-command-input], y un clic de ratón sobre un input de texto SÍ casa:focus-visible— la regla de foco (0,2,0) se quedaba conborder-colory el token de reposo leía muerto. El guard (theming-sentinel.ts) ahora haceblur()tras abrir. La revisión midió la tasa completa del instrumento viejo: 22 falsos negativos de 26 «no effect» en F2-A, con tres causas (el clic-foco, escribir sólo en[data-{c}]cuando las partes cuelgan de{c}-root, y no leer::before/::after). Las tres, arregladas.El centinela no veERA FALSO, y ARREGLADO 2026-08-21 (al tokenizar::placeholder.textarea):getComputedStyle(el, '::placeholder')SÍ devuelve el valor — medido, el color centinela volvió tal cual. La afirmación anterior nunca se comprobó y le costó acommand.input-placeholder-fguna entrada de ledger «verificada a mano» que ha salido STALE y se ha borrado. El pseudo entra ahora en el snapshot del guard, junto a::before/::after.- El centinela no ve lo que pinta un componente COMPUESTO. Los tokens que
media-player reenvía al Slider (
--media-player-track→--slider-track-bg) pintan en nodos[data-slider-*], fuera del espacio de atributos del componente medido. Sigue vivo; adjudicado por token en el ledger de R-5.4. La sonda no pasa el ratón por unARREGLADO 2026-08-21:<tr>.trentra en el filtro de hover de la sonda, y el guard R-5.4 añade un pase de hover propio para los tokenshover-*(así adjudicarontable.hover-row-bgygradient-picker.hover-preset-border).- La sonda medía una demo aún CARGANDO. El auto load-more de
feedmantiene[data-busy]~3 s y su firmacommit-settleanimabox-shadowsobre el nodo medido — los «5 diffs entre dos corridas del mismo código» del handoff eran esto, no una animación sin localizar. ARREGLADO 2026-08-21: sonda y guard esperan a que[data-busy]caiga; dos corridas del mismo código dan 0 diffs y 0 nodos ausentes (6.264 valores). - Una sonda sobre una demo que no monta la parte compara CERO valores y
pasa. Ocurrió con el skin de audio:
/uix/components/audio-playerda 404 porque la demo es la demedia-playercon el chipmedia, y la sonda cargaba en modo vídeo. Resuelto conscripts/__probe-audio-skin.ts, pero la clase de fallo es general: una parte condicional necesita que la sonda la monte. Afecta al menos a captions, buffering y paneles portalados de media-player, al estado vacío y la barra de carga decommand, y al overview dedate-range-picker. date-range-pickerno exponekind='month'/'year'en su demo (norma N-6), así que la mitad de su API no se ve ni se mide.- Un componente COMPUESTO puede tener su cromo entero muerto sin que nada
avise.
gradient-pickerre-declaraba altura, padding, tipografía, color, fondo, borde, radio, hover y foco de su trigger, y NADA de ello pintaba:popover.cssgana con[data-popover-trigger]:not([data-archetype='field-trigger'])(0,2,0 contra 0,1,0). Lo cazó el centinela —36 de 45 tokens sin efecto— y retirarlo dio 0 diffs. EL GUARD EXISTE desde 2026-08-21: R-5.4 (npm run theming:sentinel -- <c> <url>,scripts/theming-sentinel.ts+ ledgertheming-sentinel-exceptions.ts, doctrina en recipe-contract §4). Nació con la segunda ocurrencia de la clase: elitem-gapdecarouselcontra un estilo INLINE de soma, invisible para todo análisis estático. Los ocho componentes de F2-A pasan con su adjudicación escrita. --gp-current-gradientes un nombre ABREVIADO en SOMA. Lo estampagradient-picker-provider.svelte.ts(no el wrapper de eidos, como decía la ficha), y viola theming §6 r5. No es knob de tema —es un canal de valor— pero el nombre es deuda; renombrarlo toca soma, otra capa.- Hooks por CLASE en una receta de eidos.
gradient-pickerusa.gradient-picker-trigger-swatchy.gradient-picker-trigger-labelen vez dedata-*;eidos-lintlos cuenta (class-hooks: 2) y el centinela no los ve, porque filtra nodos por atributo. - El foco del trigger de
gradient-pickeres opaco (--color-primary-solid) mientras el resto del catálogo lleva la mezcla suave del sistema (--focus-ring-color, 52 %). Quedó sin tocar porque alinearlo mueve píxel — y de hecho quedó sin token, porque la regla entera está muerta bajo popover. El censo PENALIZA consumir una capa compartida correctamente.RESUELTO PARA LA FAMILIA CALENDAR 2026-08-21 y abierto para el resto: el censo tiene ahoraLAYER_VOCABULARY(mismo precedente que D-TH.2-b), así que el--calendar-*que leenrange-calendar/month-grid/year-gridcuenta comosystem.list-surfaceNO está registrado a propósito: sus consumidores puentean por PRIVADOS (--_listbox-item-*: var(--list-item-*)), otra forma, y activarlo mueve diez componentes de golpe — sigue siendo decisión aparte. Y aparece un techo NUEVO debajo: con el vocabulario de la capa fuera del denominador, a los tres sólo les quedan los forwards de paleta THM-2 (--_x-palette-*, que el censo cuenta comoprivateen TODO el catálogo) y literales de layout, así que siguen leyendo 0 %. Reclasificar el forward de paleta es la misma pregunta, un piso más abajo, y afecta a todos.- El censo PENALIZA consumir una capa compartida correctamente.
listboxadopta los cinco ejes de ritmo quelist-surfaceposee — que es exactamente lo que la doctrina de capas manda — y el censo los cuenta comoprivate, así que su alcance queda en 68 % en vez de ~86 %. Es el mismo defecto de medición que D-TH.2-b arregló para los primitivos tipográficos (--style-*pasó a contar comosystem): la capa ES la superficie de tema de ese eje. Afecta a los consumidores delist-surface(select · combobox · command · listbox · los menús) y deviewport-placement(affix · fab · menu-dial). Extenderlo es una decisión, no una corrección al paso. listboxpinta elhighlightedDOS veces: la receta pone un--color-surface-overlayplano y encima el arquetipoitemañade su velo del 8 % — medido. El plano es el duplicado que §38 deprecó; se dejó sin token para no bendecirlo, pero retirarlo mueve píxel.- La demo del bloque F2-A ya enseña los tokens (
TokensPanel), pero los ~155 componentes sin contrato siguen sin superficie donde verlos hasta que se tokenicen.
Lo que la capa calendar-surface añadió (2026-08-21):
- ⚠ El font-size de los selectores month/year del calendario es una MONEDA AL
AIRE.
[data-calendar-month-select][data-button](0,2,0) empata con[data-popover-trigger]:not([data-archetype='field-trigger'])(0,2,0) — la MISMA regla depopover.cssque dejó muerto el cromo entero del trigger degradient-picker— y gana la hoja que cargue después: dos corridas del mismo código dan 16px o 14px en los mismos nodos (24 diffs de instrumento, medidos con la sonda dos veces). El pin del calendario existe justamente para que caja, etiqueta y chevron lean UNA talla, así que perderlo es visible. Arreglarlo es subir el peso del pin de forma determinista — decisión, porque fija el píxel en un lado. Segunda ocurrencia de la clase §12.3 (empate por orden de carga) y segunda víctima de la misma regla de popover. - Los tres consumidores de la capa siguen leyendo 0 % de alcance, ahora por los forwards de paleta THM-2 — ver el punto del censo más arriba.
Lo que la revisión adversarial de F2-A añadió (2026-08-21):
PENDIENTE DE FIRMA — elFIRMADO Y EJECUTADO 2026-08-21:stripeddetablees inerte con RowDetail.:nth-child(even of [data-table-row])cuenta filas de DATOS y la paridad ya no cae en los<tr>de detalle (la demo no bandeaba NADA; ahora las filas 2 y 4 tiñen — 12 diffs medidos, 2 filas × 6 estados, y nada más se movió en 13.195 valores). El selector conserva el (0,6,0) del viejo para que el hover siga ganando su empate (no se importa el defecto §12.4 de tree-grid) y un:where(:not([data-selected]))de peso cero deja el acento seleccionado al mando — verificado los tres. La excepción del ledger se retiró;striped-row-bgadjudica VIVO (42/45).- El gap entre diapositivas de
carouseles un canal de valor de soma. Soma lo estampa INLINE desde la propgap(necesita el número para elflex-basis), así que ninguna declaración de receta puede ganarle — el tríoitem-gap*se retiró con 0 diffs. Si algún día debe ser temable, es soma quien tendría que leer un token; decisión de otra capa. - Dos
header-zllevan el literal'2'(table,tree-grid) mientras los otros catorce*-zdel catálogo leen la escalera--z-index-*. Alinearlos es decidir qué peldaño significa «cabecera pegajosa dentro de un scroller». - El spinner de
media-playerconserva720msa pelo mientras el spinner idéntico defeedganó dos tokens públicos de duración (sentinel-spinner-duration/-reduced-duration). El mismo trato o una anotaciónfunctional:, pero no las dos varas.
Lo que grid-list añadió (2026-08-21):
- ⚠ PENDIENTE DE FIRMA — la selección de fila de
grid-listnunca ha pintado. La receta seleccionaba[data-grid-list-row][data-selected], un atributo que ni el morfo declara ni soma estampa: soma estampadata-state='selected'+aria-selected='true'. La regla no casaba jamás, así que su acento (--_grid-list-palette-element) no llegó nunca al píxel y una fila seleccionada muestra sólo el velo NEUTRO del arquetipo — visualmente indistinguible de una fila con el ratón encima (capturado). La declaración muerta y su forward huérfano se retiraron en el commit de tokenización con 0 diffs de computed, que es la prueba de que estaban muertos. Repararlo mueve píxel (re-apuntar a[data-state='selected']haría aparecer un acento que el componente nunca tuvo), y choca de frente con §12.5: el arquetipoitemfija tambiéncolora (0,4,0) sobre el estado seleccionado. Es la misma decisión del velo, no una corrección al paso. El acento del SelectionCheckbox (_palette-solid) sí vive y se conservó. SHARED_LAYERSdeclara agrid-listconsumidor demenu-indicator, y no lo es.theming-census.ts:432lo lista, perolib/menu-indicator.cssno tiene un solo selector que apunte a grid-list (sus 134 líneas targetean[data-context-menu-*]/[data-dropdown-menu-*]), la receta no importa la capa y soma no renderiza indicator: el checkbox espeja ese visual pintándolo por su cuenta. La entrada hace que el informe generado escriba «Consume la capa compartidamenu-indicator» en la §4 de la ficha, que es falso y dirige mal a quien la lea. Revisar las seis entradas de esa lista contra los selectores reales de cada capa (listbox,menubarynavigation-menuestán en la misma entrada y tampoco aparecen en el fichero).
Lo que textarea añadió (2026-08-21):
-
⚠ El borde de foco de
textareano se ve al hacer CLIC — sólo con Tab. Medido: la regla de hover[data-textarea-input]:hover:not([data-disabled]):not([data-readonly])pesa (0,4,0) y la de foco[data-textarea][data-focused] [data-textarea-input](0,3,0), así que con foco y puntero a la vez gana el hover — y hacer clic deja el puntero encima por definición. Verificado con las dos reglas apuntando a colores distintos: sale el del hover. Debajo,[data-invalid]pesa (0,2,0): un campo inválido pierde su borde rojo tanto al enfocarlo como al pasar el ratón. El anillo de foco del SISTEMA (outline) sí se ve siempre, así que no es un fallo de accesibilidad, pero el orden de las tres reglas deborder-colorestá invertido respecto a lo que significan. Arreglarlo mueve píxel: no entra en el commit de tokenización. -
El guard mide con el PUNTERO encima del componente.ARREGLADO 2026-08-21:reopen()corre antes de CADA token y hace clic en[data-{c}-input]cuando el componente no tiene partecontent— elblur()que añadió la revisión adversarial quita el foco pero no el puntero, así que:hoverseguía casando durante toda la corrida y toda regla de hover pisaba a sus vecinas de reposo. Es la mitad que aquel arreglo dejó atrás. Entextareacostaba tres falsos negativos (input-border,focus-input-border,invalid-input-border); conpage.mouse.move(0, 0)tras cada blur,input-borderrevive solo. Corridos los diez componentes con ledger: ningún otro cambia, un único STALE (command.input-placeholder-fg, por el arreglo del::placeholder). -
Hueco de demo:
card-groupno monta su encabezado ESTÁTICO. La página renderiza el título sólo en su forma de disclosure ([data-button]), así que la mitad[data-static]del cromo del título —tinta, familia, tamaño, interlineado y el hueco inferior— no se ve ni se mide: cinco tokens adjudicados por eso. Misma clase quedate-range-pickerconkind='month'|'year'(norma N-6). -
El test del SUELO del censo que F0 pedía no existe.ESCRITO 2026-08-22 —src/uix/eidos/theming-reach-floor.test.ts: alcance global ≥ 56 %, literales ≤ 564, globales ≤ 1.156 y ≥ 14 componentes al 100 %, con las dos muta-pruebas que el plan exige (un literal inyectado y cinco públicos cambiados por primitivos, cada uno con su rojo). Medido de paso: los techos son el detector FINO y el porcentaje el grueso — cinco knobs de ~4.700 no mueven un porcentaje redondeado, pero sí un techo. La entrada original decía: El plan (§5, F0.d) encarga «un test que fije el suelo: alcance global ≥ el de hoy y literales ≤ 614 — el número SUBE o el test falla», con su muta-prueba. No haytheming-census.test.tsni equivalente bajosrc/uix/eidos/, así que hoy nada impide que el alcance BAJE entre sesiones: sólo se detectaría mirando la cifra a mano. Con el censo ya endurecido (ver arriba) es el momento de escribirlo. -
⚠
knob.arc-widthsignificaba DOS cosas con DOS defaults distintos — medido 2026-08-22 al tokenizarlo: el inset de la cara del dial (--space-3) y la separación superior del puntero (--space-2). Un tema que lo escribiera movía las tres declaraciones a la vez, pero SIN tema cada una valía algo distinto. Separado enarc-width+pointer-insetcon 0 diffs. Queda como aviso de clase: un mismo nombre con defaults distintos por sitio es una colisión aunque el override funcione — es la definición que el propio generador usa para marcar⚠, y conviene barrer el catálogo buscando más. -
⚠
mockupno tiene ruta de demo (/uix/components/mockupda 404), como le pasaba apicker-shell. Con 29 knobs al 38 % es el mayor de los que quedan sin tocar de su tramo, y sin demo no hay sonda ni guard: habría que medirlo prestado o darle página. -
⚠ R-5.1 y R-5.2 no están implementadas en
component-audit(0 disparos hoy; sólo existe R-5.3 de gramática y R-5.4 de alcance). El plan las da por hechas en F0 «enwarn» y F3 las quiere enerror, así que F3 no puede cerrarse sin escribirlas primero. Cuando lleguen, los componentes con alcance parcial necesitan su marcaR-5.x exception:en el README — ya puestas en los 16 de F2-B que no llegan al 100 %. -
Cuatro componentes salen NEEDS-WORK en
component:audity son PREEXISTENTES (ninguno tiene un commit de F2-B en su código):badge,gradient-picker,mockupymotion. Anotado para que nadie los atribuya al eje de theming al leer el informe. -
⚠ Un worktree de comparación NO hidrata si su
node_moduleses una junction fuera del root — Vite responde 403 a@fs/…y SvelteKit no arranca, así que la página SSR se ve pero ningún componente monta. Mordió en la revisión adversarial de F2-B, que comparó contra una base muerta sin que nada lo delatara. Se arregla conserver: { fs: { allow: [...] } }en elvite.config.tsdel worktree, y la regla que generaliza es comprobar que un componente monta ANTES de comparar. (Memoriaworktree-devserver-fsallow; vuelve a morder cada vez.) -
⚠
--radius-xsNO EXISTE, y una receta lo referenciaba. La escala de radios del sistema essm | md | lg | xl | xxl(+none,full,default), sinxs.text-focusescribíaborder-radius: var(--radius-xs, 3px): pintaba el fallback, así que nadie lo notó — hasta que tokenizarlo sin fallback dejó la variable vacía, la declaración inválida y las cuatro esquinas cuadradas (48 diffs, medidos 2026-08-22). Corregido a3pxverbatim, el valor que de verdad pintaba.Lo que esto dice del catálogo: un
var(--fantasma, fallback)es una referencia muerta que funciona por accidente, y el guard G2 que el plan menciona («referencias-fantasma contra el contrato derivado») o no existe o no cubre los fallbacks. Barrer el catálogo buscandovar(--…, …)cuyo primer nombre no esté en el contrato derivado es un pase mecánico que se paga solo. -
⚠
filterybackdrop-filterfaltaban en la lista de propiedades del guard — arreglado 2026-08-22.text-focus.glow-colorvive dentro de undrop-shadow()y leía «no effect» estando vivo;KNOB_PROPSdel censo sí las tenía, así que el censo contaba el knob y el guard no podía verlo. Verificados los 30 componentes con ledger tras el cambio: cero regresiones, cero STALE. -
⚠ El guard CONGELA las transiciones, y eso esconde los tokens cuyo trabajo ES la transición. La congelación existe por una razón buena y documentada (una propiedad transicionada devuelve su valor INICIAL justo tras escribir el token, y un token vivo parecía muerto), pero deja ciego al guard ante
text-circular.char-transition, que guarda una transición completa. Medido sin congelar: alcanza. Si aparecen más tokens de este tipo —transición, animación, easing— merecen un pase propio sin congelación, como el que ya tiene el hover. -
⚠ El valor centinela GRANDE no prueba nada contra una propiedad capada.
skin-media-playercalcula su ancho comomin(100%, base × scale): el1234pxdel guard se capa al contenedor y los siete tokens de tamaño leen «no effect» aunque estén vivos. Con valores PEQUEÑOS (111/122/133px y escalas 0.11/0.22/0.33) alcanzan exactamente. Es primo del caso ya registrado «un valor centinela igual al real lee como no efecto» (9999pxcontra un--radius-fullque ya era 9999px): el centinela tiene que ser imposible EN LA DIRECCIÓN en que la propiedad puede moverse. Un guard que probara ambos extremos —uno muy grande y uno muy pequeño— cazaría las dos familias.
Lo que la REVISIÓN ADVERSARIAL de F2-B destapó (2026-08-22):
-
drag-drop.preview-zera un token que MENTÍA, y lo escondió una adjudicación hecha sobre un nodo sintético. El ledger decía «forzado → alcanza (100 → 4321)», pero ese nodo lo había creado yo con JS. El preview REAL —el que soma monta durante un arrastre de verdad— llevaz-index: 9999INLINE (drag-drop-provider.svelte.ts:663,671), así que ninguna cascada puede ganarle: el token resolvía a 4321 y el nodo computaba 9999. Retirado con 0 diffs, que es la prueba. Misma clase que elitem-gap*decarouselque retiró la revisión de F2-A: canal de valor de soma, no superficie de tema.La lección para el protocolo: una adjudicación «forzado → alcanza» vale lo que valga el nodo que se forzó. Si lo creaste tú, no lleva lo que soma le pone. Verificado el resto de la misma clase:
clipboard(indicador real tras un clic real) ypicker-shell(pie real) coinciden con lo adjudicado y no llevan inline;skeleton-linelleva inline pero deinline-size, no del eje tokenizado. -
El orden de carga NO es el mismo en dev que en producción. Medido con
npm run build+ preview y CDP: en dev la receta delink-previewva ANTES del planooverlay, en producción queda EN MEDIO de sus dos reglas. El ganador coincide por casualidad —hay dos reglas del plano y una queda siempre al final—, pero la fragilidad de §12.9 es real y ahora está medida en los dos entornos, no supuesta. -
⚠ Un worktree de comparación NO hidrata si su
node_moduleses una junction fuera del root: Vite responde 403 a@fs/…y SvelteKit no arranca, así que la página SSR se ve pero ningún componente monta. La primera pasada de esta revisión comparó contra una base muerta y dio «77 casos, 1 diferencia» sin que eso significara nada. Se arregla conserver: { fs: { allow: [...] } }en elvite.config.tsdel worktree. Es la memoriaworktree-devserver-fsallow, y vuelve a morder cada vez.
Lo que la cola pequeña añadió (2026-08-22):
-
⚠ El censo no distingue «0 % por deuda» de «0 % POR NATURALEZA», y ya son cinco componentes. Medidos uno a uno el 2026-08-22,
aspect-ratio,text-blur,cascade,motionydate-pickertienen alcance 0 % y ninguno tiene contrato que escribir: el knob deaspect-ratioestá PRESTADO debox(la ficha ya lo marca ⤴) porque es una faceta suya, no un componente; los detext-blurson el1pxde la técnica sr-only; los decascadeymotionson elopacitydel gate antiparpadeo, mecánica del canal de motion cuyo valor tematizable vive enEidosConfig.motion; y los dedate-pickerson la correcciónmax-contentque impide que el pie del popover desborde, con un único valor correcto. Sus veredictos §5 quedan escritos.Mientras el censo los cuente igual que a un componente con deuda real, la cifra global miente por abajo y el gate de F3 («censo 100 %») es inalcanzable por construcción. Lo que hace falta es una clase más —
structural, junto asystem— o una marca en la ficha que los saque del denominador, comoLAYER_VOCABULARYhizo con la familia calendar. Es la misma pregunta que ya está abierta para las capas compartidas, un piso más abajo.
Lo que picker-shell añadió (2026-08-21):
- ⚠
picker-shellno tiene ruta de demo propia (/uix/components/picker-shellda 404) y sus partes se llamandata-picker-header/-body/-footeren vez dedata-picker-shell-{parte}— genéricas a propósito, dice su receta, «so every picker gets the same visual contract for free». La suma de las dos cosas deja al instrumental ciego: la sonda genérica devolvió 0 nodos, que es el antipatrón de comparar cero valores y pasar. Se tokenizó con sonda dirigida (abre el popover deldate-picker, captura los seis nodos reales en las cuatrodata-picker-size: 576 valores, 0 diffs) y el guard R-5.4 ganó unCOMPONENT_OVERRIDESpara poder abrirlo y filtrarlo — acotado a este componente, con los otros catorce verificados sin cambio. Lo de fondo sigue abierto: una demo propia, y decidir si el precio de los nombres genéricos (un contrato visual común para todos los pickers) vale la ceguera del instrumental. - El eje de talla de
picker-shellesdata-picker-sizesobre un ancestro PORTALADO, así que el TSC no puede emitir su cascada (scope: 'size:xs'generaría[data-picker-shell][data-size='xs'], que no casa nunca). Es el único componente del eje cuyas coordenadas por talla quedan como públicos planos que la receta consume desde sus propios selectores. Si algún día el TSC gana un eje de scope configurable, éste es su caso de prueba.
Lo que code-block añadió (2026-08-21):
-
⚠ La demo de
code-blockNO ENSEÑA elcode-block: el harness del sitio de documentación le pinta el<pre>por encima.[data-uix-docs] pre(web/routes/uix/uix.css:801) pesa (0,1,1) y gana a[data-code-block-pre](0,1,0) — y[data-uix-docs]es el shell de TODAS las páginas, así que la regla alcanza cualquier demo. Medidas siete propiedades pisadas:padding(16px 20px del harness contra los 12px de la receta),border(1px sólido contra ninguno),border-radius(8px contra 0),background-color(gris contra transparente),font-family,font-size(13px contra 14px) yline-height(18.2px contra 20.3px). El fondo, el borde y el radio que se ven en esa página son del sitio, no del componente. Es la clase «la demo puede tapar el componente» (precedenteproof-of-human), pero aquí son siete propiedades y le toca justo al componente cuya razón de ser es mostrar código. Por esopre-paddinglee muerto en el guard y está adjudicado con esa medición.Acotar la regla del harness es tocar el CSS del SITIO, que sirve a las ~162 páginas y a los bloques de código de las fichas: no entra en el commit de un componente. Decisión aparte.
No es un caso aislado — la misma familia de reglas pisa también a
code(medido 2026-08-21 al tokenizarlo, con CDP):[data-uix-docs] codedeclarafont-family,font-size,paddingyborder-radius, y gana (0,1,1) contra la regla BASE del componente (0,1,0). Sus reglas de VARIANTE (0,2,0) sí ganan, así que el efecto es selectivo y por eso sólo suradiuslee muerto — 3px del sitio donde el componente pide 4px. Cualquier decisión sobre la regla del harness cubre los dos componentes. -
⚠ El guard de claves duplicadas del handoff INSPECCIONA EL VACÍO. El comando documentado en
CONTINUE-theming.md(«Trampas que costaron un commit cada una») esgrep -oE "^ '?[a-z0-9-]+'?: \{" src/uix/eidos/lib/recipes/base.ts | …, ygrep -Eno interpretacomo tabulador — lo lee como unatliteral, así que el patrón no casa NADA: 0 coincidencias sobre 130 bloques reales. El guard salía siempre «vacío = OK» sin haber mirado nada, que es la definición del antipatrón que el propio proyecto tiene registrado. Con un tab real en el patrón encuentra los 130 (comprobado: cero duplicados en el árbol, así que no hay daño que reparar). El handoff queda corregido con la versión que sí mira.
Lo que text-gradient destapó (2026-08-22) — PENDIENTE DE FIRMA, apuntado
para resolver después (autor, 2026-08-22):
-
⚠ La familia text-effects ACUÑA tipografía propia contra el canon. Cinco claves del contrato:
text-focus.word-size(var(--font-size-4xl, 3rem)—4xles un paso que la escala canónicaxxs..xxxldelib/primitives/typography.tsNO tiene: idioma Tailwind del import de julio63b371d5c, pinta el fallback) yword-weight(900) ·text-circular.weight(900) yfont-size(1.5rem) ·text-gradient.font-weight(500, subida el 22 en97a6f03d7). Son opiniones del seed (react-bits) que el protocolo «valor verbatim» convirtió en contrato sin la pregunta previa: ¿un tratamiento de pintura sobre texto real tiene tipografía propia? La doctrina fijada en F2-B (regla 2, cinco precedentes:label,code,kbd,announce,code-block) dice que una receta CONSUME la capa tipográfica (--style-*) o HEREDA (link→inherit): un TextGradient dentro de unh1ES un h1.Son SIETE claves en CUATRO de los cinco componentes (medido 2026-08-22 al adjudicar
text-scramble): las cinco de arriba mástext-scramble.fonty.size, éstas aún sin declarar enbase.ts. Y la detext-scramblees la peor de todas porque además apunta fuera del canon:var(--font-mono, …), nombre que no existe —el canónico es--font-family-mono—, así que pintaui-monospacey el componente ignora la mono del tema (Azeret Mono). Medido en navegador.text-blures el único limpio de la familia: hereda.Resolución propuesta: retirar las cinco claves y heredar del contexto. Mueve defaults (TextFocus deja de imponer 3rem/900, TextCircular 1.5rem/900, TextGradient 500) → firma del autor. Con ello muere el fantasma
4xlsin tocar el módulo de tipografía, que es canon y está prohibido modificar (autor, 2026-08-22). Refuerza además el barrido mecánico que--radius-xsya pedía:var(--nombre, …)cuyo nombre no emite ningún generador. -
⚠ El barrido mecánico está HECHO: 9 referencias fantasma con fallback en el catálogo de componentes (2026-08-22). Es lo que pedían el hallazgo de
--radius-xsy el de--font-size-4xl, y ya no hay que suponer cuántas quedan. Método: recoger las 5.793 custom properties declaradas en TODOsrc/uix/eidos(fundación + capas + recetas +generated/) y buscar en las recetas losvar(--x, fallback)cuyo--xsea vocabulario de SISTEMA y no esté declarado — el sistema nunca lo escribe en runtime, así que un nombre ausente es una errata. (Losvar(--x)SIN fallback ya los guardarecipe-css-contract; el hueco eran justo los que llevan fallback, que «funcionan por accidente».)referencia inexistente dónde qué pinta en su lugar --color-content-tertiary×3float-panel.css:227·proof-of-human/clock.css:87·proof-of-human/rotate-align.css:114--color-content-secondary. Es el caso histórico querecipe-contract§1 cita como origen de la regla («shipped a month that way») y sigue vivo en tres recetas--style-{body,prose,label,caption}-font-variation-settings×4text.css:32,55,65,75normal— los estilos con nombre emiten 6 claves y ésta no está, así que el eje variable no llega nunca al primitivoText--font-family-sanspalabras.css:296sans-serifdel sistema. El canónico es--font-family-primary(palabras es track WIP)--font-monotext-scramble.css:11ui-monospace. El canónico es--font-family-monoNinguna se arregla aquí: dos son tipografía y esperan la firma 1.bis, y las otras dos familias son decisión de diseño (¿existe un slot
tertiary? ¿el eje variable debe emitirse por estilo?). El barrido, en cambio, debería ser un guard: la lógica es la misma que la del test de fantasmas sin fallback que ya existe, con el filtro de vocabulario de sistema encima.
Lo que chart destapó (2026-08-22):
-
El censo penaliza consumir la capa tipográfica— RESUELTO el mismo día, y la nota estaba equivocada. Escribí que las doce declaraciones dechartque pasaban avar(--style-caption|label-font-size)seguían contandoglobalporque el censo sólo exime--style-*a los seis primitivos. El censo medía BIEN: lo que faltaba era la costura. PLAN §2-A pide que todo knob lea--{c}-{slot}, y leer el canon a pelo desde el CSS deja a un tema sin poder retocar ESE componente sin mover el rol en toda la app. Con seis claves--chart-{caption,label}-*cuyo VALOR esvar(--style-{rol}-*),globalcayó a cero y el alcance a 89 %. La lección: cuando la métrica y la doctrina discrepan, sospecha de la lectura antes que del instrumento. El molde canónico ya estaba escrito en la vertebración tipográfica (--accordion-trigger-font-family: var(--style-label-font-family)), y D-TH.2 aclara que sólo los PRIMITIVOS quedan fuera.listbox(68 %, ritmo de fila delist-surface) es la misma pregunta un piso más arriba: una capa compartida no se lee a pelo, se cose — pendiente de revisar con este molde. -
⚠ Un READOUT de dato no tiene estilo con nombre en el canon. El número del gauge (
--font-size-xl), el total de un funnel, el valor de unStat: no son encabezados (h2está en ese tamaño pero significa otra cosa) ni texto de cuerpo. Su costura toma el bundle de talla, que es correcto pero no dice nada del ROL. Si aparecen más, la respuesta es un estilo con nombre nuevo en el módulo de tipografía — firma del autor, y el módulo no se toca sin ella. -
⚠ La clase
privatedel censo ES, en su mayor parte, el PUENTE DE PALETA THM-2 (medido 2026-08-22). De las 687 declaraciones que leen un privado en el catálogo, 130 leen un--_{c}-palette-*, y son las que dominan la columnaprivatede los componentes con paleta:tag-group14/14,stepper11/11,radio-group10,checkbox8,file-upload8,calendar/editable/los tres grids 7 cada uno.No es deuda y no debe acuñarse: el puente lo alimenta la capa compartida por instancia desde
[data-color], y un público encima (--{c}-selected-bg: var(--_{c}-palette-surface)) dejaría que un tema lo fijara y matara en silencio elcolor=de cada instancia — la misma clase de daño que «declarar un público de facto mata una escala» (knob). El componente ya expone sus nueve roles × variante como claves públicas: ésa es su superficie de color.Consecuencia para la métrica: el techo de un componente con paleta no es el 100 %, y su cifra no compara con la de uno sin ella — el denominador incluye un puente que por diseño no se acuña. Tres ya adjudicados con esta razón (
tag-group65 %,stepper79 %,timeline73 %); los demás la heredan cuando les toque. Si algún día se quiere una cifra comparable, la respuesta es una clasebridgeen el censo, no acuñar los tokens. -
⚠ La capa
list-surfaceno es alcanzable por un tema — MEDIDO 2026-08-22. Ni por su propio nombre ni por el puente que acuñan sus consumidores. Con el menú abierto y la fila a 36 px: un tema que escribe--list-item-height: 1234pxen:rootno mueve nada, y uno que escribe--dropdown-menu-item-heighttampoco. Es cascada: la capa declara sobre[data-list-surface][data-size]y el puente sobre[data-{c}-content]; las dos ganan a:root.Por eso
listboxno se cosió como sus hermanos: copiar el puente habría subido la cifra sin dar alcance (un token que miente). La salida es la decalendar-surface: que el vocabulario de la capa viva en una entrada de receta y el gancho sólo lo RESUELVA por talla. Toca los diez consumidores de golpe → eje propio.Y deja un aviso sobre el instrumento: el centinela da por vivos los tokens de esta forma porque escribe inline en el nodo, que gana a la regla del gancho. Para un token que un tema sólo puede escribir en
:root, «mueve un computed» no equivale a «un tema lo alcanza». -
⚠ La FIRMA DE SALIDA está escrita a mano tres veces (medido 2026-08-22).
data-last-action× 3 intents × 4 lados tiñe el borde durante la animación de cierre, y la misma matriz vive enpopover(22 reglas),drawer(22) ydialog(9), leyendo--color-{fulfill,threat,neutral}-element. Y ya ha derivado: dialog tiñe sóloborder-top-colormientras popover y drawer usan el shorthand con--border-width-medium, así que en dos de tres el GROSOR también cambia al salir.La tabla morfo→eidos la sitúa como mapeo TRANSVERSAL («
events[].prewrite→ tinting the exit anim by cause»), misma fila que los arquetipos y los selectores[data-event=…]: es el canal visual de sema materializado por eidos, como la capa de estado vive enarchetypes.css. Coserlo por componente sería triplicar un vocabulario que debería vivir UNA vez; la salida es una regla transversal sobre el arquetipocontent, con los colores de intent (que son canon) — 53 knobs asystemy un tema que retoca la firma una sola vez. Lleva una firma dentro: ¿la salida tiñe sólo el color o también el grosor? -
⚠ El test de huérfanos cuenta un COMENTARIO como consumo.
--popover-bgno lo leía ninguna regla; el único sitio del corpus que lo nombraba era un comentario depopover-arrow.svelte. El testdoes not leave declared public recipe variables orphanedbusca el nombre como TEXTO en los ficheros del componente, así que un comentario lo mantiene «vivo» para siempre. Retirado el token y corregido el comentario (2026-08-22); el test ganaría con despojar comentarios antes de buscar, igual que hace el censo. -
⚠
color-pickerarrastra 23 tokens sin adjudicar, ANTERIORES al eje (2026-08-22). Su costura los dejó en 23 —eran 24 antes—, pero el bloque viejo necesita su propio pase de medición: las muestras (7 claves), la rueda de tono (hue-0..360+saturation-floor), el ancho del panel por talla, el cuentagotas y las dos de transición. Dos causas ya descartadas: no es que el panel no abra (abre y sobrevive al blur, al parqueo del ratón y al forzado dedata-size) ni que el guard no vea sus nodos (ya se le dieron las dos familias que le faltaban). -
Un falso negativo del centinela sin causa— RESUELTO en la revisión adversarial del bloque (2026-08-22).float-panel.resize-grip-fgmovía aislado y leía muerto en una corrida completa. La causa: la pasada de HOVER deja el puntero sobre el último nodo que tocó, y una regla:hovergana a la de reposo con la que comparte nodo — el asa re-apunta su color al acento al pasar el ratón. Así que CUALQUIER token de reposo probado después de un tokenhover-*podía leer muerto. Es la misma clase que el envenenamiento por clic-foco que F2-A arregló, una pasada más tarde. Arreglado aparcando el puntero tras la pasada de hover; el propio guard delató entonces DOS excepciones STALE (float-panel.resize-grip-fgymedia-player.track), retiradas. Confirma la regla de F2-A: un falso negativo siempre tiene causa. -
⚠⚠ EL VELO DEL
drawerNO PINTA — defecto real, medido y visto (2026-08-22). Su regla esbackground: color-mix(in srgb, var(--drawer-overlay-bg) var(--drawer-overlay-opacity), transparent), y soma escribe--drawer-overlay-opacity: 1INLINE en el nodo del velo (es su progreso de arrastre, un número sin unidad).color-mixexige un PORCENTAJE ahí: con1la función entera es inválida y el fondo cae atransparent. Computado en el velo abierto:rgba(0, 0, 0, 0); escribiendo80%en el propio nodo pinta. En la captura se ve: el fondo detrás del cajón está DESENFOCADO pero no atenuado — elbackdrop-filterfunciona y el tinte no existe.No se arregla aquí porque mueve píxel y toca soma (§7.8). El arreglo puede ser de un lado o del otro: que soma escriba un porcentaje, o que la receta no use ese slot como porcentaje. Y la lección general: un valor que soma escribe inline y una receta consume tiene que compartir UNIDAD, o el fallo es silencioso.
-
⚠ Las tallas del
drawerson canal de valor en el eje que manda: soma escribewidth(oheight, según el lado) INLINE en el contenido, así quecontent-width-{sm,md,lg}no puede ganar desde:rootpara un cajón izquierda/derecha —el eje cruzado sí alcanza (height-smforzado: 260px → 976px)—. Misma clase que elitem-gapde carousel y elpreview-zde drag-drop, que se RETIRARON; aquí no, porque el token sigue siendo la fuente del valor que soma resuelve. Decidir si se retira o si soma lo lee es firma. -
⚠ El eje
sizedelcolor-pickerMUERE EN EL PORTAL — dos veces (2026-08-23). El panel viaja en un portal, fuera de[data-color-picker], así que ningún privado declarado en la raíz llega hasta él. Consecuencias medidas:--_color-picker-content-widthse declara TRES veces y no lo consume NADIE. La anchura del panel la fija--_popover-content-width-override: calc(var(--color-picker-content-width-md) + 2rem), cableada al pasomd. Un pickersmy unolgabren el MISMO panel de 344 px, ycontent-width-sm/content-width-lgson dos públicos que no mueven nada (elmdsí alcanza, por el override).- El tamaño de las muestras dentro del panel está clavado a
md: la regla del contenido redeclara--_color-picker-swatch-size: var(--color-picker-swatch-size-md)sin variantes por talla. Fuera del portal los tres pasos alcanzan (20 / 24 / 28 px → 1234 px); dentro, sólo elmd.
El arreglo honesto es una escala por talla EN EL ÁMBITO DEL CONTENIDO (que sí lleva su
data-size), no en la raíz. Mueve píxel parasmylg, así que es firma. Misma familia que la capalist-surfaceinalcanzable desde:root: el portal es una frontera de cascada, y un privado declarado en la raíz no la cruza. -
Punto ciego genérico del centinela: los tokens que SON la transición. El guard congela las transiciones antes de medir —el arreglo que hizo medibles
radio-cardsy compañía— y con ellotransition-duration/transition-easeno pueden moverse jamás. Medidos sin congelar encolor-pickeralcanzan (0.12s → 11.5s). Cualquier componente con esos dos públicos los reportará muertos: o el guard los mide en una pasada sin congelar, o cada uno paga su adjudicación escrita. -
⚠⚠ §12.9 tiene MECANISMO: el plano
overlayno «impone» tipografía donde el componente calla — GANA donde el componente habla (medido enmenubar, 2026-08-23). Acuñadaspanel-font-familyypanel-line-heightsobre una declaración que la receta YA tenía, el centinela las dio muertas:[data-depth='overlay']declara las dos con la MISMA especificidad (0,1,0) y más tarde en la cascada. Del mismo bloque,coloryfont-size—que el plano no declara— sí alcanzan. Consecuencia para la cola: mientras §12.9 no se firme, un token defont-familyoline-heighten cualquier superficieoverlayMIENTE, y son veinte componentes. Retiradas del contrato de menubar y devueltas a su fuente literal, con la razón escrita en el CSS. -
⚠ El hover de
menubarmata la capa del sistema — y es del eje navigation-menu, no de éste (medido 2026-08-23).[data-menubar-trigger]:hoverpintabackground: var(--color-surface-overlay): en hover elbackground-imagecomputanonecon--state-hovervivo. Dos razones, las dos ya escritas ennavigation-menu.csscuando retiró esta MISMA regla: el shorthand reseteabackground-image, y la regla del arquetipo va en:where(), especificidad CERO. El eje navigation-menu tienemenubaren su cola por esto; aquí no se acuña unhover-trigger-bgporque fosilizaría la invención (doctrina: el hover es del SISTEMA). -
⚠ Un token de
::placeholderlee MUERTO sobre un input sinplaceholder(medido enfield, 2026-08-23). Sin el atributo no existe la caja del pseudo-elemento, ygetComputedStyle(nd, '::placeholder')devuelve entonces el estilo del ELEMENTO — que no sigue al token. Poniendo un placeholder, alcanza (oklch(0.61 0 0)→rgb(1,2,3)). Es genérico: cualquier componente con un token de placeholder lo sufrirá si su demo arranca con el input vacío. El guard podría sembrar un placeholder antes de esa medida. -
⚠ DOS recetas escriben
:global()en un CSS PLANO, y el navegador descarta la regla entera (medido 2026-08-23):onion-menu.css(.onion-menu-icon :global(svg), que cree pintar el glifo al 70 % de su caja — computa 14px, el tamaño propio del Icon) ytimeline.css:331([data-timeline-marker] :global(svg)).:global()es un envoltorio de SVELTE: fuera de un<style>de componente el selector es inválido.image-picker.cssya documenta la trampa en su propio comentario, así que la lección estaba escrita y se repitió. Arreglar el selector EMPIEZA A PINTAR lo que la regla dice, así que mueve píxel en los dos y es firma. Mientras tanto, los literales se quedan con el defecto escrito encima y sin token (uno acuñado se retiró porque mentía). -
⚠ Congelar las transiciones no basta cuando el nodo está ANIMADO (medido en
onion-menu, 2026-08-23).rim-glow-opacityleía 0,482956 —el valor que la animación tenía en ese instante— y el token parecía muerto; con la animación congelada también, computa 0,45 y sigue al token. El guard no puede congelar animaciones en general (Presence necesita la suya para montar), así que es una medida A MANO cuando el nodo pulsa. -
⚠⚠ LA CASCADA DE PALETA ANULA LOS TONOS DEL COMPONENTE — 419 claves públicas del catálogo (medido 2026-08-23 en
button,badgeycallout).El forward emite, por componente, un bloque por tono (
[data-button][data-color='risk'] { --button-palette-solid: var(--button-risk-solid) }) y, al final, uno genérico ([data-button][data-color] { --button-palette-solid: var(--palette-solid, var(--button-primary-solid)) }). Los dos tienen especificidad (0,2,0), así que gana el último: cualquier instancia condata-colorresuelve por el--palette-*GLOBAL, que[data-color='{tono}']llena desde--color-{tono}-*. Consecuencia medida sobredata-color='risk':--button-risk-solidno mueve nada ni en:rootni en el nodo, mientras--palette-solidy--color-risk-soliden el nodo repintan. Las siete{c}-primary-*son el RESPALDO de la regla genérica y sólo actúan en una instancia SINdata-color.Tamaño: 49 recetas emiten el forward genérico y el contrato tiene 419 claves de tono (
{tono}-{track|element|border|solid|solid-hover|text|contrast}), repartidas entre ~13 componentes que además declaran reglas por tono. Son públicas y no alcanzan: R-5.4 las llamaría mentiras, y no son deuda de nadie en particular — es el orden de emisión.Arreglo propuesto (firma): emitir el bloque genérico ANTES de los de tono. Entonces una instancia con tono lee
--{c}-{tono}-*—cuyo valor por defecto ESvar(--color-{tono}-*), así que no movería un píxel— y las escalas y el custom siguen cayendo por el genérico. Contrapartida: cambia el contrato de la jaula del color — un ancestro que inyecte--palette-*dejaría de pisar un tono SEMÁNTICO en esos componentes (seguiría pisando los personalizados). Por eso no se toca aquí.Mientras tanto el guard lo adjudica con un PATRÓN (
SENTINEL_PATTERN_EXCEPTIONS), para que la razón se escriba una vez. -
⚠
combobox.content-font-familyes el TERCER token de tipografía que el planooverlayanula (medido 2026-08-23). Su panel llevadata-depth='overlay', así que la clave no mueve nada, igual que las demenubar,dropdown-menuycontext-menu—las tres retiradas ya—. No se toca aquí porque no es el componente en curso: cuando §12.9 se firme, o se retira o empieza a pintar, y la decisión es la MISMA para las cuatro. -
⚠ Ya son CUATRO los componentes canónicos sin ruta de demo:
picker-shell,mockup,surfaceyaudio-player(404 los cuatro; los dos últimos medidos el 2026-08-23). El de audio se mide dentro de/uix/components/media-player, tras el chipmedia: audio, porque es la piel de ese chasis. Lo de abajo vale igual para él: sin ruta no hay sonda ni guard, y el guard miente en verde-rojo (/uix/components/surface= 404, medido 2026-08-23). Sin ruta no hay sonda ni guard, y el guard miente en verde-rojo: reportaba 0/25 sobre una página vacía, incluido unradiusque está vivo. El parche es el mismo que se le puso a picker-shell —medirlo donde SÍ se renderiza (urlsen el override)—, pero la solución de fondo es que un componente del canon tenga su demo. -
⚠⚠ EL PLANO
overlayANULA LAS TRES VARIANTES DEtooltip(medido 2026-08-23). No es sólo la tipografía de §12.9:[data-depth='overlay']declarabackground,borderybox-shadowcon la misma especificidad que[data-tooltip-content]y más tarde en la cascada, así que gana. Medido con el panel abierto:--tooltip-bg/-border/-shadowno mueven nada,--depth-overlay-surface/-border/-shadowrepintan.Consecuencia visual:
solid,outlineyghostcomputan el MISMO fondo, el MISMO borde y la MISMA sombra.outlinees indistinguible desolid;ghostsólo aporta unbackdrop-filterinvisible tras una superficie opaca. La receta tiene una máquina de variantes completa (privados por variante) que el plano neutraliza entera.Las cinco claves afectadas quedan ADJUDICADAS, no retiradas: son la fuente de esa máquina y borrarlas dejaría los privados inválidos sin arreglar el fondo. La decisión es la de §12.9 —qué manda, el plano o la receta— y aquí ya no es sólo tipografía: es la superficie.
-
El censo pierde el eje
sizeDE PREMIO por aplicar bien el protocolo (medido 2026-08-23 ennavigation-menu).hasSizese decide grepeandodata-sizeen el CSS de la receta (theming-census.ts:287), y el paso §7.4 del protocolo ORDENA retirar los bloques[data-size]del CSS cuando la cascada la emite el TSC. Resultado: todo componente que haga lo correcto aparece con «ejesize: no» en su ficha y sinyen la tabla del censo. Ya le pasa asidebarynav-tree, y anavigation-menudesde hoy — los tres con su cascada por talla viva engenerated/base.css. La columna es informativa (no entra en el ratio), pero el conteo «con ejesize: N» del encabezado cuenta al revés de lo que quiere medir. El arreglo es leer el eje del CONTRATO (una clave-{sm,md,lg}o unscope: 'size:{k}'), no del CSS. -
La sonda y el centinela no ven las partes que el CONSUMIDOR compone (medido 2026-08-23 en
navigation-menu). Las filas de su mega-menú son<a>desnudos que la receta estiliza por descendencia ([data-navigation-menu-content] :is(a, …)): sin atributo del componente, el filtrodata-{c}-*veía 10 nodos y ninguno era el panel ni una fila — 9 de 19 knobs fuera del diff. Es la MISMA clase queproseyonion-menu, pero por composición del consumidor, no por HTML crudo ni por hooks de clase. Resuelto para este componente conEXTRA_NODES/extraNodes(y el pasoopende la sonda, que no los honraba). Queda la pregunta general: una receta que estiliza nodos sin atributo propio no es medible por defecto, y nada avisa — el gate sale verde. -
Un panel de HOVER se cierra por el propio guard.
navigation-menuabre conpointerenterypointerleaveprograma el cierre; el guard abre con clic y luego aparca el puntero en (0,0), que es exactamente el gesto que lo cierra. Producía no-determinismo (dos corridas del mismo código discrepando en un token) y las dos caras del error a la vez: falso negativo en los tokens de la fila y falso positivo encontent-link-padding-inline, que leía «vivo» por ensanchar el panel que sí se medía. Resuelto conopenBy: 'hover'. La regla general: antes de creer un «no effect», comprobar que la superficie seguía abierta al medirlo.
Deps: ninguna. Son mejoras del instrumental del eje, ejecutables cuando estorben.