# Plan de ataque — fixes de la auditoría clean-room (2026-07-10)
docs(audit): formal programme close-out + the clean-room continuation handoff
- docs/audit/components/_cierre.md — the programme closure: final gates
(matrix 134/0/0, contracts 37/37, vocab 0, baseline 57), the full package
journey with hashes, the canonized method (dossier template, evidence-only
exceptions, no-cascada, machine valves), the findings that outlive the
programme, and what moved to new owners (demo frame, active tracks,
follow-ups).
- docs/process/continue-cleanroom-fixes-2026-07.md — the LIVE turnkey for
the next sessions: the clean-room severity table as the queue, the state
reconciliation (SOM-5 P1 already closed post-snapshot; SOM-3 partial), the
proposed P1 order (machine-extending THM-1/MOR-4 first), the WIDENED
verification protocol (134/0/0 and 37/37 must HOLD), exclusions and the
side queue.
- The old audit-fixes handoff is sealed as historical with a redirect header.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
**Handoff turnkey + plan aprobable. Creado: 2026-07-11.** Rama `alpha-0.1-sec-dom` .
docs(audit): formal programme close-out + the clean-room continuation handoff
- docs/audit/components/_cierre.md — the programme closure: final gates
(matrix 134/0/0, contracts 37/37, vocab 0, baseline 57), the full package
journey with hashes, the canonized method (dossier template, evidence-only
exceptions, no-cascada, machine valves), the findings that outlive the
programme, and what moved to new owners (demo frame, active tracks,
follow-ups).
- docs/process/continue-cleanroom-fixes-2026-07.md — the LIVE turnkey for
the next sessions: the clean-room severity table as the queue, the state
reconciliation (SOM-5 P1 already closed post-snapshot; SOM-3 partial), the
proposed P1 order (machine-extending THM-1/MOR-4 first), the WIDENED
verification protocol (134/0/0 and 37/37 must HOLD), exclusions and the
side queue.
- The old audit-fixes handoff is sealed as historical with a redirect header.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
## ⏭️ ESTADO AL CIERRE 2026-07-12 + PRÓXIMO ARRANQUE
**La tabla §9 de la clean-room está prácticamente entera.** Fases F0– F4-C +
SEM-4 pusheadas antes de esta sesión (`4addfd7d`·`29e621b1`·`b55ca6ee`·
`082b6623` ·`0e75890f`). Esta sesión (2026-07-12) commiteó (rama
`alpha-0.1-sec-dom` , **push al cierre** ):
| Commit | Qué |
| --- | --- |
| `05f0031c` | **media-player Batch-4** (SOM-4 cerrado, renderProps 35→0) + **THM-2** rollout: 14 superficies a la capa compartida + vocab alpha (`surface`/`surface-hover`/`hover`) + 4 props ampliadas a `ComponentColor` |
| `cc7e9a16` | **THM-2 avatar ROOT** → paleta a la capa compartida + 33 escalas |
| `0a5aa387` | **THM-2 CERRADO** — `Avatar.Badge` fuera de la paleta-por-escala **por diseño** (alineado con el ecosistema) |
| `d9c3d03f` ·`75fa9400`·`34d1d46b` | **THM-4 CERRADO** — 5 hotspots adjudicados (4 = 0 deuda; stepper = 1 redundancia de API consolidada). Columna `unused` = informativa, sin regla |
**Método clave de esta sesión** (corrección del usuario): verificar SIEMPRE con
el **componente real + control real del demo** , nunca DOM fabricado
(`feedback_verify_with_real_components_not_synthetic_dom`).
**Queda de la §9 / cola lateral (arranque próxima sesión):**
- **F4-D · GAP-\*** (§7 del audit + §F4-D abajo): **ADJUDICADO con referencias +
ejecutado en su mayoría 07-12** (bloque «F4-D — adjudicación profesional»
abajo). ✅ selection/caret/cursor + scrollbar · ✅ **assets-variables** (fuentes
variable + API de ejes, `7d4f9500` ) · **KEEP** layer-4 / depth-z / container-
queries (ejes reservados deliberados; corregido 1 comentario falso de depth-z)
· **4 límites deliberados registrados** (LIM-SND/ICON/TOUCH/RECIPES). **Queda:**
scrim (⏳ decisión de diseño) · gap-3 auto-métricas (opcional).
test(motion): pin firma-vs-stagger cascade precedence (F4-D motion decision)
Workflow analysis (state map + 4 dispositions + adversarial stress) resolved the
direction dispute: the stagger/coordinated preset winning the (0,3,0) tie over
the generic commit-affirm firma is the DOCTRINE-CORRECT outcome (RFC B.2 — the
coordinated owns the visual axis, the generic firma is silenced). The earlier
"flip to firma" reading was backwards; that flip contradicts B.2 and its
channels:[] safety net is dormant; @layer sinks as a front-line fix (a semantic
ownership question answered with a global cascade tool + a reduced-motion
!important-inversion a11y risk).
No shipped component shows a defect (dropdown select feedback is sound+haptic
only; the item is fading out at select). The only real issue: the correct
outcome rides on an ACCIDENTAL source-order tiebreak (the explicit neutralization
was retired), undocumented + untested. Execute the proportionate fix:
- Regression test (motion.test.ts) pinning the (0,3,0)==(0,3,0) tie + the
signatures-before-presets emission order + the absence of a (0,4,0)
[data-event^='commit-select'] signature — a HAZARD MONITOR, so a generator
reorder or a widened signature fails loudly, not silently.
- Load-bearing comment at the render-css emission order.
- Doctrine note in motion.md: a known-fragile accident, not a contract; the
structural endpoint ("una firma por evento") + @layer are deferred/tracked.
Zero behavior change; base.css unchanged. Tests 28/28; npm run check src/ = 9
baseline, 0 new.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
- **cascada-vs-firmas** (motion, cola lateral): ✅ **RESUELTO 07-12** — la
dirección estaba zanjada por doctrina (B.2: gana la entrada, la firma se
silencia); ejecutado **B** (test de regresión + comentario load-bearing + nota
en `motion.md` ) como monitor de hazard. Endpoint «una firma por evento» /
`@layer` diferido y trackeado. Detalle en la tabla F4-D abajo.
- **THM-2 · rol-directo restantes** (cola, opcional): las superficies semánticas
ya colapsaron sus roles a la capa; ampliar su `color` a escalas NO tiene
sentido semántico (decisión tomada). No queda rollout pendiente salvo que se
quiera ampliar alguna más.
- Cola menor registrada al final (§Cola lateral): SOM-3 (~6 setTimeout), class-hooks
preexistentes, Task #31 pickers, etc.
Detalle por iniciativa: bloques «✅ EJECUTADA/ADJUDICADO» abajo (buscar el ID).
> **Fuente**: la tabla §9 de
docs(audit): formal programme close-out + the clean-room continuation handoff
- docs/audit/components/_cierre.md — the programme closure: final gates
(matrix 134/0/0, contracts 37/37, vocab 0, baseline 57), the full package
journey with hashes, the canonized method (dossier template, evidence-only
exceptions, no-cascada, machine valves), the findings that outlive the
programme, and what moved to new owners (demo frame, active tracks,
follow-ups).
- docs/process/continue-cleanroom-fixes-2026-07.md — the LIVE turnkey for
the next sessions: the clean-room severity table as the queue, the state
reconciliation (SOM-5 P1 already closed post-snapshot; SOM-3 partial), the
proposed P1 order (machine-extending THM-1/MOR-4 first), the WIDENED
verification protocol (134/0/0 and 37/37 must HOLD), exclusions and the
side queue.
- The old audit-fixes handoff is sealed as historical with a redirect header.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
> [`docs/audit-new-10-july/audit-active-uix.md`](../audit-new-10-july/audit-active-uix.md)
> — leerla ENTERA antes de tocar nada. El programa anterior está COMPLETO
> (cierre en [`docs/audit/components/_cierre.md`](../audit/components/_cierre.md)).
> Este plan cubre **todas** las IDs de la §9 (P1/P2/P3 + GAP-\*); nada queda
> sin fase asignada — la tabla de cobertura del final es el checklist.
docs(audit): formal programme close-out + the clean-room continuation handoff
- docs/audit/components/_cierre.md — the programme closure: final gates
(matrix 134/0/0, contracts 37/37, vocab 0, baseline 57), the full package
journey with hashes, the canonized method (dossier template, evidence-only
exceptions, no-cascada, machine valves), the findings that outlive the
programme, and what moved to new owners (demo frame, active tracks,
follow-ups).
- docs/process/continue-cleanroom-fixes-2026-07.md — the LIVE turnkey for
the next sessions: the clean-room severity table as the queue, the state
reconciliation (SOM-5 P1 already closed post-snapshot; SOM-3 partial), the
proposed P1 order (machine-extending THM-1/MOR-4 first), the WIDENED
verification protocol (134/0/0 and 37/37 must HOLD), exclusions and the
side queue.
- The old audit-fixes handoff is sealed as historical with a redirect header.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
## Reconciliación de estado (la clean-room es snapshot del 2026-07-10)
- **SOM-5 (P1) ✅ CERRADO** post-snapshot: field `commit-submit` al Enter
(`33f346ed`) + float-panel kb-gestos por handle-events (`bdd17f04`).
- **SOM-3 (P2) ◐**: float-panel ya en `uix.timers` (`bdd17f04`);
media-provider es PUERTO Svelte-agnóstico exento por diseño. Quedan ~6
`setTimeout` (image-provider, safe-polygon, dismissal, datetime/announcer,
tags-input, announce/global).
- Badge R-4.3 (única violación R-4.x viva en su §5.3) ✅ cerrada en el censo
R-1.x (`ee9786bc`). Su matriz (102/32/0) es previa: hoy **134/0/0** .
fix(cleanroom): F2+F3+F4-C+SEM-4s1 — lote mecánico, censos con guard, corpus documental y el close polimórfico de los pickers VIVO
F2 — lote mecánico (13 ítems):
- DEP-2 ogl eliminado (0 imports) · DEP-1 clsx inlineado como toClassString
propio + suite de contrato (props.test.ts; soma.md §12 cerrado).
- THM-7: los 5 selectores manuales de sema.md reescritos con semaSelector
(los ejemplos [data-toast-root] apuntaban a un part INEXISTENTE — la
deriva que el builder previene, demostrada en el propio doc).
- MOR-1 escape isomorfo + validación de attr-names en semaSelector + 9
tests (selectors.test.ts, matches() real con comillas/corchetes) ·
MOR-2 partMarkerAttr = única fuente compilador↔builder + test de paridad ·
MOR-3 _resetCompileCache borrado (0 usos).
- SOM-2 keydown continue en match sin handler + keyboardFixtureMorfo ·
SOM-1 no-await de handlers (censo async = 0; contrato V1 cumplido) + pin.
- SEM-2 trigger pre-attacha catch con logger (void trigger sin unhandled
rejection; throw intacto para awaiters) + pin · SEM-3 fallback muerto de
applyDominance → skip defensivo + timer tope de awaitExpression cancelado ·
SEC-1 adjudicado YA implementado (assertCssVariableValue desde 2026-05-11)
+ pin del path de VALOR.
- accordion → outline (§32; su outline:none dejaba CERO anillo en HCM) —
verificado en vivo · THM-6 radius-full 9999px · EID-4 recuentos 33.
F3 — censos con guard:
- SOM-3 cerrado: announcer + image-provider migrados a scheduler-preferred
(consumidores cableados: date/time-field vía soma.uix.timers; avatar/image
vía eidos.timers — verificado en vivo); guard de timers ENSANCHADO de
soma/components a TODO soma y pasado a EVIDENCIA (setTimeout exige
.schedule( en el fichero — layers/ y datetime/ escapaban del ámbito viejo).
- THM-5: R-4.7 nueva (válvula same-line /* important: <razón> */, escaneo
comment-blanked) + las 15 declaraciones anotadas con su razón + canon
recipe-contract §3/§4.
- SOM-4 adjudicado: el censo/guard YA existían (49 pins); knob/mask-field/
timeline pinneados (overrides documentados en call-site); media-player
Batch-4 (35 hits, cero renderProps) = único batch restante, registrado.
- THM-4 doctrinado en eidos.md §unused (comportamiento/composición =
legítimo; deuda = eje visual sin consumidor; hotspots por lotes).
F4-C — corpus documental (decisiones de usuario aplicadas):
- DOC-3: los 15 enlaces muertos resueltos (repoint a la edición FINAL
trackeada / des-link históricos) · docs:check I6-links WARN→ERROR.
- DOC-1: tabla «Build contract» MIGRADA a component-guide con estados
modernizados (A3–A5 → LIVE + guards de hoy); banners reapuntados; citas
de CANON/sema.md historificadas; lápida-redirect en el §13 del fósil.
- DOC-4: hold chain → holds.ts · FAQ event:* SUPERSEDED por signatures ·
gradient añadido a los DOS capstones (sextet real) · nota de paleta de
demo-authoring corregida (universalPaletteDecls + decisión THM-2 =
mecanismo universal como sucesor del tracker borrado).
- DOC-5/6: recuentos anti-frágiles datados · §4.11 dup → §4.12 · Known gaps
historificado · N-6/N-7 recuperadas de git (d68d2c45^) y canonizadas en
eidos.md §pickers · authoring E2 → canon/tsc.md · air-old des-linkado ·
EID-3 (placement) en la fila RTL · AUX-2 disabledDom documentado.
SEM-4 sesión 1 — el close polimórfico de los pickers, VIVO (D.11):
- Reconciliación: los morfos ya no declaran close (delegated al Popover,
de-dialoged 06-27); el agujero real era el cierre programático bypaseando
dismissWith → save/cancel/select eran perceptualmente SILENCIOSOS.
- Fix: PickerShellHandle.setPopoverDismiss + closeWith(cause) en los 5
providers (14 sitios; select/commit → 'save' = commit.save+fulfill,
cancel → 'cancel' = emerge; fallback raw para headless) + UN inyector en
el eidos PickerShell root (norma N-8). Picker genérico fuera a propósito
(ya suena commit-set/cancel por diseño S9).
- Verificado en vivo (date-picker): Done → close·commit·fulfill·active ·
Cancel → close·emerge · cierre real.
Gates: matriz 141/141 (los 6 morfos nuevos de la pista de texto paralela
también PASS) · contracts 38/38 · eidos 314 · sema 178 · morfo 94 ·
docs:check 0/0 con I6 en error · baseline propio 57.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
## F2 ✅ EJECUTADA 2026-07-11 (lote mecánico completo)
> Gates: matriz **135/0/0** · contracts **38/38** · eidos **310/310** (+pin
> SEC-1) · sema 178 · morfo **94/94** (+9 selectors) · runtime 43 (+3 pins)
> · props 8/8 (suite nueva) · baseline **57** · vocab exit 0 · accordion
> verificado en vivo (outline con foco de teclado real, box-shadow none).
> Los 13 ítems: DEP-2 (ogl fuera) · DEP-1 (clsx → `toClassString` propio +
> suite; soma.md §12 cerrado) · THM-7 (los CINCO selectores manuales de
> sema.md al builder — y los ejemplos `[data-toast-root]` apuntaban a un
> part inexistente: la deriva que el builder previene, demostrada en el
> propio doc) · THM-6 · EID-4 × 2 · MOR-1 (escape isomorfo + validación de
> attr-names + 9 tests, incluida la prueba matches() real) · MOR-2
> (`partMarkerAttr` única fuente + test de paridad) · MOR-3 (no-op
> borrado, 0 usos) · SOM-2 (keydown `continue` + fixture) · SOM-1 (censo
> async = 0 → no-await, contrato V1 cumplido + pin) · SEM-3 (fallback
> muerto de applyDominance → skip defensivo; timer tope de awaitExpression
> cancelado al ganar las animaciones) · SEM-2 (trigger pre-attacha catch
> con logger → `void trigger` sin unhandled rejection, throw intacto para
> awaiters + pin) · accordion→outline (patrón tabs, tokens de componente).
>
> **Dos adjudicaciones de auditoría**:
> - **SEC-1 = YA IMPLEMENTADO desde 2026-05-11** (`assertCssVariableValue`
> rechaza `;{}` en valores — el auditor buscó escape y no vio el reject;
> ahora hay pin de test del path de VALOR).
> - **SEM-2(a) = el composition root YA inyectaba su logger al engine**
> (`active-uix.svelte.ts` `eventsOpts.logger ?? logger`) — los fallos de
> canal nunca fueron silenciosos en apps reales. Mi primer intento
> (fallback a console en el engine) lo TUMBÓ el guard de pureza de sema
> ("no console en producción") — el guard defendió la arquitectura
> contra el propio fix: revertido y documentada la garantía real en la
> opción `logger`.
## F3 ✅ EJECUTADA 2026-07-11 (censos con guard)
> Gates: matriz **135/0/0** (con R-4.7 viva) · contracts 38/38 · eidos
> **314/314** · attr-audit verde · baseline propio **57** (el árbol marca 58:
> +1 AJENO en `src/packs/ambient/ambient.svelte`, sesión paralela Scene).
>
> - **SOM-3 CERRADO**: censo real = cropper/tags-input/float-panel YA
> migrados; dismissal/safe-polygon/announce-global YA con forma sancionada
> (scheduler-preferred + fallback sin-timers). Migrados los 2 restantes:
> `datetime/announcer` (clear del live-region) e `image-provider` (delay de
> fallback) — consumidores cableados (date/time-field vía `soma.uix.timers`;
> avatar/image vía `eidos.timers`, verificado en vivo: 20 avatares, 0
> shells muertos). **Guard ENSANCHADO** de `soma/components` a TODO soma
> (layers/ y datetime/ escapaban) y pasado a EVIDENCIA en vez de allowlist:
> fichero con setTimeout debe contener también `.schedule(`.
> - **THM-5 CERRADO**: censo real = 15 declaraciones (no 17; tabs/select ya
> no tienen). **R-4.7 nueva** (error): `!important` exige válvula same-line
> `/* important: <razón> */` — escaneo comment-blanked como R-2.1. Las 15
> anotadas con su razón verdadera (inline de soma × 3 · fusión de radios × 8
> · opt-out de presencia · cursor del grip · kills de reduced-motion × 2).
> Válvula + regla asentadas en `docs/canon/recipe-contract.md` §3/§4.
> - **SOM-4 ADJUDICADO** (otra sobre-estimación de la clean-room: "sin censo
> ni guard" — el script `soma-attr-audit.ts` + el guard con **49
> componentes pinneados** ya existían): backlog real = knob(1)/
> mask-field(1)/timeline(2) — overrides de valor conscientes documentados
> en el call-site, PINNEADOS — y **media-player (35 hits / 10 parts, cero
> renderProps)** = el ÚNICO batch de migración restante ("Batch 4"),
> dimensionado y pendiente de programación (decisión de usuario).
> - **THM-4 DOCTRINADO**: criterio en `architecture/eidos.md` §unused —
> "declarado y jamás estilizado" NO es error (attrs de comportamiento,
> artefactos de composición: css-field→spin-field, collapsible headless,
> selectores cross-component), la deuda es el attr declarado PARA un eje
> visual que nadie consume → consumir o podar. Quema de hotspots
> (media-player 47 · stepper 30 · color-picker 29 · avatar 28 · trp 26) =
> lotes con dossier, registrados abajo.
## DECISIONES F4 TOMADAS (usuario, 2026-07-12)
1. **THM-2 = MECANISMO UNIVERSAL** : una cascada genérica `--palette-*` de 33
bloques (~3-4 KB total) que todas las recetas consumen con fallback
propio, en vez del rollout per-component (medido: **21.2 KB button /
20.7 KB toggle → × 15 ≈ +315 KB sobre 345 KB**). Sesión de diseño TSC
propia (migrar button/toggle al slot genérico + convención + guards).
2. **SEM-4 = AHORA, tras F4-C** (2 sesiones: close polimórfico × 5 pickers
patrón Dialog + triggers de los 6 menús/árboles con el caveat
branch-vs-item + flag `emission` D.2).
3. **DOC-1 hogares APROBADOS** : build-contract §13 → component-guide;
intent-assignment → CANON (ejecutado abajo).
4. **Orden: F4-C primero** (ejecutada abajo) → SEM-4 → resto.
## F4-C ✅ EJECUTADA 2026-07-12 (corpus documental)
> Gates: docs:check **0 err / 0 warn** con **I6-links subido a ERROR**
> (contramedida: nunca más 15 enlaces muertos tolerados) · matriz 135/0/0 ·
> vocab exit 0.
>
> - **DOC-3**: los 15 enlaces muertos, resueltos — repoint (CANON + docs/README
> → edición FINAL trackeada; el puntero HOMOGENEIZADO ya no existía) o
> des-link histórico (THEMING_AUDIT × 4, sema-findings, 7 rutas del
> MOTION_RFC que es doc-RETIRADA).
> - **DOC-2**: cerrado — binarios FINAL ya trackeados por `4addfd7d`; el
> único puntero vivo (CANON §Sources → v2_3.md) reapuntado a FINAL.pdf +
> book-map.
> - **DOC-1**: **tabla «Build contract» MIGRADA a component-guide.md** con
> estados modernizados (A3– A5 → LIVE con los guards de hoy: R-4.3, §32,
> size-bundle, §37…); banners de component-guide + eidos/components/README
> reapuntados; citas de CANON §3 y sema.md §expression historificadas
> ("THIS section is the canonical..."); lápida-redirect en el §13 del
> fósil.
> - **DOC-4**: cadena de hold de sema.md → holds.ts/`SEMA_HOLDS_BY_INTENT`
> (el campo SEMA_MAP eliminado 2026-07-06) · FAQ `event:*` de notes.md
> marcada SUPERSEDED por las motion signatures · `gradient` añadido a los
> DOS capstones (reference §10 + channels §5, ahora "the sextet" de
> verdad) · nota de paleta de demo-authoring corregida
> (universalPaletteDecls + decisión THM-2 como sucesor del tracker).
> - **DOC-5**: recuentos fósiles → forma anti-frágil con fecha (≈140
> componentes 2026-07; tabla de purge anotada como medición 2026-06;
> "25 soma"/"66 morfos" datados).
> - **DOC-6**: §4.11 duplicado → §4.12 (0 citas externas verificadas) ·
> "Known gaps" de Layout Batch 1 anotado HISTÓRICO/superseded ·
> **TODO(reconcile) × 2 RESUELTOS**: N-6/N-7 recuperadas de git
> (`d68d2c45^`) y canonizadas en eidos.md §pickers; tracker de paleta →
> sucesor F4-A · authoring.md E2 → canon/tsc.md · air-old des-linkado.
> - **EID-3**: excepción de *placement* físico asentada en la fila RTL de la
> tabla Build contract. **AUX-2**: `disabledDom` mixto documentado en la
> regla 2 de active-uix.md (writes no-op / reads-that-must-return THROW).
## SEM-4 · SESIÓN 1 ✅ EJECUTADA 2026-07-12 — pickers (D.11)
> **Hallazgo de reconciliación**: D.11 era snapshot de mayo — los morfos de
> los 5 pickers YA NO declaran `close` propio (la reconciliación
> de-dialoged de 2026-06 los pasó a `expression: 'delegated'`: su firma de
> apertura/cierre pertenece al POPOVER que componen). El agujero real HOY:
> los cierres programáticos (`commit()`, `cancel()`, auto-close al
> seleccionar) flipaban `open.current = false` a pelo, **bypaseando** el
> `dismissWith` del Popover → save/cancel/select eran perceptualmente
> SILENCIOSOS (solo sonaban Escape/outside/botón-close, los caminos propios
> del popover).
>
> **Fix (upgrade coherente con delegated)**: delegado de cierre por causa.
> - `PickerShellHandle` gana `setPopoverDismiss?` + tipos `PickerCloseCause`
> ('save'|'cancel'|'dismiss') / `PickerDismissDelegate` (bridge soma
> picker-shell).
> - **UN inyector** (norma N-8) en el eidos PickerShell root — monta dentro
> del subtree del Popover y entrega `popover.dismissWith` al provider vía
> el handle; `null` al desmontar.
> - Los 5 providers (date, date-range, time, time-range, color):
> `closeWith(cause)` con fallback raw (composiciones headless);
> 14 sitios migrados — select/commit → **'save'** (commit.save+fulfill,
> "seleccionar ES el compromiso"), cancel → **'cancel'** (emerge).
> - El Picker GENÉRICO queda FUERA a propósito: ya dispara su propia firma
> (`commit-set` fulfill / `commit-cancel`, diseño S9 "celebrate the new
> state") — añadir emerge encima doblaría señales (doctrina D.6 de
> repartir intensidad).
> - Comentario fósil de picker-shell-close.svelte (citaba `close-commit`
> inexistente) corregido.
>
> **Verificado en vivo** (date-picker; los 5 comparten inyector+patrón):
> Done → stamp `close · commit · fulfill · active` · Cancel → `close ·
> emerge · (sin intent) · active`, en el content del popover, con cierre
> real (data-state='closed'). Gates: matriz **141/141** (la sesión paralela
> sumó 6 morfos de texto — todos PASS) · suites pickers/popover verdes ·
> baseline propio 57 (+1 ajeno `text-blur.ts` de la sesión paralela).
## SEM-4 · SESIÓN 2 ✅ CERRADA POR VERIFICACIÓN 2026-07-12 — D.6 ya estaba vivo
> **D.6 era otro falso positivo por registro stale** (como SEC-1 y parte de
> SOM-4): los 6 providers YA emiten vía `runtime.trigger` — dropdown/context
> (`open`/`close`/`commit-select` × 2), menubar/nav-menu (`commit-select`),
> trees (`emerge-expand`/`collapse` + `commit-select`) — y el caveat
> branch-vs-item estaba resuelto de punta a punta (emisión con
> `fallbackTarget: branchEl/rowEl` + packs con `onBranch`/`onRow`).
> **Verificado en vivo**: `open · emerge · active` y `commit-select ·
> commit · affirm` en dropdown-menu. El registro D.6/D.11 de
> book-deviations quedó RECONCILIADO (notas RESUELTO con fecha y lección:
> el registro se actualiza en el mismo pase que el cableado).
>
> **D.2 (flag `emission`) — ADJUDICADO: DESCARTAR.** Su propósito era hacer
> auditable la dormancia de D.6, que ya no existe; un check estático de
> emisión sería frágil (nombres dinámicos `trigger(eventName)`) y el flag
> sería feature especulativa sin consumidor (Simplicity First). Si una
> dormancia real reaparece, la lección anti-stale de arriba es la
> contramedida barata.
>
> Con esto **SEM-4 COMPLETO** (sesión 1 = pickers `b55ca6ee`; sesión 2 =
> verificación + reconciliación del registro).
feat(theming): THM-2 — paleta universal por instancia (capa compartida + forward), 5/17 superficies
El mecanismo genérico ya existía (universalPaletteDecls auto-completaba las 33
escalas para recetas con tokens palette-*), pero con PREFIJO de componente
(--button-palette-*) — cada superficie pagaba ~21 KB, por eso solo button/toggle
lo tenían (rollout a las 17 = ~+315 KB). Colapso al modelo compartido:
- renderSharedPaletteLayer: UNA cascada component-agnóstica
[data-color='{role|scale}'] { --palette-{slot}: … } — 8 roles + 33 escalas,
emitida una vez (~19 KB). Roles 1:1 a --color-{role}-*; escalas con el step
map + on-solid contrast, valor idéntico a la cascada per-component retirada.
- renderRecipePaletteForward: toda receta con palette-* (público) o _palette-*
(privado) recibe [data-{c}][data-color] { --{c}-palette-{slot}:
var(--palette-{slot}, host-default) }. La guarda de PRESENCIA es la seguridad
de anidamiento — un hijo sin color dentro de un ancestro coloreado matchea
solo [data-{c}] (su default) y nunca lee el --palette-* heredado.
- Generalización a la paleta PRIVADA: checkbox/radio-group/switch ganan las 33
escalas con CERO cambio de CSS (ya consumen --_{c}-palette-*).
- universalPaletteDecls eliminado; 2 guards migrados al modelo compartido
(recipe-css-contract "shared layer + forward"; active-eidos-config contrast
en la capa compartida).
DONE (per-instance): button, toggle, checkbox, radio-group, switch (5/17).
Rollout de las 12 restantes en docs/process/continue-cleanroom-fixes §THM-2.
Doctrina en theming/reference §25 ("The per-instance palette layer").
Verificado en vivo (button + checkbox): color="teal" → escala teal exacta ·
affirm → rol exacto · primary → primary · nested-nocolor en ancestro teal →
primary (sin contaminación). Peso 345K → 329K. Gates: matriz 141/141 ·
eidos 314 · recipe-css-contract + active-eidos-config 98/98 · contracts 38/38.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
## THM-2 ✅ MECANISMO + 5 SUPERFICIES 2026-07-12 (paleta universal)
> El mecanismo genérico YA existía (`universalPaletteDecls` auto-completaba
> las 33 escalas para cualquier receta con tokens `palette-*`); el hueco real
> era el **prefijo de componente** (`--button-palette-*`) → cada superficie
> pagaba ~21 KB, por eso solo button/toggle. **Colapso ejecutado**:
> - **Capa compartida** (`renderSharedPaletteLayer`): UNA cascada
> `[data-color='{role|scale}'] { --palette-{slot} }` (8 roles + 33 escalas,
> ~19 KB una vez) reemplaza las copias per-component.
> - **Forward de presencia** (`renderRecipePaletteForward`): toda receta con
> `palette-*` (público) o `_palette-*` (privado) recibe
> `[data-{c}][data-color] { --{c}-palette-{slot}: var(--palette-{slot}, host-default) }`.
> La guarda de PRESENCIA es la seguridad de anidamiento (un hijo sin color
> dentro de un ancestro teal se queda en su default — verificado en vivo).
> - **Generalización a paleta privada**: checkbox/radio-group/switch ganan las
> 33 escalas con CERO cambio de CSS (ya consumen `--_{c}-palette-*`).
> - `universalPaletteDecls` eliminado; 2 guards migrados al nuevo modelo
> (recipe-css-contract "shared layer + forward"; active-eidos-config
> contrast en la capa compartida).
>
> **Verificado en vivo** (button + checkbox): `color="teal"` → escala teal
> exacta · `affirm` → rol exacto · `primary` → primary · **nested-nocolor en
> ancestro teal → primary (sin contaminación)**. Peso 345K → **329K** (− 16K)
> Y las 17 superficies ya PUEDEN usar la capa. Gates: matriz 141/141 · eidos
> 314 · recipe-css-contract 98/98 · contracts 38/38.
### Tracker de rollout THM-2 (sucesor de `pendiente_color_demos.md`)
feat(theming+soma): media-player Batch-4 (SOM-4) + THM-2 shared-palette rollout to 14 surfaces
media-player Batch-4 (closes SOM-4): the 10 sub-parts migrate to
renderProps() (35 attr-dup hits -> 0). Register the sources the morfo
reads (label propRef, on stateRef, direction/type) so renderProps emits
type/role/aria-*/data-state; keep only contract-only soma-owned attrs.
Pin the soma-attr-audit ceiling to 0.
THM-2 rollout - collapse each per-component palette cascade into the
shared per-instance layer (size win, behavior-preserving for roles,
adds the 33 scales):
- Rename palette tokens to canonical _palette-{slot} + route via the
presence-guarded forward: tags-input, stepper, badge, card, select
(parts-aware trigger+content), file-upload, editable, tag-group.
- Extend the shared vocabulary with alpha surface(a2)/surface-hover(a3)
slots (soft variant of variant-aware recipes) and hover(4) - the
latter also fixes Toggle's latent scale-hover.
- tag-group: variant-aware migration (solid/outline -> canonical,
soft -> alpha surface; merge outline-fg into text, prune orphans).
- toggle-group: replace the 4-role cross-recipe composition with a
hand-authored presence-scoped route to the shared layer (the
composition DSL can't presence-scope); covers roles + 33 scales.
- Widen color to ComponentColor (roles + 33 scales) where arbitrary
color is meaningful: badge, tag-group, tags-input, toggle-group, and
add the scale picker to their demos. Semantic controls stay restricted.
Verified end-to-end with the REAL components (drive the demo color
control -> the component emits data-color -> the shared layer resolves
the exact scale). Gates: recipe-css-contract/config/contracts/
soma-attr-audit/media-player suites green; 0 orphan tokens.
avatar remains for a focused pass: its variant-resolved matrix and badge
sub-part don't fit a mechanical migration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
**El rollout tiene DOS ejes (descubrimiento 2026-07-12 por crítica del usuario
"usa componentes reales, no divs"):** (1) el **token** en la capa compartida
(colapso de peso + resolución CSS); (2) el **tipo de la prop `color`** del
componente, que GATEA qué colores son alcanzables. El rename del token hace que
la cascada resuelva CUALQUIER `data-color` que el componente emita — pero si la
prop está tipada `ColorRole` , el componente NUNCA emite una escala, así que
`color="teal"` es error de tipo y las 33 escalas quedan LATENTES. Verificación
correcta = componente REAL + control REAL del demo (el picker de 33 escalas),
nunca `<div data-color>` fabricado ([[feedback_verify_with_real_components_not_synthetic_dom]]).
feat(theming): THM-2 — paleta universal por instancia (capa compartida + forward), 5/17 superficies
El mecanismo genérico ya existía (universalPaletteDecls auto-completaba las 33
escalas para recetas con tokens palette-*), pero con PREFIJO de componente
(--button-palette-*) — cada superficie pagaba ~21 KB, por eso solo button/toggle
lo tenían (rollout a las 17 = ~+315 KB). Colapso al modelo compartido:
- renderSharedPaletteLayer: UNA cascada component-agnóstica
[data-color='{role|scale}'] { --palette-{slot}: … } — 8 roles + 33 escalas,
emitida una vez (~19 KB). Roles 1:1 a --color-{role}-*; escalas con el step
map + on-solid contrast, valor idéntico a la cascada per-component retirada.
- renderRecipePaletteForward: toda receta con palette-* (público) o _palette-*
(privado) recibe [data-{c}][data-color] { --{c}-palette-{slot}:
var(--palette-{slot}, host-default) }. La guarda de PRESENCIA es la seguridad
de anidamiento — un hijo sin color dentro de un ancestro coloreado matchea
solo [data-{c}] (su default) y nunca lee el --palette-* heredado.
- Generalización a la paleta PRIVADA: checkbox/radio-group/switch ganan las 33
escalas con CERO cambio de CSS (ya consumen --_{c}-palette-*).
- universalPaletteDecls eliminado; 2 guards migrados al modelo compartido
(recipe-css-contract "shared layer + forward"; active-eidos-config contrast
en la capa compartida).
DONE (per-instance): button, toggle, checkbox, radio-group, switch (5/17).
Rollout de las 12 restantes en docs/process/continue-cleanroom-fixes §THM-2.
Doctrina en theming/reference §25 ("The per-instance palette layer").
Verificado en vivo (button + checkbox): color="teal" → escala teal exacta ·
affirm → rol exacto · primary → primary · nested-nocolor en ancestro teal →
primary (sin contaminación). Peso 345K → 329K. Gates: matriz 141/141 ·
eidos 314 · recipe-css-contract + active-eidos-config 98/98 · contracts 38/38.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
feat(theming+soma): media-player Batch-4 (SOM-4) + THM-2 shared-palette rollout to 14 surfaces
media-player Batch-4 (closes SOM-4): the 10 sub-parts migrate to
renderProps() (35 attr-dup hits -> 0). Register the sources the morfo
reads (label propRef, on stateRef, direction/type) so renderProps emits
type/role/aria-*/data-state; keep only contract-only soma-owned attrs.
Pin the soma-attr-audit ceiling to 0.
THM-2 rollout - collapse each per-component palette cascade into the
shared per-instance layer (size win, behavior-preserving for roles,
adds the 33 scales):
- Rename palette tokens to canonical _palette-{slot} + route via the
presence-guarded forward: tags-input, stepper, badge, card, select
(parts-aware trigger+content), file-upload, editable, tag-group.
- Extend the shared vocabulary with alpha surface(a2)/surface-hover(a3)
slots (soft variant of variant-aware recipes) and hover(4) - the
latter also fixes Toggle's latent scale-hover.
- tag-group: variant-aware migration (solid/outline -> canonical,
soft -> alpha surface; merge outline-fg into text, prune orphans).
- toggle-group: replace the 4-role cross-recipe composition with a
hand-authored presence-scoped route to the shared layer (the
composition DSL can't presence-scope); covers roles + 33 scales.
- Widen color to ComponentColor (roles + 33 scales) where arbitrary
color is meaningful: badge, tag-group, tags-input, toggle-group, and
add the scale picker to their demos. Semantic controls stay restricted.
Verified end-to-end with the REAL components (drive the demo color
control -> the component emits data-color -> the shared layer resolves
the exact scale). Gates: recipe-css-contract/config/contracts/
soma-attr-audit/media-player suites green; 0 orphan tokens.
avatar remains for a focused pass: its variant-resolved matrix and badge
sub-part don't fit a mechanical migration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
**Capa compartida (eje 1) — DONE (13):** button, toggle, checkbox, radio-group,
switch · tags-input, stepper, badge, card, select, file-upload, editable ·
**tag-group** (2026-07-12, variant-aware: solid/outline→slots canónicos,
**soft→slots ALPHA `surface` (a2)/`surface-hover`(a3)** que AÑADÍ a
`PALETTE_SLOT_STEP` + capa compartida + forward — extensión aditiva del
vocabulario, aprobada por el usuario; merge outline-fg→text + poda de 8
intermedios huérfanos).
feat(theming): THM-2 — paleta universal por instancia (capa compartida + forward), 5/17 superficies
El mecanismo genérico ya existía (universalPaletteDecls auto-completaba las 33
escalas para recetas con tokens palette-*), pero con PREFIJO de componente
(--button-palette-*) — cada superficie pagaba ~21 KB, por eso solo button/toggle
lo tenían (rollout a las 17 = ~+315 KB). Colapso al modelo compartido:
- renderSharedPaletteLayer: UNA cascada component-agnóstica
[data-color='{role|scale}'] { --palette-{slot}: … } — 8 roles + 33 escalas,
emitida una vez (~19 KB). Roles 1:1 a --color-{role}-*; escalas con el step
map + on-solid contrast, valor idéntico a la cascada per-component retirada.
- renderRecipePaletteForward: toda receta con palette-* (público) o _palette-*
(privado) recibe [data-{c}][data-color] { --{c}-palette-{slot}:
var(--palette-{slot}, host-default) }. La guarda de PRESENCIA es la seguridad
de anidamiento — un hijo sin color dentro de un ancestro coloreado matchea
solo [data-{c}] (su default) y nunca lee el --palette-* heredado.
- Generalización a la paleta PRIVADA: checkbox/radio-group/switch ganan las 33
escalas con CERO cambio de CSS (ya consumen --_{c}-palette-*).
- universalPaletteDecls eliminado; 2 guards migrados al modelo compartido
(recipe-css-contract "shared layer + forward"; active-eidos-config contrast
en la capa compartida).
DONE (per-instance): button, toggle, checkbox, radio-group, switch (5/17).
Rollout de las 12 restantes en docs/process/continue-cleanroom-fixes §THM-2.
Doctrina en theming/reference §25 ("The per-instance palette layer").
Verificado en vivo (button + checkbox): color="teal" → escala teal exacta ·
affirm → rol exacto · primary → primary · nested-nocolor en ancestro teal →
primary (sin contaminación). Peso 345K → 329K. Gates: matriz 141/141 ·
eidos 314 · recipe-css-contract + active-eidos-config 98/98 · contracts 38/38.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
feat(theming+soma): media-player Batch-4 (SOM-4) + THM-2 shared-palette rollout to 14 surfaces
media-player Batch-4 (closes SOM-4): the 10 sub-parts migrate to
renderProps() (35 attr-dup hits -> 0). Register the sources the morfo
reads (label propRef, on stateRef, direction/type) so renderProps emits
type/role/aria-*/data-state; keep only contract-only soma-owned attrs.
Pin the soma-attr-audit ceiling to 0.
THM-2 rollout - collapse each per-component palette cascade into the
shared per-instance layer (size win, behavior-preserving for roles,
adds the 33 scales):
- Rename palette tokens to canonical _palette-{slot} + route via the
presence-guarded forward: tags-input, stepper, badge, card, select
(parts-aware trigger+content), file-upload, editable, tag-group.
- Extend the shared vocabulary with alpha surface(a2)/surface-hover(a3)
slots (soft variant of variant-aware recipes) and hover(4) - the
latter also fixes Toggle's latent scale-hover.
- tag-group: variant-aware migration (solid/outline -> canonical,
soft -> alpha surface; merge outline-fg into text, prune orphans).
- toggle-group: replace the 4-role cross-recipe composition with a
hand-authored presence-scoped route to the shared layer (the
composition DSL can't presence-scope); covers roles + 33 scales.
- Widen color to ComponentColor (roles + 33 scales) where arbitrary
color is meaningful: badge, tag-group, tags-input, toggle-group, and
add the scale picker to their demos. Semantic controls stay restricted.
Verified end-to-end with the REAL components (drive the demo color
control -> the component emits data-color -> the shared layer resolves
the exact scale). Gates: recipe-css-contract/config/contracts/
soma-attr-audit/media-player suites green; 0 orphan tokens.
avatar remains for a focused pass: its variant-resolved matrix and badge
sub-part don't fit a mechanical migration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
**Prop `color` ampliada a `ComponentColor` (eje 2) — DONE + verificado real
(3):** **badge, tag-group, tags-input** (2026-07-12, `ColorRole` →`ComponentColor`
+ picker de 33 escalas en el demo + **verificado con el componente real** : click
en el chip "teal" del demo → el componente emite `data-color="teal"` → resuelve
escala teal exacta, incl. surface alpha translúcido). button/toggle/card ya la
tenían. **Semánticos, se quedan restringidos** (decisión usuario): checkbox/
radio-group (`AffirmativeColorRole`) · stepper (`ProgressiveColorRole`) ·
editable (`EditableColorRole`) · select/file-upload (`ColorRole`, accents de
form-control). Para éstos, THM-2 = colapso de peso behavior-preserving (roles).
feat(theming): THM-2 — paleta universal por instancia (capa compartida + forward), 5/17 superficies
El mecanismo genérico ya existía (universalPaletteDecls auto-completaba las 33
escalas para recetas con tokens palette-*), pero con PREFIJO de componente
(--button-palette-*) — cada superficie pagaba ~21 KB, por eso solo button/toggle
lo tenían (rollout a las 17 = ~+315 KB). Colapso al modelo compartido:
- renderSharedPaletteLayer: UNA cascada component-agnóstica
[data-color='{role|scale}'] { --palette-{slot}: … } — 8 roles + 33 escalas,
emitida una vez (~19 KB). Roles 1:1 a --color-{role}-*; escalas con el step
map + on-solid contrast, valor idéntico a la cascada per-component retirada.
- renderRecipePaletteForward: toda receta con palette-* (público) o _palette-*
(privado) recibe [data-{c}][data-color] { --{c}-palette-{slot}:
var(--palette-{slot}, host-default) }. La guarda de PRESENCIA es la seguridad
de anidamiento — un hijo sin color dentro de un ancestro coloreado matchea
solo [data-{c}] (su default) y nunca lee el --palette-* heredado.
- Generalización a la paleta PRIVADA: checkbox/radio-group/switch ganan las 33
escalas con CERO cambio de CSS (ya consumen --_{c}-palette-*).
- universalPaletteDecls eliminado; 2 guards migrados al modelo compartido
(recipe-css-contract "shared layer + forward"; active-eidos-config contrast
en la capa compartida).
DONE (per-instance): button, toggle, checkbox, radio-group, switch (5/17).
Rollout de las 12 restantes en docs/process/continue-cleanroom-fixes §THM-2.
Doctrina en theming/reference §25 ("The per-instance palette layer").
Verificado en vivo (button + checkbox): color="teal" → escala teal exacta ·
affirm → rol exacto · primary → primary · nested-nocolor en ancestro teal →
primary (sin contaminación). Peso 345K → 329K. Gates: matriz 141/141 ·
eidos 314 · recipe-css-contract + active-eidos-config 98/98 · contracts 38/38.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
feat(theming+soma): media-player Batch-4 (SOM-4) + THM-2 shared-palette rollout to 14 surfaces
media-player Batch-4 (closes SOM-4): the 10 sub-parts migrate to
renderProps() (35 attr-dup hits -> 0). Register the sources the morfo
reads (label propRef, on stateRef, direction/type) so renderProps emits
type/role/aria-*/data-state; keep only contract-only soma-owned attrs.
Pin the soma-attr-audit ceiling to 0.
THM-2 rollout - collapse each per-component palette cascade into the
shared per-instance layer (size win, behavior-preserving for roles,
adds the 33 scales):
- Rename palette tokens to canonical _palette-{slot} + route via the
presence-guarded forward: tags-input, stepper, badge, card, select
(parts-aware trigger+content), file-upload, editable, tag-group.
- Extend the shared vocabulary with alpha surface(a2)/surface-hover(a3)
slots (soft variant of variant-aware recipes) and hover(4) - the
latter also fixes Toggle's latent scale-hover.
- tag-group: variant-aware migration (solid/outline -> canonical,
soft -> alpha surface; merge outline-fg into text, prune orphans).
- toggle-group: replace the 4-role cross-recipe composition with a
hand-authored presence-scoped route to the shared layer (the
composition DSL can't presence-scope); covers roles + 33 scales.
- Widen color to ComponentColor (roles + 33 scales) where arbitrary
color is meaningful: badge, tag-group, tags-input, toggle-group, and
add the scale picker to their demos. Semantic controls stay restricted.
Verified end-to-end with the REAL components (drive the demo color
control -> the component emits data-color -> the shared layer resolves
the exact scale). Gates: recipe-css-contract/config/contracts/
soma-attr-audit/media-player suites green; 0 orphan tokens.
avatar remains for a focused pass: its variant-resolved matrix and badge
sub-part don't fit a mechanical migration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
**toggle-group ✅ HECHO + verificado real (2026-07-12):** colcoreaba vía
composición cross-recipe en la paleta pública de Toggle (4 roles). Migrado a
**ruta a la capa compartida**: (1) añadí el slot `hover` (4) a `PALETTE_SLOT_STEP`
— behavior-preserving para Toggle (`--toggle-{role}-hover`=`--color-{role}-hover`)
y **de paso arregla el hover-en-escalas de Toggle** (estaba latente-roto); (2)
quité el bloque `composition` del recipe (era su ÚNICO consumidor — el DSL de
composición no puede presence-scope `[data-color]` sin valor) y **hand-authoré
UNA regla** en `toggle-group.css` : `[data-toggle-group][data-color]
[data-toggle-group-item] { --toggle-palette-{slot}: var(--palette-{slot}) }`
(presence-guard = anidamiento seguro; cubre roles + 33 escalas); (3) amplié
`ToggleGroupColor` a `ComponentColor` (+ sentinels absent/unknown) + picker.
Test `active-eidos-config` que pinneaba la composición → actualizado (el DSL
`emitComposition` queda como infra sin consumidor actual). **Verificado real** :
chip "teal" → grupo emite `data-color="teal"` → item (un Toggle) obtiene
`--toggle-palette-solid` =teal-9, `-hover` =teal-4, `-border` =teal-7, `-text` =teal-11;
Toggle sin regresión (affirm-hover preservado, teal-hover ahora funciona).
feat(theming): THM-2 - avatar root palette to the shared layer + 33 scales
The avatar body's per-instance palette now resolves through the shared
per-instance layer (size collapse, behavior-preserving for roles, adds
the 33 scales):
- avatar.svelte: isCanonicalColor recognizes palette scales, so a scale
name routes to data-color instead of the custom CSS-color path ('teal'
is also a CSS color name, so it used to fall to --avatar-color-custom).
- recipes/base.ts: _palette-{solid,surface,contrast,text,border} (host
fallback only - the avatar always emits a canonical data-color, so the
presence-guarded forward routes roles + scales) + _bg/_fg/_border move
from a variant x color matrix to variant-only readers of
--_avatar-palette-*. The matrix helper + intermediates stay (the badge
still uses them; 0 orphans). avatar.css unchanged.
- demo: scale picker.
Verified end-to-end with the REAL component (drive the demo color control
-> the avatar emits data-color -> the shared layer resolves the exact
scale): affirm+soft behavior-preserving, teal+soft = scale-teal-a2
(translucent surface), teal+solid = scale-teal-9.
The Avatar.Badge sub-part (aria-hidden decorative chip with its own color
+ default) stays on its role palette + custom-CSS: migrating it needs a
per-part forward + pruning ~36 orphaned intermediates, disproportionate
for a decoration whose arbitrary-color case already works. Documented for
a decision.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
**avatar ROOT ✅ HECHO + verificado real (2026-07-12):** la arruga #1 (default
sutil sin-color) resultó **inalcanzable** — `avatar.svelte` hace
`color ?? 'neutral'` , así que el avatar SIEMPRE emite `data-color` canónico y el
matrix siempre casa (host = fallback muerto). Migración limpia sin borrar
intermedios:
- `avatar.svelte` : `isCanonicalColor` ahora incluye `PALETTE_SCALES` (una escala
va a `data-color` , no a la vía custom-CSS — clave: `teal` es TAMBIÉN un color
CSS, antes caía a `--avatar-color-custom` ).
- `recipes/base.ts` : `_palette-{solid,surface,contrast,text,border}` (solo host
fallback; el forward rutea roles + 33 escalas) + `_bg/_fg/_border` pasan de
matrix (variant× color) a **variant-only** leyendo `--_avatar-palette-*` . El
matrix helper + los 48 intermedios SE QUEDAN (el badge sigue usándolos → 0
orphans). avatar.css SIN CAMBIOS (lee `--_avatar-bg/fg/border` igual).
- **Verificado real** (control del demo + picker de escalas añadido): chip "teal"
→ el avatar emite `data-color="teal"` ; affirm+soft behavior-preserving
(`--_avatar-bg`=`--color-affirm-surface`); teal+soft=`--scale-teal-a2`
(surface translúcido), teal+solid=`--scale-teal-9`. `AvatarColor` ya era
`ColorRole | (string & {})` → acepta escalas sin cambio de tipo.
**Sub-parte `Avatar.Badge` — FUERA de la paleta-por-escala A PROPÓSITO
(decisión de diseño cerrada 2026-07-12, alineada con el ecosistema):** es un
chip de estado DECORATIVO (`aria-hidden`) con su PROPIO `color` /default
(affirm-solid), independiente del avatar. Acepta **roles/intents** (estado:
online→affirm, alerta→threat) ** + custom-CSS** (cualquier string). Eso es
EXACTAMENTE lo que hace la referencia: MUI/Spectrum/Bootstrap → color de badge
= semántico (rol/estado); Chakra `bg` /Mantine Indicator/Ant `color` → color
directo/CSS. **Ningún framework mete el badge en un sistema de escala de paleta
por instancia** — un badge de estado no necesita `teal` /`plum` como escala de
marca. Migrarlo (forward hand-authored por-parte + ~36 podas de intermedios)
sería sobre-ingeniería que se aparta de la convención. Por tanto **NO es deuda:
es el diseño correcto.** THM-2 CERRADO.
feat(theming+soma): media-player Batch-4 (SOM-4) + THM-2 shared-palette rollout to 14 surfaces
media-player Batch-4 (closes SOM-4): the 10 sub-parts migrate to
renderProps() (35 attr-dup hits -> 0). Register the sources the morfo
reads (label propRef, on stateRef, direction/type) so renderProps emits
type/role/aria-*/data-state; keep only contract-only soma-owned attrs.
Pin the soma-attr-audit ceiling to 0.
THM-2 rollout - collapse each per-component palette cascade into the
shared per-instance layer (size win, behavior-preserving for roles,
adds the 33 scales):
- Rename palette tokens to canonical _palette-{slot} + route via the
presence-guarded forward: tags-input, stepper, badge, card, select
(parts-aware trigger+content), file-upload, editable, tag-group.
- Extend the shared vocabulary with alpha surface(a2)/surface-hover(a3)
slots (soft variant of variant-aware recipes) and hover(4) - the
latter also fixes Toggle's latent scale-hover.
- tag-group: variant-aware migration (solid/outline -> canonical,
soft -> alpha surface; merge outline-fg into text, prune orphans).
- toggle-group: replace the 4-role cross-recipe composition with a
hand-authored presence-scoped route to the shared layer (the
composition DSL can't presence-scope); covers roles + 33 scales.
- Widen color to ComponentColor (roles + 33 scales) where arbitrary
color is meaningful: badge, tag-group, tags-input, toggle-group, and
add the scale picker to their demos. Semantic controls stay restricted.
Verified end-to-end with the REAL components (drive the demo color
control -> the component emits data-color -> the shared layer resolves
the exact scale). Gates: recipe-css-contract/config/contracts/
soma-attr-audit/media-player suites green; 0 orphan tokens.
avatar remains for a focused pass: its variant-resolved matrix and badge
sub-part don't fit a mechanical migration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
Rename limpio (patrón, para los que tenían slots canónicos): renombrar la clave
del token en `recipes/base.ts` a `_palette-{slot}` + los consumidores
`--_{c}-{slot}` /`--_{c}-accent-{slot}` en el `.css` ; el forward auto-emite
`[data-{c}][data-color]{--_{c}-palette-slot: var(--palette-slot, host)}` .
Baseline estable = 9 errores AJENOS en `src/` (4 connection + 1 nav-menu
`sideOffset` + 4 file-upload-soma `props['aria-label']` , todos pre-existentes/
track paralelo, probado con git stash). Registrado en `docs/next-features.md §6` .
feat(theming+soma): media-player Batch-4 (SOM-4) + THM-2 shared-palette rollout to 14 surfaces
media-player Batch-4 (closes SOM-4): the 10 sub-parts migrate to
renderProps() (35 attr-dup hits -> 0). Register the sources the morfo
reads (label propRef, on stateRef, direction/type) so renderProps emits
type/role/aria-*/data-state; keep only contract-only soma-owned attrs.
Pin the soma-attr-audit ceiling to 0.
THM-2 rollout - collapse each per-component palette cascade into the
shared per-instance layer (size win, behavior-preserving for roles,
adds the 33 scales):
- Rename palette tokens to canonical _palette-{slot} + route via the
presence-guarded forward: tags-input, stepper, badge, card, select
(parts-aware trigger+content), file-upload, editable, tag-group.
- Extend the shared vocabulary with alpha surface(a2)/surface-hover(a3)
slots (soft variant of variant-aware recipes) and hover(4) - the
latter also fixes Toggle's latent scale-hover.
- tag-group: variant-aware migration (solid/outline -> canonical,
soft -> alpha surface; merge outline-fg into text, prune orphans).
- toggle-group: replace the 4-role cross-recipe composition with a
hand-authored presence-scoped route to the shared layer (the
composition DSL can't presence-scope); covers roles + 33 scales.
- Widen color to ComponentColor (roles + 33 scales) where arbitrary
color is meaningful: badge, tag-group, tags-input, toggle-group, and
add the scale picker to their demos. Semantic controls stay restricted.
Verified end-to-end with the REAL components (drive the demo color
control -> the component emits data-color -> the shared layer resolves
the exact scale). Gates: recipe-css-contract/config/contracts/
soma-attr-audit/media-player suites green; 0 orphan tokens.
avatar remains for a focused pass: its variant-resolved matrix and badge
sub-part don't fit a mechanical migration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
## media-player Batch-4 ✅ EJECUTADA 2026-07-13 — SOM-4 CERRADO (attr-dedup completo)
feat(theming+soma): media-player Batch-4 (SOM-4) + THM-2 shared-palette rollout to 14 surfaces
media-player Batch-4 (closes SOM-4): the 10 sub-parts migrate to
renderProps() (35 attr-dup hits -> 0). Register the sources the morfo
reads (label propRef, on stateRef, direction/type) so renderProps emits
type/role/aria-*/data-state; keep only contract-only soma-owned attrs.
Pin the soma-attr-audit ceiling to 0.
THM-2 rollout - collapse each per-component palette cascade into the
shared per-instance layer (size win, behavior-preserving for roles,
adds the 33 scales):
- Rename palette tokens to canonical _palette-{slot} + route via the
presence-guarded forward: tags-input, stepper, badge, card, select
(parts-aware trigger+content), file-upload, editable, tag-group.
- Extend the shared vocabulary with alpha surface(a2)/surface-hover(a3)
slots (soft variant of variant-aware recipes) and hover(4) - the
latter also fixes Toggle's latent scale-hover.
- tag-group: variant-aware migration (solid/outline -> canonical,
soft -> alpha surface; merge outline-fg into text, prune orphans).
- toggle-group: replace the 4-role cross-recipe composition with a
hand-authored presence-scoped route to the shared layer (the
composition DSL can't presence-scope); covers roles + 33 scales.
- Widen color to ComponentColor (roles + 33 scales) where arbitrary
color is meaningful: badge, tag-group, tags-input, toggle-group, and
add the scale picker to their demos. Semantic controls stay restricted.
Verified end-to-end with the REAL components (drive the demo color
control -> the component emits data-color -> the shared layer resolves
the exact scale). Gates: recipe-css-contract/config/contracts/
soma-attr-audit/media-player suites green; 0 orphan tokens.
avatar remains for a focused pass: its variant-resolved matrix and badge
sub-part don't fit a mechanical migration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
> Gates: **soma-attr-audit media-player = 0 hits** (35→0) · provider test 9/9 ·
> guard soma-attr-audit con `media-player: 0` verde · contracts **38/38** ·
> baseline propio **57** restaurado · **navegador (dev real)**:
> DOM de cada botón con sus attrs de morfo emitidos por renderProps + labels
> LOCALIZADOS (`Reproducir`/`Silenciar`/`Avanzar`/`Pantalla completa`), y el
> camino reactivo verificado en vivo (mute click → `data-state` off→on +
> `aria-pressed` false→true + `aria-label` "Silenciar"→"Activar sonido"; play
> click → paused→playing + "Reproducir"→"Pausar").
>
> **Hallazgo de reconciliación**: el ceiling REAL era **0**, no un residuo. El
> handoff asumía "dejar el `aria-label` como override" (patrón carousel), pero
> carousel guarda su label porque su morfo declara un `translationRef` DEFAULT y
> soma añade la lógica prop-or-translation encima. El morfo de media-player
> declara `aria-label: propRef('label')` a secas — el label ES un prop, así que
> lo correcto es ALIMENTARLO como source (`props: { label: () => this.label }`)
> y dejar que renderProps lo emita. Cero override.
>
> **Patrón aplicado** a las 10 sub-partes (`media-player-provider.svelte.ts`):
> por parte, registrar en `runtime.part(...)` los sources que el morfo lee —
> `props: { label }` (buttons), `states: { on }` (mute/fullscreen/pip/caption,
> para el par `data-state`+`aria-pressed` vía `stateRef('on')`),
> `props: { direction }` (seek), `props: { type }` (time) — y en el `.assert()`
> cambiar `...runtimePart.props` por `...runtimePart.renderProps()`, dejando SOLO
> lo contract-only que soma posee (play-button `data-state` sin `value`;
> `data-disabled` de presencia; `data-media` de media/`aria-hidden` de
> poster/buffering salen de renderProps). El `data-media` de la parte `media` lo
> resuelve renderProps contra el source `media` de nivel-componente.
>
> **Ajuste de test**: en `media-player-provider.svelte.test.ts` los accesos
> indexados `props['aria-label']`/`['aria-pressed']`/`['data-state']` dejaron de
> tipar (el `as const` sobre el bag ya no expone esas claves — vienen del spread
> `renderProps()`: `Record<string,unknown>`) → convertidos a `toMatchObject`
> (mismo aserto de runtime, type-safe; estilo del test de carousel). Valores en
> runtime idénticos byte-a-byte (los 9 tests siguen verdes).
>
> **Cierre en el guard**: `soma-attr-audit.test.ts` gana `'media-player': 0`
> con dossier de por qué es 0; el comentario "Still UNMIGRATED" retirado.
### THM-4 · media-player ✅ ADJUDICADO 2026-07-12 — 0 deuda real
Dossier de los **47 `unused`** (columna de `eidos-lint media-player` ) contra la
doctrina `eidos.md §unused` (comportamiento/composición = legítimo; "declarado
para un eje visual que nadie consume, sin razón" = deuda). Clasificación
completa — **cero deuda silenciosa** :
- **21 · estados de botón** (play/mute/fullscreen/pip/caption/seek/settings ×
`data-state` /`data-disabled`/`data-direction`) → **composición legítima** : el
cromo del botón (state-layer, disabled, focus) vive en el `<IconButton>`
compuesto; `data-state` conduce el swap de glifo en Svelte (JS), no en CSS.
- **7 · sliders** (time/volume × `data-orientation` /`data-disabled`/`data-seeking`)
→ **composición** : el visual es el `[data-slider]` compuesto.
- **11 · flags de estado del root** (`data-paused`/`muted`/`seeking`/`ended`/
`error` /`fullscreen`/`pip`/`live`/`can-play`/`captions` + `controls='visible'` )
→ **espejos de comportamiento** del elemento media (el morfo declara toda la
superficie behavioral; `controls='visible'` es el estado por defecto — solo
`hidden` se estiliza).
- **6 · resto** (media-part `data-media` [lo estiliza el padre], time
`data-type='current'/'remaining'` [default; solo `duration` distingue],
captions/buffering/controls `data-*` propios [driven por el padre / superficie
opcional]) → legítimo.
- **2 (nota) · superficies v2 documentadas**: `settings-button` (data-state
open/closed + disabled) y el custom `captions` NO tienen provider/CSS/render —
son **roadmap v2 declarado a propósito** (ambos READMEs lo marcan v2). No es
deuda silenciosa; es superficie futura documentada. Candidatas a poda SOLO si
se abandona el v2.
**Conclusión**: el count alto es porque media-player es un composite grande (10
partes + 10 espejos de engine), no porque tenga deuda. Nada que consumir/podar
sin decisión de feature (p.ej. un badge LIVE o un overlay de error serían
diseño nuevo, no THM-4). **media-player adjudicado, 0 deuda.**
### THM-4 · stepper ✅ ADJUDICADO 2026-07-12 — mayoría legítima + 1 redundancia de API
De los **30 `unused`** : ~la mitad legítimos (`data-state='incomplete'` = estado
base; `data-orientation='horizontal'` = default; estados de content). El
hallazgo: el morfo declara en CADA parte (item/trigger/indicator/separator)
**DOS representaciones del mismo estado** — `data-state` (enum complete/current/
incomplete) **Y** los booleanos `data-current` /`data-complete`/`data-incomplete`
(`propRef`, severity optional). El propio CSS las usa **inconsistentemente** :
enum en trigger/indicator, booleano `data-complete` en el separator. Así que NO
es deuda-sin-consumidor (la forma booleana SÍ se consume en el separator) —
es **redundancia** : cada parte declara ambas formas y usa una.
**CONSOLIDADO ✅ (decisión de usuario 2026-07-12)** en `data-state` (canónico),
API pública unificada. 5 sitios: morfo (podados 12 declaraciones booleanas de
item/trigger/indicator/separator) · provider (podados los sources
current/complete/incomplete de item/indicator/separator + los writes explícitos
del trigger + los getters `isComplete` /`isIncomplete` — `isCurrent` se queda,
lo usan aria-current/tabindex) · provider-test (assertions booleanas fuera) ·
README (10 filas booleanas fuera) · separator.css (`[data-complete]` →
`[data-state='complete']` ). **Verificado real** (demo, botón Next): estados por
`data-state` (complete/current/incomplete), booleanos ausentes, separator
completo pinta `--_stepper-palette-solid` vía `data-state='complete']`
(behavior-preserving). Gates: provider-test + contracts verdes · baseline 9 ·
**stepper unused 30→18** (total catálogo 1152→1140).
### THM-4 — lectura emergente (2 lotes)
media-player (0 deuda) + stepper (0 deuda-real, 1 redundancia de API —
**consolidada**): los hotspots son **mayormente legítimos** (composites grandes
+ superficie behavioral); la deuda-sin-consumidor genuina es **rara** . Lo
accionable son **decisiones de diseño** (como el dual-API de stepper que el
usuario decidió consolidar), no podas mecánicas. Esto
CONFIRMA la predicción de la doctrina ("la mayoría son legítimos") y sugiere que
**la tasa de deuda NO justifica una regla de máquina** — THM-4 es sobre todo
docs(theming): close THM-4 — 5 hotspots adjudicated, only 1 real debt item
Adjudicate color-picker (29), avatar (28), time-range-picker (26): all
0 debt — composition (de-dialoged pickers compose Popover; avatar's
per-role color now lives in the shared layer after the THM-2 migration;
sliders/toggle composed), behavioral (sema last-action, format, endpoint,
loading status), and per-file-lint blind spots (recipe-token variant
rules). Across the 5 biggest hotspots (156 unused) the only real debt was
stepper's dual-representation API, now consolidated. Confirms the doctrine:
the unused column stays informational, no machine rule warranted. THM-4
closed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
adjudicación documentada.
### THM-4 · color-picker (29) + avatar (28) + time-range-picker (26) ✅ ADJUDICADOS 2026-07-12 — 0 deuda
- **color-picker (29)** → 0 deuda: `data-state='open/closed'` (root/content/
trigger) = **composición** (de-dialoged, `color-picker-content.svelte` compone
`PopoverContent` — el estado lo pinta el Popover) · `data-last-action=*` =
**sema/dismissal** (escrito por prewrites close-commit/cancel/dismiss, lo
consumen sema packs) · `data-format` /`data-disabled/invalid/readonly/required`
+ area/channel-slider/swatch = behavioral + composición (Sliders/Field
compuestos).
- **avatar (28)** → 0 deuda: `[data-avatar][data-color='ROLE']` × 8 = **artefacto
de capa compartida** — mi migración THM-2 movió el color al forward
`[data-avatar][data-color]` presence-guarded, así que los per-rol dejaron de
vivir en avatar.css (viven en la capa) → el count SUBIÓ por una MEJORA, no
deuda · `data-variant='soft/solid'` = lo consume el **recipe token** (generado,
verificado 2 hits) — punto ciego del lint per-fichero (solo ve avatar.css) ·
badge `[data-color='ROLE']` × 8 = el matrix consume la forma compuesta
`[variant][color]` , no la per-rol suelta · `data-status=*` = estado de carga
behavioral (loading/idle SÍ se usan; loaded/error = default).
- **time-range-picker (26)** → 0 deuda: `data-state='open/closed'` = composición
(Popover) · `data-last-action` /`data-starting-style`/`data-ending-style` =
sema + motion @starting -style · `*-slider[data-endpoint/disabled]` +
day-period-toggle = composición (Sliders/Toggle) + behavioral (endpoint) ·
`data-disabled/required` = form-state.
### THM-4 — CONCLUSIÓN (5 hotspots adjudicados)
Los 5 mayores hotspots (156 `unused` juntos) están adjudicados: **media-player,
color-picker, avatar, trp = 0 deuda** (composición + behavioral + capa
compartida); **stepper = 1 redundancia de API, consolidada** (`75fa9400`).
**Deuda real total en los 5 mayores = 1 ítem** (arreglado). Esto CONFIRMA la
doctrina ("la mayoría son legítimos") y sella la decisión: **la columna `unused`
se queda informativa, sin severidad/regla de máquina** — la tasa de deuda no la
justifica. El resto del catálogo (~1140) es la cola larga de las mismas
categorías legítimas. Hallazgo colateral: migrar un componente a la capa
compartida (avatar) SUBE su `unused` (los per-rol pasan a artefacto de capa) —
el count alto no es señal de deuda. **THM-4 CERRADO.**
feat(theming+soma): media-player Batch-4 (SOM-4) + THM-2 shared-palette rollout to 14 surfaces
media-player Batch-4 (closes SOM-4): the 10 sub-parts migrate to
renderProps() (35 attr-dup hits -> 0). Register the sources the morfo
reads (label propRef, on stateRef, direction/type) so renderProps emits
type/role/aria-*/data-state; keep only contract-only soma-owned attrs.
Pin the soma-attr-audit ceiling to 0.
THM-2 rollout - collapse each per-component palette cascade into the
shared per-instance layer (size win, behavior-preserving for roles,
adds the 33 scales):
- Rename palette tokens to canonical _palette-{slot} + route via the
presence-guarded forward: tags-input, stepper, badge, card, select
(parts-aware trigger+content), file-upload, editable, tag-group.
- Extend the shared vocabulary with alpha surface(a2)/surface-hover(a3)
slots (soft variant of variant-aware recipes) and hover(4) - the
latter also fixes Toggle's latent scale-hover.
- tag-group: variant-aware migration (solid/outline -> canonical,
soft -> alpha surface; merge outline-fg into text, prune orphans).
- toggle-group: replace the 4-role cross-recipe composition with a
hand-authored presence-scoped route to the shared layer (the
composition DSL can't presence-scope); covers roles + 33 scales.
- Widen color to ComponentColor (roles + 33 scales) where arbitrary
color is meaningful: badge, tag-group, tags-input, toggle-group, and
add the scale picker to their demos. Semantic controls stay restricted.
Verified end-to-end with the REAL components (drive the demo color
control -> the component emits data-color -> the shared layer resolves
the exact scale). Gates: recipe-css-contract/config/contracts/
soma-attr-audit/media-player suites green; 0 orphan tokens.
avatar remains for a focused pass: its variant-resolved matrix and badge
sub-part don't fit a mechanical migration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
### Cola pendiente (sin orden fijo — algunas requieren decisión de usuario)
- **THM-2 rollout** — CERRADO (ver §THM-2; `Avatar.Badge` fuera por diseño).
docs(theming): close THM-4 — 5 hotspots adjudicated, only 1 real debt item
Adjudicate color-picker (29), avatar (28), time-range-picker (26): all
0 debt — composition (de-dialoged pickers compose Popover; avatar's
per-role color now lives in the shared layer after the THM-2 migration;
sliders/toggle composed), behavioral (sema last-action, format, endpoint,
loading status), and per-file-lint blind spots (recipe-token variant
rules). Across the 5 biggest hotspots (156 unused) the only real debt was
stepper's dual-representation API, now consolidated. Confirms the doctrine:
the unused column stays informational, no machine rule warranted. THM-4
closed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
- **THM-4 hotspots** — **CERRADO** : 5 hotspots adjudicados (media-player/color-
picker/avatar/trp = 0 deuda · stepper = 1 redundancia consolidada `75fa9400` ).
Columna `unused` = informativa, sin regla. Dossiers arriba.
- **F4-D GAP-\*** — menú de personalización (§7): pendiente de SCOPING del
usuario (qué se arranca vs qué se registra como límite deliberado).
- **cascada-vs-firmas** (motion) — cola lateral: las reglas `[data-stagger]`
sombrean firmas de evento de igual/menor especificidad.
fix(cleanroom): F2+F3+F4-C+SEM-4s1 — lote mecánico, censos con guard, corpus documental y el close polimórfico de los pickers VIVO
F2 — lote mecánico (13 ítems):
- DEP-2 ogl eliminado (0 imports) · DEP-1 clsx inlineado como toClassString
propio + suite de contrato (props.test.ts; soma.md §12 cerrado).
- THM-7: los 5 selectores manuales de sema.md reescritos con semaSelector
(los ejemplos [data-toast-root] apuntaban a un part INEXISTENTE — la
deriva que el builder previene, demostrada en el propio doc).
- MOR-1 escape isomorfo + validación de attr-names en semaSelector + 9
tests (selectors.test.ts, matches() real con comillas/corchetes) ·
MOR-2 partMarkerAttr = única fuente compilador↔builder + test de paridad ·
MOR-3 _resetCompileCache borrado (0 usos).
- SOM-2 keydown continue en match sin handler + keyboardFixtureMorfo ·
SOM-1 no-await de handlers (censo async = 0; contrato V1 cumplido) + pin.
- SEM-2 trigger pre-attacha catch con logger (void trigger sin unhandled
rejection; throw intacto para awaiters) + pin · SEM-3 fallback muerto de
applyDominance → skip defensivo + timer tope de awaitExpression cancelado ·
SEC-1 adjudicado YA implementado (assertCssVariableValue desde 2026-05-11)
+ pin del path de VALOR.
- accordion → outline (§32; su outline:none dejaba CERO anillo en HCM) —
verificado en vivo · THM-6 radius-full 9999px · EID-4 recuentos 33.
F3 — censos con guard:
- SOM-3 cerrado: announcer + image-provider migrados a scheduler-preferred
(consumidores cableados: date/time-field vía soma.uix.timers; avatar/image
vía eidos.timers — verificado en vivo); guard de timers ENSANCHADO de
soma/components a TODO soma y pasado a EVIDENCIA (setTimeout exige
.schedule( en el fichero — layers/ y datetime/ escapaban del ámbito viejo).
- THM-5: R-4.7 nueva (válvula same-line /* important: <razón> */, escaneo
comment-blanked) + las 15 declaraciones anotadas con su razón + canon
recipe-contract §3/§4.
- SOM-4 adjudicado: el censo/guard YA existían (49 pins); knob/mask-field/
timeline pinneados (overrides documentados en call-site); media-player
Batch-4 (35 hits, cero renderProps) = único batch restante, registrado.
- THM-4 doctrinado en eidos.md §unused (comportamiento/composición =
legítimo; deuda = eje visual sin consumidor; hotspots por lotes).
F4-C — corpus documental (decisiones de usuario aplicadas):
- DOC-3: los 15 enlaces muertos resueltos (repoint a la edición FINAL
trackeada / des-link históricos) · docs:check I6-links WARN→ERROR.
- DOC-1: tabla «Build contract» MIGRADA a component-guide con estados
modernizados (A3–A5 → LIVE + guards de hoy); banners reapuntados; citas
de CANON/sema.md historificadas; lápida-redirect en el §13 del fósil.
- DOC-4: hold chain → holds.ts · FAQ event:* SUPERSEDED por signatures ·
gradient añadido a los DOS capstones (sextet real) · nota de paleta de
demo-authoring corregida (universalPaletteDecls + decisión THM-2 =
mecanismo universal como sucesor del tracker borrado).
- DOC-5/6: recuentos anti-frágiles datados · §4.11 dup → §4.12 · Known gaps
historificado · N-6/N-7 recuperadas de git (d68d2c45^) y canonizadas en
eidos.md §pickers · authoring E2 → canon/tsc.md · air-old des-linkado ·
EID-3 (placement) en la fila RTL · AUX-2 disabledDom documentado.
SEM-4 sesión 1 — el close polimórfico de los pickers, VIVO (D.11):
- Reconciliación: los morfos ya no declaran close (delegated al Popover,
de-dialoged 06-27); el agujero real era el cierre programático bypaseando
dismissWith → save/cancel/select eran perceptualmente SILENCIOSOS.
- Fix: PickerShellHandle.setPopoverDismiss + closeWith(cause) en los 5
providers (14 sitios; select/commit → 'save' = commit.save+fulfill,
cancel → 'cancel' = emerge; fallback raw para headless) + UN inyector en
el eidos PickerShell root (norma N-8). Picker genérico fuera a propósito
(ya suena commit-set/cancel por diseño S9).
- Verificado en vivo (date-picker): Done → close·commit·fulfill·active ·
Cancel → close·emerge · cierre real.
Gates: matriz 141/141 (los 6 morfos nuevos de la pista de texto paralela
también PASS) · contracts 38/38 · eidos 314 · sema 178 · morfo 94 ·
docs:check 0/0 con I6 en error · baseline propio 57.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
## (histórico) ARRANQUE F4 (iniciativas, cada una con decisión)
F0– F3 hechas. F4 espera scoping del usuario por iniciativa (ver §F4 abajo):
**F4-A** THM-2 paleta-33 (medir peso primero) · **F4-B** SEM-4 firma dormida
(close × 5 + packs × 6 + flag `emission` ) · **F4-C** corpus documental
(DOC-1/2 decisiones + barrido DOC-3/4/5/6 + EID-3 + AUX-2 + docs:check a
error) · **F4-D** menú GAP-\*. Lotes ya dimensionados que pueden entrar en
cualquier hueco: **media-player Batch-4** (renderProps, 35 hits) · quema
THM-4 de hotspots · cascada-vs-firmas (motion, en cola lateral).
## (histórico) ARRANQUE F3 (censos con guard)
F2 hecha (arriba). F3 según la sección de abajo, orden sugerido: **SOM-3**
(los ~6 setTimeout restantes a `uix.timers` , patrón float-panel, y retirar
exenciones del guard) → **THM-5** (17 `!important` : anotación
`/* important: <razón> */` + regla de máquina para no anotados) → **SOM-4**
(censo providers re-declarando attrs morfo → tabla + guard WARN + mecánicos;
si el volumen es grande, plan por lotes para aprobación) → **THM-4**
(doctrina para selectores sin consumidor + quemar hotspots). Tras F3 → F4
(iniciativas, cada una con decisión de usuario).
## (histórico) ARRANQUE F2 (lote mecánico)
Estado al cierre 2026-07-11: **F0 ✅ + F1 ✅** (commiteadas — `4addfd7d` +
el handoff posterior). Gates vigentes que deben MANTENERSE: matriz
**135/0/0** · contracts **38/38** · eidos **309/309** · sema 178 · baseline
**57** · vocab exit 0. Leer los bloques «EJECUTADA» de F0/F1 (abajo) para
el contexto y arrancar F2 directo — es el lote SIN decisiones de usuario.
Orden sugerido (barato → delicado, no-cascada: ítem → gates → siguiente):
1. **DEP-2** ogl fuera de package.json (+lockfile) · **DEP-1** clsx
inlineado (cerrar el "pending" de soma.md §12).
2. One-liners: **THM-7** (ejemplo sema.md → semaSelector) · **THM-6**
(menu-dial `--radius-full, 1px` → 9999px) · **EID-4** ("31-scale" → 33 × 2).
3. **MOR-1** escape/validación en semaSelector (isomorfo, sin CSS.escape
del DOM) + tests con `"` y `]` · **MOR-2** des-duplicar convención de
marker · **MOR-3** `_resetCompileCache` implementar-o-borrar.
4. **SOM-2** keydown `continue` en match sin handler + test · **SOM-1**
censo de handlers async → si 0, quitar el await (si hay, DECISIÓN #3 ).
5. **SEM-2** logging por defecto (dev) en canales sin logger + vía
`void trigger` sin unhandled rejection (throw intacto para awaiters) ·
**SEM-3** fallback muerto applyDominance + cancelar timer de
awaitExpression · **SEC-1** serializador renderCssVariables rechaza
`;` /`}` embebidos + test.
6. **accordion → outline** (hallazgo F1: `outline:none` + box-shadow = cero
anillo en HCM) — con verificación visual, patrón tabs.css:102.
Después de F2 → F3 (censos SOM-3◐/SOM-4/THM-5/THM-4). Las iniciativas F4
(THM-2 paleta · SEM-4 firma dormida · DOC-1/2 · GAP-\*) esperan decisión.
## Principios de secuencia
1. **La máquina primero.** Los fixes que amplían el enforcement (THM-1,
MOR-4, guard de enumeración) van ANTES que el resto: destapan trabajo que
las fases posteriores deben ver en el tablero.
2. **Rojo = parar la línea.** Una suite/guard en rojo (hoy: THM-3) se arregla
antes de cualquier otra cosa de la sesión. Ninguna sesión cierra en rojo.
3. **No-cascada** : un fix → gates → el siguiente. Fix sistémico ⇒ guard que
lo fije (doctrina del programa 2026-06-26).
4. **Iniciativas ≠ fixes.** THM-2, SEM-4, DOC-1/2 y los GAP-\* requieren
decisión de alcance del usuario ANTES de ejecutar — este plan las
dimensiona y propone, no las arranca en frío.
---
## F0 — La máquina, en verde y completa ✅ EJECUTADA 2026-07-11
> Resultado: matriz **135/0/0** (card-group dentro) · contracts **38/38**
> (guard MOR-4 nuevo) · suite eidos **306/306** · baseline 57 · vocab exit 0.
> Verificación en navegador: shake `commit-block` vivo (`block-shake`,
> `--duration-moderate`, stamp `commit · risk`) + chevrons a
> `--opacity-muted` (0.65 computado). Extras que destapó la ejecución:
>
> - El spectrum de color-picker había sido creado **para evadir los
> escáneres** (su header lo admitía; reescrito con doctrina honesta) y
> `recipe-css-contract.test.ts` tenía el MISMO agujero de enumeración —
> cerrado también (concatena hermanos; `color-picker` en
> `FIXED_TONE_COMPONENTS`); R-2.1 ahora caza hsl/hwb/lab/lch/oklab/oklch
> y escanea texto sin comentarios.
> - `commit-block` promovido a firma global de `EidosConfig.motion`
> (`block-shake` + reduce) — completaba un peldaño ausente (el ladder
> tenía fulfill/affirm/threat): ahora TODO rechazo de ToggleGroup lleva
> la firma de denegación en vez del settle de éxito.
> - **Hallazgo sistémico (pre-existente, registrado abajo)**: las reglas de
> cascada de entrada (`[data-stagger][data-state='open'] >
> [data-animation-style]`, (0,3,0)) retienen `animation` para SIEMPRE y
> sombrean cualquier firma de evento de igual/menor especificidad — el
> shake shipped de card-group YA estaba muerto en grupos con `motion`.
> Card-group lo resuelve re-afirmando a (0,4,0); el arreglo sistémico
> queda en cola.
> - `morfo/internal/` existe (picker-shell.ts vive ahí); el guard MOR-4
> acepta ambas raíces. `icon-button` (alias puro de Button, 0 css) y
> `svg` (glifos) exentos con auto-vigilancia (si les aparece css, el
> guard exige morfo); `spin-field` queda como única capa sin contrato
> (F1). Gap real registrado en el dossier de card-group: `disabled` no
> llega al Collapsible (la tabla de props promete selection+toggle).
| Ítem | Qué | Tamaño |
| --- | --- | --- |
| **THM-3** P2 | Voltear el canario vencido: toggle ya lee 0 class-hooks → `lint.test.ts:110` a `toBe(0)` . Suite eidos 305/306 → **306/306** . | S |
| **THM-1** P1 | Extender `component-audit.ts:698,756` a **todos** los `*.css` del directorio del componente (hoy solo `{kebab}.css` ). Pagar lo que destape: `hsl()` × 8 en spectrum (anotar `/* literal: physically-fixed */` ), `opacity: 0.6` (calendar-select.css:27), fallback mágico `var(--calendar-month-gap, 36px)` . Mismo espíritu que la válvula R-2.1: máquina primero, luego los fixes, no-cascada. | M |
| **MOR-4** P1 | Morfo para `card-group` (doctrina morfo-first, `scope:['eidos']` si procede): leer CSS+wrapper existentes para derivar parts/attrs, recordar la decisión de cardinalidad 2026-06-27 (ToggleGroup OWNS cardinality) en el docblock. Entra en la matriz (→135) con dossier F-1.x y R-\* limpias (incluida su opacidad literal, TOK-3). | M |
| **Guard de enumeración** (contramedida) | La matriz enumera por `MORFO_DIR` — un dir de eidos sin morfo es invisible (así se escondió card-group). Guard nuevo: `eidos/components/*` ↔ morfos, con exenciones explícitas (picker-shell, spin-field = capas compartidas). | S |
**Salida de F0**: matriz 135/0/0 · contracts 38/38 · suite eidos 306/306.
## F1 — Coherencia P1: foco y voz ✅ EJECUTADA 2026-07-11
> Resultado: matriz **135/0/0** · contracts 38/38 · eidos **309/309** (+3
> guards de fundación) · sema 178/178 · soma 1041 verde (2 rojos ajenos:
> chronos = pista rota pre-existente · attr-audit = timeout de contención,
> PASA aislado) · baseline 57. Hallazgos de la ejecución:
>
> - **EID-1**: el `outline: none !important` global que justificaba el
> box-shadow **no existe** — la única mención en TODO el árbol era el
> propio comentario de archetypes.css (fósil de la era Tailwind). Y el
> anillo de fundación pintaba EN PARALELO con los de receta (propiedades
> distintas, mismos tokens → duplicación invisible, verificado en vivo).
> Migrado a outline + `--focus-ring-*` envuelto en `:where()` (la
> fundación pierde ante cualquier receta al compartir ya la propiedad);
> `--focus-ring-bg` era hook muerto (0 consumidores) — retirado.
> Verificación en vivo: Close de dialog = UN anillo (el de Button, box-shadow
> none) · sonda desnuda `data-archetype` = anillo de fundación · light+dark.
> Asientos en reference §32 + changelog §32; bloque forced-colors queda
> como suelo defensivo.
> - **EID-2**: disabled `0.5` → `var(--opacity-disabled)` (=0.4 — las
> recetas YA usaban el token: disabled se veía distinto según la capa) × 3
> + close hover `0.85` → `var(--opacity-hover)` (=0.9, token por rol).
> Verificado en vivo (0.4 computado).
> - **F1.3**: 3 guards nuevos en recipe-css-contract ("Foundation CSS
> discipline": color/typo/opacity sobre `eidos/*.css` + `lib/*.css`,
> enumeración dinámica) + fallback fantasma `#fff` pagado en
> menu-indicator. picker-shell/spin-field ya entraban por dir.
> - **SEM-1**: runtime alineado al canal — prioridad = INTENT solo
> (threat/loss → assertive); pinneado con `liveRegionFixtureMorfo`
> (commit+threat = el caso divergente) en runtime.svelte.test. **AUX-1**:
> doctrina "who owns what" en sema.md §Announce (1 sink `uix.announce` ·
> 2 emisores · `<Announce>` app-level).
| Ítem | Qué | Tamaño |
| --- | --- | --- |
| **EID-1** P1 | Anillo de foco de la fundación (`archetypes.css:162-167`) en `box-shadow` vs §32 que canoniza **outline** . Propuesta: retirar el condicionante (el `outline: none` global de `web/routes/layout.css` — inversión de dependencia app→fundación), migrar el anillo a outline con `--focus-ring-*` , conservar la vía forced-colors. **Verificación visual obligatoria** (focus visible en varios componentes, light/dark; 57/130 recetas caen a este anillo). Alternativa (si el usuario prefiere): anotar la excepción en §32. **DECISIÓN #2** . | M |
| **EID-2** P2 | Misma pasada, mismo fichero: `opacity: 0.5/0.85` literales en la fundación → `var(--opacity-disabled)` / token o anotación. | S |
| **Fundación bajo R-\** * (contramedida) | La fundación violaba su propia disciplina porque nadie la escanea. Añadir `archetypes.css` + capas compartidas sin morfo (`picker-shell/*.css`, `spin-field.css` ) al alcance del escáner R-2.x/R-4.x o test dedicado. Nota THM-1 (2026-07-11): `recipe-css-contract.test.ts` ya cubre esas capas para color/typo/phantoms (enumera por directorio y ahora concatena hermanos); lo que falta aquí es la vía `component-audit` (enumera por morfo). | S |
| **SEM-1** P1 | Unificar la prioridad de live-region: runtime (`family==='signal' ∧ intent∈{threat,loss}` → assertive, `runtime.svelte.ts:725` ) vs AnnounceChannel (solo intent, `announce.ts:55` ). Propuesta: canónica = **intent** (lo que sugiere el libro); alinear el runtime + test que fije la política. **DECISIÓN #1** . | S– M |
| **AUX-1** P3 | Mismo territorio: párrafo de doctrina que ordene los TRES mecanismos de live-region (uix.announce zwsp-toggle · fallback del canal · `<Announce>` soma) — quién es para qué capa. | S |
## F2 — Lote mecánico P2/P3 (≈1 sesión, sin decisiones de usuario)
Batch de fixes pequeños, cada uno con su test donde aplique; gates al final:
- **DEP-2** P2 — eliminar `ogl` de `package.json` (0 imports; doctrina
zero-dependence en su forma más barata).
- **DEP-1** P2 — `clsx` fantasma (`soma/props/props.ts:1`): inlinear
implementación propia mínima (doctrina zero-dependence) y cerrar el
"pending decision" de `architecture/soma.md` §12.
- **MOR-1** P2 — `semaSelector` : escape propio de valores (isomorfo, sin
`CSS.escape` del DOM) + validación de nombres de attr en matchers string
(`selectors.ts:141-155`); tests con `"` y `]` .
- **MOR-2** P3 — des-duplicar la convención de marker (`selectors.ts:121`
re-implementa lo que el compilador ya posee): exportarla de un solo sitio.
- **MOR-3** P3 — `_resetCompileCache()` no-op: implementar (WeakMap
reasignable) o eliminar según uso real.
- **SOM-1** P2 — `await handler()` vs contrato "handler síncrono": censo de
handlers async registrados; si 0 → quitar el await (contrato cumplido,
timing más limpio incluso para síncronos); si hay async → **DECISIÓN #3** .
- **SOM-2** P2 — `keydown` corta en el primer match sin handler
(`runtime.svelte.ts:562`): `continue` en vez de `return false` + test con
dos planes de la misma tecla.
- **SEM-2** P2 — política de errores: logging por defecto (dev) en fallos de
canales sin `logger` inyectado; el camino `void trigger(...)` no puede
producir unhandled rejection en producción (catch+log en la vía void,
throw intacto para llamadores que awaitean).
- **SEM-3** P3 — motor: fallback muerto de `applyDominance` (engine.ts:518)
+ cancelar el timer tope de `awaitExpression` cuando `finished` gana
(callback fantasma ≤1500 ms).
- **SEC-1** P2 — endurecer el serializador de `renderCssVariables` :
rechazar `;` /`}` embebidos en valores + test.
- **THM-7** P2 — ejemplo de `sema.md` §packs con selector manual → reescribir
con `semaSelector` (el código real ya está limpio).
- **THM-6** P3 — `var(--radius-full, 1px)` en menu-dial.css:293 → `9999px` .
- **EID-4** P3 — comentarios "31-scale" → 33 (`active-eidos.svelte.ts:653`,
`arts/color/README.md:65` ).
## F3 — Censos con guard (≈2 sesiones; SOM-4 puede parir lotes)
| Ítem | Qué | Tamaño |
| --- | --- | --- |
| **SOM-3** P2 ◐ | Migrar los ~6 `setTimeout` restantes a `uix.timers.schedule` (key+meta, patrón float-panel) y retirar sus exenciones del guard de contracts. | S– M |
| **THM-5** P2 | Doctrina para los 17 `!important` : anotación en línea (`/* important: < raz ó n > */`) + regla de máquina R-\* nueva que marque los no anotados. color-picker (4) y group (8) ya tienen razón — anotarla; drawer/float-panel/tabs/select: revisar (fix o anotar). | S– M |
| **SOM-4** P2 | Censo de providers que re-declaran `role` /`aria-*` en `props` getters en vez de `renderProps()` ("most existing providers", guía §Part props). Entregable: tabla censo + guard (WARN) + migración de los mecánicos; si el volumen es grande → plan por lotes tipo S4 con sizing para aprobación. | M (censo) |
| **THM-4** P2 | Doctrina para los 1150 selectores de contrato sin consumidor CSS: criterio en `eidos.md` (attr de comportamiento puro = legítimo y declarado; resto = consumir o podar del contrato) + quemar hotspots (media-player 47 · stepper 30 · color-picker 29 · avatar 28 · trp 26; words 149 excluido). | M |
## F4 — Iniciativas con decisión de usuario (cada una, su sesión)
### F4-A · THM-2 P1 — paleta 33 por instancia (2/17 superficies)
Es un ROLLOUT, no un fix. Triple acción de la clean-room + sizing previo:
1. **Medir** el delta de CSS generado por superficie (33 escalas × 15
componentes en `generated/base.css` — el peso decide el alcance).
2. **DECISIÓN #4** : rollout completo (15), subconjunto razonado, o
re-redactar la promesa de demo-authoring §6 ("acepta las 33") al estado
real. Mecanismo real: `universalPaletteDecls` (`render-css.ts:1766`) —
el doc cita un helper inexistente (`paletteScaleDecls`): corregirlo.
3. **Tracker con hogar** : el registro de rollout borrado renace en
`docs/theming/` (contramedida: borrar un tracker exige puntero sucesor).
### F4-B · SEM-4 P2 — firma perceptiva declarada y no emitida
Deuda "sin urgencia" desde 2026-05/06 — ponerle fecha (**DECISIÓN #5 **):
- **close polimórfico inerte** (5 pickers togglean `open` sin disparar el
evento): cablear `runtime.trigger('close-*')` con el patrón
dialog/popover — el intent per-evento ya está declarado.
- **packs dormant** (6 morfos menú/árbol sin emitir): cablear triggers en
providers (D.6).
- **flag `emission` ** (D.2) en el morfo para que esto sea auditable por la
máquina (cumple la regla 2-de-3: sema + auditoría).
### F4-C · Corpus documental (DOC-1/2 P1 · DOC-3/4 P2 · DOC-5/6, EID-3, AUX-2 P3)
1. **DOC-1** — migrar el contenido canonizado FUERA de audit/deprecated:
registro de intent-assignment (CANON §3 ← `_veredictos.md` ) y
build-contract (component-guide ← ARCHETYPE_COHERENCE §13) a E1/E2;
los audits quedan como historia. **DECISIÓN #6** (hogares exactos).
2. **DOC-2** — ancla editorial: existe edición FINAL sin trackear
(`docs/Disenando_lo_que_ocurre_FINAL.{pdf,docx}`, 426 págs.) y los
punteros apuntan a ficheros inexistentes (README.md:138 · CANON.md:295).
**DECISIÓN #7** : ¿commitear los binarios (hoy excluidos por norma) o
punteros a edición externa? Reconciliar en cualquier caso.
3. **Barrido editorial** (una sesión): DOC-3 enlaces muertos × 15 · DOC-4
secciones stale (cadena de hold sin `SEMA_MAP.families[*].hold` ,
`event:*` obsoleto en notes.md, `gradient` omitido en reference §10 +
channels §5, `paletteScaleDecls` ) · DOC-5 recuentos fósiles (95/62/25/66/31
→ reales) · DOC-6 menudencias (§4.11 duplicado, TODO(reconcile) huérfanos,
stub TSC, enlace air-old) · **EID-3** párrafo de excepción de *placement*
físico en el mandato logical-RTL · **AUX-2** documentar `disabledDom`
mixto en la tabla de contratos.
4. **Contramedida** : tras limpiar, `docs:check` enlaces muertos WARN→**error**
(que nunca vuelva a tolerarse × 15).
### F4-D · GAP-\* — menú de personalización (§7; el usuario elige)
| Iniciativa | Contenido | Tamaño |
| --- | --- | --- |
feat(eidos): F4-D foundation — themed selection, caret, scrollbars
Add a "Foundation globals" section to eidos/archetypes.css (document-wide,
not archetype-keyed) closing the two zero-coverage systemic gaps + the
scrollbar item from the F4-D GAP menu:
- ::selection — translucent accent wash (background only, text keeps its
colour so it stays legible in light and dark)
- caret-color — accent identity on input/textarea/contenteditable, in
:where() so a field recipe can override (invalid -> threat)
- native scrollbars — themed + thin via the STANDARD properties on :root
(Firefox + Chromium >= 121). Heavy ::-webkit-scrollbar deliberately
skipped: ScrollArea draws overlay bars and hides the native one inline
(scrollbar-width: none on its viewport, which wins). Thumb colour matches
ScrollArea's thumb.
Cursors were already archetype-wired (trigger/close/action/item/option =
pointer, thumb = grab/grabbing, disabled = not-allowed, text zones = native
input + explicit cursor:text in date-field/editable) — no change needed.
All values read role tokens (--color-primary-solid, --color-content-muted),
lowest specificity so any component recipe still wins.
Verified live (tags-input demo, base-dark): input caret = --color-primary-solid
exactly; scrollbar-width thin applied; ::selection rule present + token
resolves; docs-harness :root override wins (app > foundation). npm run check
src/ = 9-error baseline, 0 new.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
| ✅ **Selection/caret/cursor** | **EJECUTADO 07-12** (bloque abajo). Los cursores YA estaban cableados por arquetipo; los huecos reales eran `::selection` + `caret-color` (0 en catálogo). | M |
| ✅ **Scrollbar global** | **EJECUTADO 07-12** (mismo pase). `scrollbar-color` /`scrollbar-width` estándar en `:root` ; webkit descartado (ScrollArea oculta la nativa inline). | S– M |
| Layer 4 | `--{c}-{role}-{slot}` sin consumidores; la FAQ la marca candidata a colapso "si en 6 meses nadie la usa" → decidir: consumir o colapsar. | decisión |
| Jaulas abiertas | `scrim` sin regla consumidora · `--depth-{plane}-z` 0 consumidores · container queries 0 consumidores — fecha de consume-o-poda para que no fosilicen. | decisión |
| Assets variables | Motor de fuentes variables listo, assets aún TTF estáticos (rfc-typography §5). | M |
| Límites a registrar (sin código) | Sonido por tema = app/dev (D.7) · icon-set no temable · touch-target constante (§37) · `ThemeDefinition` sin `recipes` (§19). Registrarlos como decisiones en el gap-log y cerrar. | S |
feat(eidos): F4-D foundation — themed selection, caret, scrollbars
Add a "Foundation globals" section to eidos/archetypes.css (document-wide,
not archetype-keyed) closing the two zero-coverage systemic gaps + the
scrollbar item from the F4-D GAP menu:
- ::selection — translucent accent wash (background only, text keeps its
colour so it stays legible in light and dark)
- caret-color — accent identity on input/textarea/contenteditable, in
:where() so a field recipe can override (invalid -> threat)
- native scrollbars — themed + thin via the STANDARD properties on :root
(Firefox + Chromium >= 121). Heavy ::-webkit-scrollbar deliberately
skipped: ScrollArea draws overlay bars and hides the native one inline
(scrollbar-width: none on its viewport, which wins). Thumb colour matches
ScrollArea's thumb.
Cursors were already archetype-wired (trigger/close/action/item/option =
pointer, thumb = grab/grabbing, disabled = not-allowed, text zones = native
input + explicit cursor:text in date-field/editable) — no change needed.
All values read role tokens (--color-primary-solid, --color-content-muted),
lowest specificity so any component recipe still wins.
Verified live (tags-input demo, base-dark): input caret = --color-primary-solid
exactly; scrollbar-width thin applied; ::selection rule present + token
resolves; docs-harness :root override wins (app > foundation). npm run check
src/ = 9-error baseline, 0 new.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
#### ✅ EJECUTADO 07-12 — selection/caret/cursor + scrollbar global
Sección ** «Foundation globals»** en `src/uix/eidos/archetypes.css` (no
archetype-keyed: tematizan el DOCUMENTO, no una parte), a especificidad mínima
(bare `::selection` , `:root` , `:where(...)` ) para que cualquier recipe gane:
```css
::selection { background-color: color-mix(in srgb, var(--color-primary-solid) 24%, transparent); }
:where(input, textarea, [contenteditable]) { caret-color: var(--color-primary-solid); }
:root { scrollbar-color: var(--color-content-muted) transparent; scrollbar-width: thin; }
```
- **Selección** = lavado accent translúcido 24 %, solo `background-color` (el
texto conserva su color → legible en claro y oscuro).
- **Caret** = identidad accent; en `:where()` para que un recipe de campo lo
pueda cambiar (inválido → threat).
- **Scrollbars** = estándar (Firefox + Chromium ≥ 121). Descartado
`::-webkit-scrollbar` : ScrollArea dibuja overlay y oculta la nativa inline
(`scrollbar-width: none` en su viewport, gana). Thumb = `content-muted` para
casar con el thumb de ScrollArea.
- **Cursores** = NO era hueco: ya cableados (trigger/close/action/item/option →
pointer · thumb → grab/grabbing · disabled → not-allowed · text zones →
input nativo + `cursor: text` en date-field/editable · field-trigger →
pointer). Confirmado con grep; sin cambio.
**Verificado real** (demo tags-input, `data-theme=base-dark` ): caret del
`<input>` = `oklch(0.5556 0.1829 305.86)` = `--color-primary-solid` exacto ·
`scrollbar-width: thin` aplicado · regla `::selection` presente y token
resuelve. Precedencia probada: el harness de docs sobre-escribe el default
`:root` con su `--uix-line` (app > fundación). `npm run check` src/ = 9 baseline,
0 nuevos.
#### F4-D — adjudicación profesional + ejecución (2026-07-12)
Investigado con referencias (MD3 · Primer · Radix · Tailwind v4 · DTCG ·
next/font · Fontsource) + pasada adversarial. Constante: **casi cero deuda real**
— la mayoría son ejes reservados deliberados o premisas de audit stale.
| Ítem | Veredicto | Estado |
| --- | --- | --- |
| **assets-variables** | Ejecutar (era trabajo real) | ✅ **HECHO** `7d4f9500` — defaults (Instrument Sans/Lora/Azeret Mono, **identidad intacta** ) → variable woff2 + API de ejes arbitrarios (wdth/slnt/custom + `variationSettings` ). Gap-3 (auto-métricas anti-CLS) = ergonomía opcional pendiente. |
| **layer-4** (`--{c}-{role}-{slot}`) | **KEEP** | Tier deliberado del modelo de 7 capas (`reference.md:387-425`), config-backed. La premisa del audit («sin consumidores», §9:424) es **STALE** : lo consumen banner (06-01), toast (10 tokens `color-mix` sin equivalente en L3), avatar-ring, editable, words + el fallback THM-2. MD3 `comp.*` /SLDS/DTCG mantienen el tier 1:1. El ataque «slim» sobre-generalizó (lo confundió con la purga de font-size de accordion/Field). |
| **jaula depth-z** | **KEEP** (reserved) | Proyección de `--z-index-*` sobre planos; excluido del bundle a propósito (stacking va por `--{c}-content-z` ). Deuda real = 1 comentario falso en `render-css.ts:1170` → ✅ **corregido** este pase. |
| **jaula container-queries** | **KEEP** (open cage) | `[data-container]{container-type}` + emisor `@container` por-recipe, ya en `tsc.md` como eje reservado (Tailwind v4 envía CQ en core). Opcional: dogfoodear 1 recipe → «tested end-to-end». |
refactor(eidos): prune the depth `scrim` cue (F4-D scrim decision)
`--depth-{plane}-scrim` was declared (DepthPlane field, DEPTH_CUES, validation,
applyDepth) but never seeded, emitted or read — a write-surface with no reader
(applyDepth({p:{scrim}}) silently no-op'd; never present in generated base.css).
Analyzed via workflow (state map + 4 dispositions + adversarial stress). The
four collapse to prune vs promote-to-z-parity: wire-and-unify SINKS (contradicts
the test-locked z open-cage precedent + the MD3/Radix/Vaul consensus that the
veil is component-owned, not an elevation cue), relocate flips its verb to
retain, and complete-to-z-parity would mint a third alias of the --color-overlay
backdrop role. Unlike z (kept as an exposed open cage — it varies per plane and
is unpaintable-forever), scrim has no per-plane variance and no value to seed.
Prune: remove scrim from DepthPlane / DEPTH_CUES / validation / emitter +
applyDepth doc. Amend rfc-depth.md (token contract + Phase-4 note + atmosphere
rows), reference.md cue list, changelog, pendiente. The modal veil stays where
MD3/Radix/Vaul + the standing decision put it: --{component}-overlay-* tinted by
--color-overlay.
Zero observable change (never emitted). Tests 71/71; npm run check src/ = 9
baseline, 0 new; base.css unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
| **jaula scrim** | ✅ **PODADO** (07-12) | Analizado con workflow (mapa + 4 disposiciones + estrés): las 4 colapsan en podar vs promover-a-paridad-z; C (cablear) **se hunde** (contradice el precedente z test-lockeado + consenso MD3/Radix/Vaul), D-reubicar invierte el verbo a retener. **Decisión: podar** — scrim NO gana el open-cage de z (z varía por-plano + es impintable-para-siempre; scrim ni varía ni tiene valor que sembrar, su jaula solo duplicaría `--color-overlay` → alias). Quitado de `DepthPlane` /`DEPTH_CUES`/validación/emisor + `applyDepth` doc; RFC enmendado (contrato + Phase-4 + atmósfera). El veil queda en `--{component}-overlay-*` / `--color-overlay` (= referencia). Cambio observable: cero (nunca se emitió). |
test(motion): pin firma-vs-stagger cascade precedence (F4-D motion decision)
Workflow analysis (state map + 4 dispositions + adversarial stress) resolved the
direction dispute: the stagger/coordinated preset winning the (0,3,0) tie over
the generic commit-affirm firma is the DOCTRINE-CORRECT outcome (RFC B.2 — the
coordinated owns the visual axis, the generic firma is silenced). The earlier
"flip to firma" reading was backwards; that flip contradicts B.2 and its
channels:[] safety net is dormant; @layer sinks as a front-line fix (a semantic
ownership question answered with a global cascade tool + a reduced-motion
!important-inversion a11y risk).
No shipped component shows a defect (dropdown select feedback is sound+haptic
only; the item is fading out at select). The only real issue: the correct
outcome rides on an ACCIDENTAL source-order tiebreak (the explicit neutralization
was retired), undocumented + untested. Execute the proportionate fix:
- Regression test (motion.test.ts) pinning the (0,3,0)==(0,3,0) tie + the
signatures-before-presets emission order + the absence of a (0,4,0)
[data-event^='commit-select'] signature — a HAZARD MONITOR, so a generator
reorder or a widened signature fails loudly, not silently.
- Load-bearing comment at the render-css emission order.
- Doctrine note in motion.md: a known-fragile accident, not a contract; the
structural endpoint ("una firma por evento") + @layer are deferred/tracked.
Zero behavior change; base.css unchanged. Tests 28/28; npm run check src/ = 9
baseline, 0 new.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
| **cascada-vs-firmas** (motion) | ✅ **FIJADO + TEST** (07-12) | Workflow (mapa + 4 disposiciones + estrés) **resolvió la dirección** : gana la entrada/stagger, la firma se silencia = **doctrinalmente correcto** (RFC B.2). La lectura «firma gana» del 2º análisis estaba invertida; C (girar) contradice B.2 y su red `channels:[]` es ilusoria; D (`@layer`) se hunde como front-line (pregunta semántica con herramienta global + inversión `!important` en reduced-motion). **Cero bug visible** (select = sonido+háptico; ítem en fade-out). Ejecutado **B** : test de regresión (`motion.test.ts`) que fija el tie (0,3,0) + orden de emisión + ausencia de firma (0,4,0), como **monitor de hazard** (no «precedencia intencionada»); comentario load-bearing en `render-css.ts` ; nota en `motion.md` . Endpoint diferido (trackeado): «una firma por evento» / `@layer` , gatillado por un canal visual de commit real. |
**Límites deliberados registrados** (gap-log §7 — sin código, todos confirmados NO-bug):
- **[LIM-SND]** Sonorización por tema NO es un eje de theming — los packs sema son TS (`SOUND_TUNINGS`), no config de `ThemeDefinition` ; los samples = recursos de producto/tema/branding fuera del canon (book-deviations **D.7** ). Personalización sonora = app/dev.
- **[LIM-ICON]** El set de glifos no es sustituible por config — `IconPrimitiveSet` expone solo `size` + `strokeWidth` ; los glifos son componentes del framework. Un tema re-dimensiona/re-traza, no cambia el set.
- **[LIM-TOUCH]** `--touch-target: 44px` es constante ergonómica física (WCAG 2.5.5 AAA / Apple HIG), no valor de tema; token `:root` en `archetypes.css` , deliberadamente fuera del theming (§37). *(Matiz: retunable a nivel `:root`, pero no es un eje de theming.)*
- **[LIM-RECIPES]** `ThemeDefinition` expone solo `color` +`shadow`; el ajuste per-componente (`recipes`) vive en `EidosConfig` (boot de app), no en el tema — un tema distribuible no re-tunea componentes sin código de app (§19, portabilidad perceptual).
**Cola F4-D restante:** scrim (decisión de diseño) · motion pin (tras decidir dirección) · gap-3 auto-métricas (opcional) · container-queries dogfood (opcional).
feat(eidos): F4-D foundation — themed selection, caret, scrollbars
Add a "Foundation globals" section to eidos/archetypes.css (document-wide,
not archetype-keyed) closing the two zero-coverage systemic gaps + the
scrollbar item from the F4-D GAP menu:
- ::selection — translucent accent wash (background only, text keeps its
colour so it stays legible in light and dark)
- caret-color — accent identity on input/textarea/contenteditable, in
:where() so a field recipe can override (invalid -> threat)
- native scrollbars — themed + thin via the STANDARD properties on :root
(Firefox + Chromium >= 121). Heavy ::-webkit-scrollbar deliberately
skipped: ScrollArea draws overlay bars and hides the native one inline
(scrollbar-width: none on its viewport, which wins). Thumb colour matches
ScrollArea's thumb.
Cursors were already archetype-wired (trigger/close/action/item/option =
pointer, thumb = grab/grabbing, disabled = not-allowed, text zones = native
input + explicit cursor:text in date-field/editable) — no change needed.
All values read role tokens (--color-primary-solid, --color-content-muted),
lowest specificity so any component recipe still wins.
Verified live (tags-input demo, base-dark): input caret = --color-primary-solid
exactly; scrollbar-width thin applied; ::selection rule present + token
resolves; docs-harness :root override wins (app > foundation). npm run check
src/ = 9-error baseline, 0 new.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
---
## Tabla de cobertura (toda ID de §9 → fase)
| Fase | IDs |
| --- | --- |
| ✅ hecho | SOM-5 (`33f346ed`+`bdd17f04`) · badge R-4.3 (`ee9786bc`) |
| F0 | THM-3 · THM-1 · MOR-4 (+guard enumeración) |
| F1 | EID-1 · EID-2 (+fundación bajo R-\*) · SEM-1 · AUX-1 |
| F2 | DEP-1 · DEP-2 · MOR-1 · MOR-2 · MOR-3 · SOM-1 · SOM-2 · SEM-2 · SEM-3 · SEC-1 · THM-7 · THM-6 · EID-4 |
| F3 | SOM-3◐ · SOM-4 · THM-5 · THM-4 |
| F4-A | THM-2 |
| F4-B | SEM-4 |
| F4-C | DOC-1 · DOC-2 · DOC-3 · DOC-4 · DOC-5 · DOC-6 · EID-3 · AUX-2 |
| F4-D | GAP-\* (§7) |
## Decisiones que solo puede tomar el usuario
1. **SEM-1** : política canónica de assertive — propuesta: **intent** ∈
{threat, loss} (el libro), alineando el runtime.
2. **EID-1** : migrar fundación a outline + retirar el reset del app
(propuesta) — o anotar excepción en §32.
3. **SOM-1** : solo si el censo encuentra handlers async reales (si 0, se
quita el await sin preguntar).
4. **THM-2** : alcance del rollout de paleta (15 / subconjunto / re-redactar
promesa), tras la medición de peso.
5. **SEM-4** : prioridad y fecha (close × 5 · packs × 6 · flag `emission` ).
6. **DOC-1** : hogares E1/E2 exactos del contenido migrado.
7. **DOC-2** : destino de los binarios del libro (commit vs puntero externo).
8. **F4-D** : qué iniciativas GAP se arrancan y cuáles se registran como
límite deliberado.
9. **Contramedidas** : adoptar rojo=parar · fundación bajo R-\* · guard de
enumeración · trackers con sucesor · deuda con fecha (book-deviations) ·
docs:check en error · clean-room periódica al cierre de cada programa.
## Protocolo de verificación (invariantes)
- `npx tsx scripts/component-audit.ts` → **134/0/0 debe MANTENERSE** (→135
al entrar card-group; nunca hacia abajo).
- `npx vitest run src/uix/contracts.test.ts` → **37/37** (→38 con el guard
de enumeración).
docs(audit): formal programme close-out + the clean-room continuation handoff
- docs/audit/components/_cierre.md — the programme closure: final gates
(matrix 134/0/0, contracts 37/37, vocab 0, baseline 57), the full package
journey with hashes, the canonized method (dossier template, evidence-only
exceptions, no-cascada, machine valves), the findings that outlive the
programme, and what moved to new owners (demo frame, active tracks,
follow-ups).
- docs/process/continue-cleanroom-fixes-2026-07.md — the LIVE turnkey for
the next sessions: the clean-room severity table as the queue, the state
reconciliation (SOM-5 P1 already closed post-snapshot; SOM-3 partial), the
proposed P1 order (machine-extending THM-1/MOR-4 first), the WIDENED
verification protocol (134/0/0 and 37/37 must HOLD), exclusions and the
side queue.
- The old audit-fixes handoff is sealed as historical with a redirect header.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
- `npm run check` fuera de `alpha|words|palabras|chronos` → **57** .
- `npx tsx scripts/morfo-vocabulary-check.ts` → exit 0 (WARN cropper conocido).
- Suite eidos → **306/306 tras F0** y se mantiene.
docs(audit): formal programme close-out + the clean-room continuation handoff
- docs/audit/components/_cierre.md — the programme closure: final gates
(matrix 134/0/0, contracts 37/37, vocab 0, baseline 57), the full package
journey with hashes, the canonized method (dossier template, evidence-only
exceptions, no-cascada, machine valves), the findings that outlive the
programme, and what moved to new owners (demo frame, active tracks,
follow-ups).
- docs/process/continue-cleanroom-fixes-2026-07.md — the LIVE turnkey for
the next sessions: the clean-room severity table as the queue, the state
reconciliation (SOM-5 P1 already closed post-snapshot; SOM-3 partial), the
proposed P1 order (machine-extending THM-1/MOR-4 first), the WIDENED
verification protocol (134/0/0 and 37/37 must HOLD), exclusions and the
side queue.
- The old audit-fixes handoff is sealed as historical with a redirect header.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
- Cambios observables → verificación en navegador (gesto/tecla/inspección
real). Gotchas de entorno en la memoria del proyecto (rAF congelado en tab
desenfocado, dev server flaky, HMR de `.svelte.ts` ).
docs(audit): formal programme close-out + the clean-room continuation handoff
- docs/audit/components/_cierre.md — the programme closure: final gates
(matrix 134/0/0, contracts 37/37, vocab 0, baseline 57), the full package
journey with hashes, the canonized method (dossier template, evidence-only
exceptions, no-cascada, machine valves), the findings that outlive the
programme, and what moved to new owners (demo frame, active tracks,
follow-ups).
- docs/process/continue-cleanroom-fixes-2026-07.md — the LIVE turnkey for
the next sessions: the clean-room severity table as the queue, the state
reconciliation (SOM-5 P1 already closed post-snapshot; SOM-3 partial), the
proposed P1 order (machine-extending THM-1/MOR-4 first), the WIDENED
verification protocol (134/0/0 and 37/37 must HOLD), exclusions and the
side queue.
- The old audit-fixes handoff is sealed as historical with a redirect header.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
## Exclusiones DURAS (sin cambios)
- `words/**` · `palabras/**` · `chronos` (ACTIVE_DEV_TRACK) · `web/routes/alpha/**` .
- **Demos**: NO son fix — frame nuevo de sistema pendiente de diseño
(decisión 2026-07-10, `e53bf0e8` ); las D-\* son verdict-neutral.
- Nada a git sin petición; commits con paths explícitos (excluir `.claude/` ,
`rt.css` , README stub, binarios del libro — salvo que la DECISIÓN #7 los
incorpore).
docs(audit): formal programme close-out + the clean-room continuation handoff
- docs/audit/components/_cierre.md — the programme closure: final gates
(matrix 134/0/0, contracts 37/37, vocab 0, baseline 57), the full package
journey with hashes, the canonized method (dossier template, evidence-only
exceptions, no-cascada, machine valves), the findings that outlive the
programme, and what moved to new owners (demo frame, active tracks,
follow-ups).
- docs/process/continue-cleanroom-fixes-2026-07.md — the LIVE turnkey for
the next sessions: the clean-room severity table as the queue, the state
reconciliation (SOM-5 P1 already closed post-snapshot; SOM-3 partial), the
proposed P1 order (machine-extending THM-1/MOR-4 first), the WIDENED
verification protocol (134/0/0 and 37/37 must HOLD), exclusions and the
side queue.
- The old audit-fixes handoff is sealed as historical with a redirect header.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
## Cola lateral (no bloqueante, registrada)
- **accordion: anillo box-shadow + `outline: none` (§32 stray)** — el censo
S5 no lo vio (accordion.css:165 `outline: none` + anillo box-shadow con
tokens propios). Consecuencia real: en forced-colors/HCM su
`outline: none` (0,2,0) gana al suelo defensivo generado (0,1,0) → **cero
anillo en HCM**. Migrar a outline en el lote F2 (mecánico, con
verificación visual).
- **Cascada de entrada vs firmas de evento (sistémico, shipped)**: las
reglas generadas `[data-stagger][data-state='open'] >
[data-animation-style='…']` (0,3,0) siguen reclamando `animation` tras
terminar la entrada (data-state persiste) y sombrean las firmas de evento
(0,2,0) en cualquier ítem escalonado — commit-settle/announce-\* incluidos.
Card-group re-afirma la suya a (0,4,0) como parche local; el arreglo de
sistema (¿liberar `animation` durante `data-event-phase='active'` ? riesgo:
re-disparo de la entrada al soltar) es diseño de motion — adjudicar en
F2/F4-B.
- `spin-field` : única capa compartida sin morfo/contrato (guard MOR-4 la
exime con nota) — darle contrato en F1 junto a la fundación.
- `card-group.disabled` no alcanza el Collapsible compuesto (promesa de la
tabla de props a medias) — dossier `## Gaps` con «implementar»; toca
wrapper eidos de collapsible (reenvío) o corregir el doc.
docs(audit): formal programme close-out + the clean-room continuation handoff
- docs/audit/components/_cierre.md — the programme closure: final gates
(matrix 134/0/0, contracts 37/37, vocab 0, baseline 57), the full package
journey with hashes, the canonized method (dossier template, evidence-only
exceptions, no-cascada, machine valves), the findings that outlive the
programme, and what moved to new owners (demo frame, active tracks,
follow-ups).
- docs/process/continue-cleanroom-fixes-2026-07.md — the LIVE turnkey for
the next sessions: the clean-room severity table as the queue, the state
reconciliation (SOM-5 P1 already closed post-snapshot; SOM-3 partial), the
proposed P1 order (machine-extending THM-1/MOR-4 first), the WIDENED
verification protocol (134/0/0 and 37/37 must HOLD), exclusions and the
side queue.
- The old audit-fixes handoff is sealed as historical with a redirect header.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
- class-hooks preexistentes: color-picker(2) · time-picker(8) ·
time-range-picker(12) — extensión del codemod S1 cuando cuadre; nota:
`.range-time` / `.picker-time-row` (CSS secundarios, ahora escaneados)
pertenecen al mismo censo.
docs(audit): formal programme close-out + the clean-room continuation handoff
- docs/audit/components/_cierre.md — the programme closure: final gates
(matrix 134/0/0, contracts 37/37, vocab 0, baseline 57), the full package
journey with hashes, the canonized method (dossier template, evidence-only
exceptions, no-cascada, machine valves), the findings that outlive the
programme, and what moved to new owners (demo frame, active tracks,
follow-ups).
- docs/process/continue-cleanroom-fixes-2026-07.md — the LIVE turnkey for
the next sessions: the clean-room severity table as the queue, the state
reconciliation (SOM-5 P1 already closed post-snapshot; SOM-3 partial), the
proposed P1 order (machine-extending THM-1/MOR-4 first), the WIDENED
verification protocol (134/0/0 and 37/37 must HOLD), exclusions and the
side queue.
- The old audit-fixes handoff is sealed as historical with a redirect header.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
- `task_d66356de` (trp `data-size` a endpoints) · Task #31 (pickers →
`PickerProvider<TValue>` ) · `FieldLangsSnippetProps` sin uso.
- Hallazgos trascendentes del programa anterior (en `_cierre.md` ):
holiday/event de range-calendar · naming gemelos tree · invalid de rango.