# CONTINUE — lectura del corpus doctrinal Registro de lectura. Existe porque la lectura del corpus **no cabe en una sola ventana de contexto** (~19.000 líneas) y sin este fichero cada sesión la reempieza o se la salta — que es exactamente cómo se auditó a ciegas el 2026-07-29/30 y hubo que revertir 7 commits (`3097cfcb6`). No es fuente de verdad. Es un marcador de posición. --- ## Empieza aquí (arranque en frío) **Dónde está.** 29 documentos leídos sobre el original (≈14.650 L), en cuatro sesiones el 2026-07-30. COMPLETOS: el núcleo de arquitectura, el canon, `theming/reference`, **las tres guías** (component-guide · component-audit · demo-authoring), **`book-deviations`** y **todo theming**. Ningún documento queda contaminado. **Lo primero que hay que saber si entras en frío.** El inventario que este fichero traía era **incompleto**: contaba «30 documentos / 19.000 L», y el corpus que el propio mapa ([`docs/README.md`](../README.md)) declara son **59 ficheros / 27.348 L** (excluidos `process/`, `old-deprecated/` y los 139 dossiers de `audit/components/`). Faltaban documentos que el corpus llama vinculantes — ver hallazgo **nº 24**. El inventario de abajo ya está corregido. **Qué hacer ahora.** Seguir leyendo, por este orden: 1. Los 7 RFCs (1860) — la última tanda del plan original. 2. **`spec/delegation-contract.md`** (502) — **NORMATIVO** (RFC-2119, ids `AG-n`). Nunca estuvo en el inventario. 3. `book-map` (201) · `authoring` (136) · `decisions.md` (88) · `building-a-component` (55). 4. **`theming/changelog.md`** (1879) — el documento sin leer más grande del corpus; es la crónica fechada detrás de las decisiones de `reference`. 5. El resto nunca inventariado: `getting-started` (107) · `comparison` (95) · `next-features` (287) · `decisions/design-text-effects` (145). Al acabar cada tanda: actualizar el inventario de abajo y los hallazgos. **Las tres reglas que no se saltan.** - **No escribir código ni doctrina hasta acabar la lectura.** Quedan ≈5.355 L del corpus mapeado (más los 4 registros de diseño que indexa `decisions.md`, 4.574 L, cuyo estatuto hay que decidir al leer `decisions.md`). - **Un hallazgo doc↔código se verifica CONTRA EL CÓDIGO.** Si sólo se sostiene citando otro documento, no está verificado — así se fabricaron los tres hallazgos falsos que costaron el revert. - **Paso 0 antes de citar**: comprobar que la línea no la escribió una sesión previa (`git log -L n,n+1:fichero`). Vale también **para el código**: el hallazgo nº 28 se sostuvo comprobando que `announce.ts` lo escribió trabajo real (`53b6f629f`), no una sesión de agente. **Lo único que espera decisión tuya.** Sigue siendo el hallazgo **nº 14** (enum cerrado del morfo vs. `color` abierto a 42), ahora con la población exacta acotada: son **12 morfos**, no 46 — ver nº 37. Todo lo demás es deriva doc↔código con arreglo evidente, listado en «Lo que NO se ha hecho». **Estado del árbol**: `npm run docs:check` da **2** errores (no 1), ambos la misma enfermedad — `says "8 roles" but COLOR_ROLES.length is 9`: - `src/uix/eidos/components/callout/README.md:23` — daño colateral del revert (hallazgo nº 21). - `src/uix/blocks/banner/README.md:15` — **nuevo, y sin trackear**: trabajo en curso del tier blocks (F2.11). No lo introdujo esta lectura y **no se toca** (es trabajo ajeno en vuelo). Lo que sí enseña: el conteo hardcodeado no es un fósil que se esté extinguiendo, se está **reproduciendo en obra nueva** — cuarta aparición junto a los nº 21, 22/27 y 34. --- ## La regla que lo motiva La instrucción del usuario fue: «audita el sistema, **para ello previamente lee toda la documentación**». Es una **precondición bloqueante**, no contexto opcional. Incumplirla produjo tres hallazgos falsos, un commit dedicado a retirarlos, y preguntas al usuario cuya respuesta estaba escrita. El orden lo fija el propio corpus en [`docs/README.md`](../README.md) §«Reading order for a fresh start»: `overview` → `active-architecture` → `CANON` → el capítulo de la capa que se toque. ## ⚠️ Contaminación: 7 documentos leídos en estado editado — **RESUELTA 2026-07-30** > **Cerrada.** Los 12 documentos marcados `RELEER` se releyeron sobre el > original. La limpieza del árbol se verificó **antes** de leer, no se supuso: > `git diff c39170abb^ HEAD -- docs/CANON.md docs/architecture/ docs/glossary.md > docs/guides/completion-checklist.md docs/testing-and-tooling.md docs/theming/` > devuelve **vacío** → lo que hay en HEAD es byte a byte el estado pre-auditoría. > Esa comprobación es el paso 0 de cualquier relectura futura. **Lo primero que hay que saber.** El commit revertido `c39170abb` editaba 12 documentos. La lectura del 2026-07-30 se hizo sobre el árbol **con esas ediciones dentro**, así que parte de lo que se leyó como «doctrina» era la escritura del propio agente que luego se revirtió. Consecuencia: **esos 7 hay que releerlos en su estado actual (original).** ~~Pendiente~~ — **hecho el 2026-07-30**; lo que sigue se conserva porque la lección vale más que el incidente. Dos ejemplos medidos de cómo engañó: - `testing-and-tooling.md` — el original dice *«a guard fails when an active provider ships without one»*. La edición lo había cambiado a *«This is a convention, not a guard — the mechanical check does not exist»*. Se citó como doctrina para justificar una decisión de arnés de tests. **Era autocita.** - `overview.md` — el original documenta `provider.commitState()` / `provider.emitEvent()` («Three Soma scenarios»). La edición lo sustituyó por «One door: runtime.trigger» + la frase *«neither has ever existed in the code»*. Con eso se «descubrió» una contradicción contra `soma-architecture.md` que **la propia edición había creado**. Tras el revert ambos documentos coinciden. Lección para quien siga: **antes de citar un documento como doctrina, comprobar que la línea citada no la escribió una sesión previa.** `git log -p -- ` sobre el párrafo en cuestión. ## Inventario Estado: `LIMPIO` = leído sobre el original · `RELEER` = leído contaminado · `—` = sin empezar. **Hoy no queda ninguno en `RELEER`.** ### Núcleo de arquitectura | Doc | Líneas | Estado | | --- | --- | --- | | `README.md` (el mapa) | 155 | LIMPIO | | `architecture/sema.md` | ~~1024~~ **1051** | ⚠️ LIMPIO pero **CADUCA** — ver nº 25 | | `architecture/eidos.md` | 1055 | LIMPIO — leída completa 2026-07-30 | | `architecture/overview.md` | 503 | LIMPIO — releída completa 2026-07-30 | | `architecture/active-architecture.md` | 837 | LIMPIO — releída completa 2026-07-30 | | `architecture/active-uix.md` | 115 | LIMPIO — releída completa 2026-07-30 | | `architecture/soma.md` | 471 | LIMPIO — releída completa 2026-07-30 | | `architecture/soma-architecture.md` | 1034 | LIMPIO — releída **completa** 2026-07-30 | | `testing-and-tooling.md` | 110 | LIMPIO — releída completa 2026-07-30 | | `CANON.md` | 305 | LIMPIO — releída completa 2026-07-30 | | `architecture/morfo.md` | 888 | LIMPIO — leída completa 2026-07-30 | | `architecture/active-app.md` | 351 | LIMPIO — leída completa 2026-07-30 | | `architecture/agent.md` | 472 | LIMPIO — leída completa 2026-07-30 | | `architecture/packs.md` | 86 | LIMPIO — releída completa 2026-07-30 | | `architecture/blocks.md` | 113 | LIMPIO — leída completa 2026-07-30 | | **`spec/delegation-contract.md`** | 502 | — **NORMATIVO, nunca inventariado** (nº 24) | > `morfo.md` era la prioridad y ya está leída (era la capa del contrato, la más > tocada durante la auditoría fallida, y se llegó a escribir criterio en su > §Step 2 sin haberla leído nunca). Lo que salió, en «Hallazgos». ### Canon | Doc | Líneas | Estado | | --- | --- | --- | | `canon/tsc.md` (Token Scope Contract) | 329 | LIMPIO — leída completa 2026-07-30 | | `canon/vocabularies.md` (conjuntos cerrados, generados) | 153 | LIMPIO — leída completa 2026-07-30 | | `canon/recipe-contract.md` | 184 | LIMPIO — leída completa 2026-07-30 | ### Theming y motion | Doc | Líneas | Estado | | --- | --- | --- | | `theming/reference.md` | 1822 | LIMPIO — leída **completa** 2026-07-30 | | `theming/motion.md` | 791 | LIMPIO — releída completa 2026-07-30 | | `theming/gradient-finish.md` | 475 | LIMPIO — leída completa 2026-07-30 (s.4) | | `theming/guide.md` | 241 | LIMPIO — leída completa 2026-07-30 (s.4) | | `theming/motion-guide.md` | 232 | LIMPIO — releída completa 2026-07-30 | | `theming/notes.md` | 179 | LIMPIO — leída completa 2026-07-30 (s.4) | | `theming/channels.md` | 143 | LIMPIO — leída completa 2026-07-30 (s.4) | | **`theming/changelog.md`** | 1879 | — **nunca inventariado** (nº 24) | **Theming queda COMPLETO salvo `changelog.md`**, que el mapa sitúa en E3 y nadie había contado. ### Guías y decisiones | Doc | Líneas | Estado | | --- | --- | --- | | `guides/component-guide.md` (pasos + reglas A1–A37) | 1568 | LIMPIO — leída completa 2026-07-30 (s.4) | | `decisions/book-deviations.md` | 851 | LIMPIO — leída completa 2026-07-30 (s.4) | | `guides/component-audit.md` | 369 | LIMPIO — leída completa 2026-07-30 (s.4) | | `guides/completion-checklist.md` | 337 | LIMPIO — releída completa 2026-07-30 | | **`guides/demo-authoring.md`** | 266 | LIMPIO — leída completa 2026-07-30 (s.4); **nunca inventariada** pese a ser LOCKED (nº 24) | | `book-map.md` | 201 | — | | `authoring.md` | 136 | — | | `glossary.md` | 89 | LIMPIO — releída completa 2026-07-30 | | `decisions.md` | 88 | — | | `building-a-component.md` | 55 | — | | `rfcs/*` (7 ficheros) | 1860 | — | ### Nunca inventariado (descubierto en la sesión 4 — nº 24) | Doc | Líneas | Estrato según el mapa | Estado | | --- | --- | --- | --- | | `theming/changelog.md` | 1879 | E3 | — | | `spec/delegation-contract.md` | 502 | E1 — **NORMATIVO** | — | | `next-features.md` | 287 | E3 | — | | `guides/demo-authoring.md` | 266 | E4 — **LOCKED** | LIMPIO (s.4) | | `decisions/design-text-effects.md` | 145 | E3 | — | | `getting-started.md` | 107 | puerta de entrada | — | | `comparison.md` | 95 | «I want to…» | — | | `audit/components/_{veredictos,naming,system,cierre}.md` | 412 | registro, pero **citados como canon** (S4/S7/S10, N1–N10) | — | Fuera del mapa, indexados por `decisions.md`: `design-connection` (1987) · `design-timer` (1540) · `design-session` (857) · `design-chat-block` (190). Su estatuto se decide al leer `decisions.md`. **Total real del corpus: 27.348 líneas en 59 ficheros** (sin `process/`, `old-deprecated/` ni los 139 dossiers por componente). **Leídas sobre el original: 29 documentos (≈ 14.650 L)** — núcleo de arquitectura, canon, `theming/reference`, theming completo salvo `changelog`, las tres guías y `book-deviations`. Quedan ≈ 5.355 L del corpus mapeado. ## Hallazgos que SÍ se sostienen Salen de documentos limpios y verificados como originales: 1. **«Only composition roots create shared services»** — `architecture/active-uix.md` §Ownership, regla 1: *«`morfo`, `soma`, `sema`, `eidos` and components never create `dom`, `langs`, `prefs`, `format`, `clipboard` or equivalents: they receive them from `ActiveUix`»*. `eidos.md` la repite (*«ActiveEidos creates no shared services»*). Las dos puertas son `createActiveUix` / `attachActiveUix`; el arranque de un shell son tres líneas (`createActiveUix` → `setActiveUix` → `Soma.create()`). ✅ **Confirmado 2026-07-30**: el fichero se releyó sobre el original y el párrafo es literal (`active-uix.md` líneas 61–63). La advertencia de RELEER que había aquí queda retirada. 2. **Overlays: `sequence: 'post'`, no `'pre'`** — `sema.md` (LIMPIO), nota destacada: un evento de aparición cuyo provider fija `open` en el HANDLER debe declarar `'post'`; con `'pre'` el hold de ~240 ms gatea el montaje. 3. **`intentRationale`** — `sema.md` (LIMPIO), `BK-FRAME-NO-INTENT`: una familia estructural (`contact`/`emerge`/`shift`/`sustain`/`delegate`) que declare intent no neutro debe justificarlo en `MorfoEventSemantic.intentRationale` o `validateMorfo` la rechaza. `handle` está exento. 4. **El scheduler de los holds** — `sema.md` (LIMPIO): los holds van por `uix.timers.schedule` (`semaDelay`, `sema/timers.ts`), y sólo caen a `setTimeout` *«when a channel is built without a scheduler (direct unit tests)»*. Matiza la regla 1 para tests unitarios; no la anula. ### De `morfo.md` (leída completa, original) 5. **§Step 2 omite `'private'` — CONFIRMADO contra el original.** Las líneas 397–399 listan sólo `'public'` y `'virtual'`; la lista de referencia (líneas 55–59) sí documenta los tres `kind`. El autor decide en §Step 2, así que el hueco es real. ⚠️ Lo que estaba MAL era el criterio que la sesión revertida escribió ahí como arreglo («el test es mecánico: debe existir ``»), que clasifica mal a todo primitivo de API plana. El hueco sigue abierto; el arreglo no está decidido. 6. **`emit: 'value'` es legítimo** (línea 436): los `data-*` no-enum van por defecto a `emit: 'presence'`; `emit: 'value'` es para atributos que llevan un valor real (`data-value`, `data-min`, `data-max`). 7. ⚠️ **Y una regla que CUESTIONA la conclusión de la sesión revertida sobre `data-color`** (línea 878, «Common pitfalls»): *«**Provider emits a data-attr not in the morfo.** Strict mode logs a warning at runtime; morfo-check fails in CI. Either add the attr to the morfo or rename the provider's emission to `data-_*`»*. Más el incidente 2026-05-20 (línea 868): *«A component is not done if the provider or demo emits required `data-*` that Morfo does not declare»*. La sesión revertida concluyó que `data-color` NO debe ir en el morfo porque su vocabulario es de eidos; esta regla dice que lo emitido debe declararse. La distinción a resolver es si un attr estampado por el WRAPPER de eidos (no por el provider de soma) entra en la regla — y eso exige leer `eidos.md` completo y `theming/reference`. ~~NO resolver antes.~~ → **RESUELTO en el nº 13**: el attr lo estampa el wrapper PERO su prop cruza la frontera de soma, así que va al morfo. 8. **`data-_*`** es el prefijo reservado para attrs privados, deliberadamente fuera del morfo; `validateMorfo()` los RECHAZA en una declaración. ### De `eidos.md` (limpia, leída hasta la 750) 9. **Existe una categoría SANCIONADA de data-attrs visuales fuera del morfo.** §«From Soma», línea 489: *«The wrapper adds the visual token data-attrs (`data-variant`, `data-size`, `data-block`, `data-icon-only`) and does not reimplement state»*. Y la tabla §«From morfo» (líneas 451–460) enumera lo que eidos consume del morfo — `parts[].kebab`, `archetype`, `states` + `data[].values`, `data-starting-style`/`data-ending-style`, `events[].name`, `semantic.family`/`.intent`, `prewrite[]`, `focus.trap`: **`data-color` NO aparece**. → Esto es la mitad de la respuesta a la pregunta aparcada (nº 7): la regla de `morfo.md` línea 878 («lo que el PROVIDER emite, se declara») convive con una categoría de attrs que estampa el WRAPPER y que no van al morfo. `data-size` es el precedente concreto y verificable (`switch.css` lo usa en 4 reglas y no está en el morfo de switch). → **Falta la otra mitad**: si `data-color` pertenece a esa categoría. La lista de la línea 489 es ilustrativa, no exhaustiva, y no lo incluye. La respuesta está en **`theming/reference.md` §25** («Color model: palette + roles + intents»), que el propio `eidos.md` señala como canon del color (línea 589). ~~NO resolver antes de leer §25.~~ → **RESUELTO en el nº 13** (§25 leída 2026-07-30; la respuesta estaba además en §1.bis y §39). 10. **`--*` es namespace de eidos** (§«The `--*` token rule»): *«The upper layers (sema, soma, morfo) do NOT consume these tokens and do not use the prefix»*. Es sobre custom properties, no sobre data-attrs, pero fija el principio de propiedad. 11. **`eidos-only` es una clasificación VÁLIDA, no un error** (§«Selector-drift defense», líneas 850–858). `eidos-lint` clasifica cada selector en *morfo-backed* / *eidos-only* / *invalid*, y define **eidos-only** como: *«the marker is there, but at least one `data-*` is not declared in the morfo. **Valid by convention** (visual tokens like `data-variant`, `data-size` come from the wrapper)»*. Sólo *invalid* (attr declarado con valor fuera del enum) es bug. 12. **THM-4 (líneas 865–887) da la disposición para el caso inverso**: un attr *«declared FOR a visual axis and that nothing anywhere consumes — no recipe, no shared layer, no sibling, no behavioral reason»* es deuda, y la disposición es *«consume it or **prune it from the morfo** (never leave "declared for styling, styled nowhere")»*. → Las dos piezas juntas apuntan a que un `data-color` estampado por el wrapper y ausente del morfo es **legítimo** (eidos-only), y que un `data-color` declarado en el morfo sólo por un eje visual es podable. Pero **sigue faltando `theming/reference.md` §25**, que `eidos.md` designa como el canon del color: ~~no dar la pregunta por cerrada hasta leerla~~ → **CERRADA en el nº 13**. ### De la relectura de los 12 contaminados (2026-07-30, sesión 2) Todos verificados dos veces: documento + línea sobre el original, y **contra el código**, que es la única autoridad que no puede haberla escrito un agente. 13. **PREGUNTA APARCADA (nº 7 / 9 / 12) — CERRADA: `data-color` SÍ va en el morfo.** Tres evidencias independientes, ninguna autocita: - `architecture/active-architecture.md` línea 565, tabla §6 «DOM attributes — the universal channel»: `data-color="primary"` · escribe **`dom.apply` (effect)** · lee **Eidos**. Es la misma casilla que `data-state` y `data-intent`, NO la de los attrs del wrapper. Provenance comprobada con `git log -L 565,566`: entró con la traducción `8668e117b` desde el doc castellano, y ahí lo escribió `d4dfdaffa` — muy anterior a cualquier sesión de agente. - `guides/completion-checklist.md` **E-3.5**: *«All visual props (`size`, `variant`, `color`, `radius`) map to `data-{prop}="value"` on the root»*; más **R-3.1**, **R-3.3** (*«`data-color` reflects the intent»*) y **G-1.2/G-1.3** (el subset se declara y el recipe sólo casa el declarado). - El código: **12 morfos declaran `data-color`** (avatar, backdrop, button, callout, card, card-group, chronos, dialog, metrics, surface, switch, toggle), como entrada `data` real — `attr: 'data-color', values: [...], value: v.propRef('color')`. → La conclusión de la sesión revertida (`f8e35b8fd`, «el morfo no puede nombrar lo que es de eidos») era **falsa**. La categoría wrapper-only del hallazgo nº 9 existe (`data-variant`, `data-size`, …) pero `data-color` no pertenece a ella: viene del prop y viaja por el morfo. **AMPLIACIÓN (lectura de `theming/reference.md`, misma sesión).** El corpus ya tenía la respuesta escrita, dos veces, y con más autoridad de la que yo reuní: - **`reference.md` §1.bis** existe LITERALMENTE para impedir este error. Se titula *«Theming lives in Eidos, not in Morfo (by design)»* y se abre con *«This is the most frequent architectural question»*. Su §«What SHOULD enter morfo regarding theming» (líneas 282–291) es inequívoco: *«A component exposing `color` as a prop → **must declare `data-color.values: ['primary', 'affirm', ...]` in its morfo**»*, y en la misma lista, *«purely visual attrs (`data-variant`, `data-size`) that NOBODY else needs → they do NOT go in morfo»*. La tabla de la línea 248 asigna `data-color.values` a **Morfo**, «cross-layer: soma validates, eidos targets, sema references». La sesión revertida hizo justo lo que esta sección enumera como propuesta a rechazar. - **`reference.md` §39** (líneas 1750–1757, 2026-07-15 — la doctrina más reciente) da el **criterio mecánico** que faltaba, y el porqué: *«`data-gradient` is an eidos-only WRAPPER attr … **Rule of thumb: morfo declares an attr only when its driving prop crosses the soma boundary**»*, con el modo de fallo medido: si declaras en morfo un attr cuyo prop no llega a soma, el runtime emite `undefined` y `mergeProps(restProps, state.props)` pisa el sello del wrapper. → Con eso la frontera queda nítida y comprobable: `color` **cruza** la frontera de soma (los 12 morfos lo resuelven con `v.propRef('color')`) → va al morfo. `variant` / `size` / `gradient` no la cruzan → los sella el wrapper. No es una convención: es de dónde se resuelve el valor. 14. **Y la pregunta REAL que había debajo: el enum del morfo es más estrecho que el prop que se envía.** `theming/reference.md` §25, líneas 1496–1507 (decisión de diseño 2026-07-18, «reversión de los subconjuntos»): *«`color` acepta el sistema completo — rol / intent / 33 escalas donantes / valor CSS crudo (`ComponentColorProp`) — en TODOS los componentes, sin excepciones»*, con guard estructural en `recipe-css-contract.test.ts`. El tipo es abierto: `ComponentColorProp = ComponentColor | (string & {})` (`eidos/lib/types.ts:216`). Pero el morfo de `switch` declara `data-color` con **6 valores cerrados** (`['primary','secondary','neutral','affirm','risk','threat']`), y `scripts/morfo-check.ts:167` falla cuando un attr con `values` emite algo fuera del conjunto. Esa es la contradicción que merece decisión del usuario, no la de si `data-color` va en el morfo. **CADENA COMPLETA, verificada extremo a extremo (misma sesión).** No es hipotética — seguí el valor desde el prop hasta el DOM: 1. `eidos/lib/component-color.ts` → `CANONICAL_COLOR` = `COLOR_ROLES` **∪ `PALETTE_SCALES`** = **42 nombres**. `resolveComponentColor('teal')` devuelve `dataColor: 'teal'` (sólo un color CSS **crudo** se desvía a `data-color-custom` + `--color-custom`). 2. `eidos/components/switch/switch.svelte:42` pasa ese `dataColor` al prop `color` de soma. 3. `soma/components/switch/switch-provider.svelte.ts:88–94` lo devuelve **verbatim** (sólo intercepta el intent evaluativo y el caso custom). 4. El morfo declara `data-color` con **6** valores. → `` emite `data-color="teal"`, fuera del enum. Y no es un caso aislado — enums declarados hoy: **avatar 8 · button 8 · card 8 · switch 6 · toggle 6 · dialog 3**, contra los 42 que el wrapper puede sellar. Los seis pueden emitir fuera de contrato **por diseño**. **NO verificado** si hoy salta en CI: `morfo:check` sólo ve lo que las demos renderizan de hecho, y no lo he ejecutado (necesita dev server). El agujero es que el guard de «lo declarado se cumple» depende de que una demo elija el valor infractor — justo en el eje que la decisión de 2026-07-18 abrió a 42. 15. **`provider.commitState()` / `provider.emitEvent()` no existen en el código.** `grep -rn "commitState\|emitEvent"` sobre todo el repo (`.ts`, `.svelte`, `.js`, sin `node_modules`) → **cero coincidencias**. Los documentan dos docs, en originales verificados: `architecture/overview.md` líneas 320–334 («Three Soma scenarios») y `architecture/soma-architecture.md` líneas 269–293 («Three operations covering every scenario», con el cuerpo `async commitState(change, event?)` escrito entero). → El *desmentido* de la sesión revertida era **cierto sobre el código**; lo inválido fue el método (lo «descubrió» citando su propia edición). Y la retirada del hallazgo fue correcta en lo suyo: la *contradicción entre los dos docs* sí era manufacturada — los dos dicen lo mismo. La deriva es **doc↔código, y afecta a los dos por igual**. 16. **El guard de tests de provider NO existe** — la pregunta que quedó abierta, ahora cerrada por la puerta buena. `NO_MISSING_PROVIDER_TESTS` aparece en **dos ficheros y los dos son docs** (`soma-architecture.md` y `docs/old-deprecated/fable_audit.md`); no hay comprobación de existencia de `*.test.ts` en `scripts/`, no hay invariante de cobertura en `src/uix/contracts.test.ts` (sus guards son VG-8, SYS-1, MOR-4, A30, A31, THEME-SYS-1 y ~30 más, ninguno de cobertura), y no hay script npm que lo haga. Lo afirman `soma-architecture.md` §6 líneas 495–497 (*«the guard returns `NO_MISSING_PROVIDER_TESTS`»*) y `testing-and-tooling.md` líneas 43–46 (*«a guard fails when an active provider ships without one»*). Ambas frases son originales y ambas son **falsas contra el código**. 17. **`clsx`: dos originales se contradicen, y el código ya resolvió.** `architecture/soma.md` §3 líneas 103–107 dice que era fantasma y se inlineó el 2026-07-11 (DEP-1) como `toClassString`, import fuera. `soma-architecture.md` §12 línea 850 sigue diciendo *«`clsx` is imported in `props/props.ts` without being declared … a debt pending decision»*, y §8.bis línea 713 *«`class` → merged with clsx»*. El código (`src/uix/soma/props/props.ts:49–52`) confirma la versión de `soma.md`: el flattener propio, sin import. `soma-architecture.md` está **stale**. 18. **`packs.md` y `glossary.md` describen un futuro que ya ocurrió.** `packs.md` líneas 78–79: *«When `Aura` lands, the scene runtime is promoted to a `uix.scene` service; until then it stays a standalone factory»*; `glossary.md` línea 34: Aura *«Not built yet»*. En el código: `uix.scene` **es servicio** (`active-uix/active-uix.svelte.ts:152–178`, `createEngineScene` + lectura de `app.scene` en attach) y **Aura existe en las tres capas** (`morfo/components/aura.ts`, `soma/components/aura`, `eidos/components/aura`). 19. **`CANON.md` apunta a un libro que no está.** El frontmatter (líneas 7–9) declara `sources.book: docs/Disenando_lo_que_ocurre_HOMOGENEIZADO.pdf`; ese fichero **no existe en el repo** (`find -iname "*HOMOGENEIZADO*"` → vacío). El §Sources del mismo documento (línea 296) cita `Disenando_lo_que_ocurre_FINAL.pdf`, que **sí existe**. El puntero roto es el del frontmatter, y lo escribió `53b6f629f` (commit de trabajo real, no una sesión de auditoría). 20. **`package.json` conserva el entry `generate:contracts-docs`** y el script `scripts/generate-contracts-docs.ts` ya no existe. `testing-and-tooling.md` líneas 76–77 documenta el borrado; el entry huérfano sigue. Ya estaba fichado como pendiente menor en [`CONTINUE-docs-corpus.md`](./CONTINUE-docs-corpus.md) §Fósiles. 21. **`npm run docs:check` lleva en rojo desde el revert, y es daño colateral del propio revert.** Salida actual en HEAD: `ERROR [I1-count] src/uix/eidos/components/callout/README.md:23 — says "8 roles" but COLOR_ROLES.length is 9`. El diff lo explica solo: `c39170abb` **arreglaba** esa línea (`the 8 roles` → `the canonical color roles`, o sea aplicaba la ley «enlaza el const, no copies el número») y `3097cfcb6` la revirtió con todo lo demás. → El revert fue correcto en su intención pero deshizo también **arreglos buenos**; conviene revisar su diff buscando más casos antes de dar por saneado el árbol. Fix pendiente: una línea, sin decidir (no lo toco: regla 5). ### De la tanda canon + núcleo restante + theming (2026-07-30, sesión 3) 22. **`active-app.md` enumera a mano un catálogo que ya driftó.** Su tabla §«Available services» lista **12** factories y el árbol §«Filesystem layout» los repite uno a uno. En `git ls-files src/arts/active-app/service-factories/` hay **15**: faltan **`agent.ts`, `motion.ts` y `scene.ts`** en ambos sitios. Es la misma enfermedad que el «8 roles» del nº 21 y que la ley que el propio corpus escribió (`authoring.md`: catálogos hardcoded → puntero). El árbol es la fuente; la lista es el problema. 23. **`canon/tsc.md` anuncia un scope que dos docs declaran superado.** Su tabla «Available scopes» lista `event:${v}` → *«Motion token bound to a perceptual signal»* sin nota alguna. Pero `reference.md` §13 se titula *«Sema integration (`event:*` scope)»* y su cuerpo entero es **«⚠️ Superseded»**, y `motion.md` (líneas 46 y 699) lo da por **descartado, «no real use»**. Comprobado: el tipo SIGUE existiendo (`eidos/lib/config-types.ts:471,499`) y **cero recetas lo consumen**. O sea que las tres frases son literalmente ciertas — pero quien construya desde el canon (que es para lo que está el canon) lo leerá como disponible. Severidad baja; coste de arreglo, una nota. ### De las guías + `book-deviations` + theming (2026-07-30, sesión 4) Todos con los dos anclajes: documento + línea sobre el original, y comando contra el código. 24. **El inventario de este mismo fichero estaba incompleto — y faltaban dos documentos que el corpus declara vinculantes.** Censo de `git ls-files` sobre `docs/` (sin `process/`, `old-deprecated/` ni los 139 dossiers): **59 ficheros, 27.348 L**, contra las «30 / 19.000» que se venían contando. Los ausentes no eran marginales: - **`guides/demo-authoring.md`** (266) — `component-audit.md` §0 la manda leer *«cada vez»*, la incluye en el «paquete mínimo» de todo brief (línea 44: *«el template LOCKED de demo, D-1.x es error-level»*) y la cita 7 veces como fuente de las reglas de paridad. - **`spec/delegation-contract.md`** (502) — el mapa la rotula **NORMATIVE** (RFC-2119, ids estables `AG-n`). - **`theming/changelog.md`** (1879) — el mayor documento del corpus sin leer. → Leída ya `demo-authoring` en esta sesión. El inventario de arriba queda corregido; el arranque en frío ya no manda leer un corpus truncado. 25. **`sema.md` se leyó en un estado que hoy no existe.** El registro lo daba LIMPIO a 1024 L; el fichero tiene **1051** y sigue limpio en el árbol. Lo cambió **hoy** `6920684d0` (*«extraer el motor Web Audio de sema al art `$sound`»*), que reescribió su §SoundChannel entera —el título pasó a *«SoundChannel — doctrine here, machine in `$sound`»*— y añadió el porqué del corte. No es contaminación de agente (commit de trabajo real): es **caducidad**, el otro modo en que una lectura deja de valer. Releer esa sección antes de citar sema en materia de sonido. 26. **La tabla de alias de `CLAUDE.md:96` no incluye `$sound`.** El alias existe en la fuente de verdad (`vite.config.ts:35`) y en `svelte.config.js:41` desde el mismo commit de hoy. La tabla enumera 24 artes a mano; van 25. 27. **Amplía el nº 22 — `active-app.md` documenta 12 de 16 factories.** `git ls-files src/arts/active-app/service-factories/` (sin `index.ts`) da **16**; faltan en la tabla §«Available services» y en el árbol §«Filesystem layout»: `agent`, `motion`, `scene` y —desde hoy— `sound`. `src/arts/README.md` **sí** se actualizó en el mismo pase; `active-app.md` no. Es el argumento de la ley del propio `authoring.md`: catálogo hardcoded → puntero. 28. **Sema tiene CUATRO canales en el código; la doctrina cuenta dos o tres.** `grep "implements Channel"` sobre `src/uix/sema/chans/`: `VisualChannel` · `SoundChannel` · `HapticChannel` · **`AnnounceChannel`** (`announce.ts:64`). Y no es código muerto: opción del motor (`engine.ts:88` — `announce?: true | false | AnnounceChannelOptions | Channel`), instanciado en `engine.ts:327`, exportado en `exports.ts:74`. Entró con `53b6f629f` (2026-07-08, trabajo real — procedencia comprobada). Contra eso: - `book-deviations.md` **D.8** lista «ARIA dinámico» en *«Lo que NO es canal»* (*«Sólo un morfo lo necesita (Announce). Hacer canal añadiría engine surface sin caso plural»*) y cierra: *«si en el futuro `Announce` necesita ser pluggable… vale convertirlo en canal formal. **Hoy no.**»* - `theming/channels.md` §2: *«sema's runtime channels, which are **3**»*. - `CLAUDE.md`: *«sema executes `sound` + `haptic` **only**»*. **Matiz que hay que conservar**: el *criterio* de D.8 sobrevive intacto — announce no admite modulación por `intent.deltas`, y la cabecera del propio `announce.ts` lo reconoce citando D.8: *«a channel by registration, not a parametric-signature channel»*. Lo stale es la **disposición** («hoy no», «no es canal», la lista de 2) y el conteo de `channels.md`. Curiosidad que vale como lección: **el código cita como justificación un documento que dice lo contrario de lo que el código hizo.** 29. **La tabla de holds de D.9 quedó stale contra D.12 — en el mismo documento.** D.9 (líneas 486–495) publica como *«tabla canónica `SEMA_HOLDS_BY_INTENT`»*: `commit.fulfill → hold: 'noticed'` y `signal.loss → hold: 'noticed'`. `holds.ts:86` tiene `fulfill: { hold: 'settled' }` y `holds.ts:103` `loss: { hold: 'brief' }`, con el comentario de la corrección del 2026-07-06 — que es exactamente lo que D.12 decisión 2 documenta 200 líneas más abajo. Quien lea D.9 como canon (y su rótulo invita) se lleva los valores anteriores a la corrección. 30. **D.3 figura como pendiente cuando ya está ejecutado; D.2 sigue pendiente de verdad.** §F.4 lista *«Cambio inmediato pendiente en proyecto: split de `intentPolicy` en `intentRequirement` + `intentGuidance` (D.3)»* — pero ambos ejes viven en `src/uix/sema/types.ts` (y `CLAUDE.md` ya los doctrina como vigentes). En cambio §F.3 acierta: `emission` **no** existe en `src/uix/morfo/types.ts`. Y `npm run morfo:vocabulary` (D.4) **sí** existe. 31. **El grep que `component-guide.md` manda ejecutar dos veces no escanea nada.** Ítem 37 de la checklist y regla A34.1 documentan: `grep -n "['\"]soma\.[a-z-]" src --include=!*.md`. Medido: `--include='!*.md'` no es negación en grep — la orden devuelve **0 líneas, exit 1, sin stderr**. Con `--exclude='*.md'` salen ≥5 coincidencias (`accordion-provider:114`, `calendar-provider:190`, `form-auto-fields:460`, `portal.svelte:38`…). Una verificación declarada **obligatoria** que siempre «pasa» porque no mira nada. (Aparte: las coincidencias que sí aparecen son namespaces de `logger`, no claves de traducción — el patrón, además de inerte, es demasiado ancho.) 32. **Numeración rota de la checklist de `component-guide.md`.** El frontmatter y `component-audit.md` la citan como «pasos 1–40»; hay **42 entradas** y la secuencia real es `1…33, 36, 38, 39, 40, 34, 35, 37, 38, 39` — los ítems **38 y 39 están duplicados** con contenidos distintos (38: loop A35 / auditoría de topología DOM; 39: loop asíncrono A36 / smoke). Las citas por número que sí resuelven (A32 → «items 34–35») siguen bien; las de 38/39 son ambiguas. 33. **Puntero roto en `component-guide.md` A36** (línea 1512): cita `src/uix/soma/components/form/BUG-onchange-onblur-hang.md` *«para el transcript diagnóstico completo»*. El fichero lo borró `2d35f4b3e` (**2026-05-08**), cuyo propio asunto era *«prune obsolete audits + studies, **refresh remaining references**»*. La cita la (re)escribió `62a62075f` (2026-07-02) al migrar la guía al libro — dos meses **después** del borrado. En `form/` sólo queda `README.md`. 34. **Censo hardcodeado: «≈140 morfos today» ×2** (líneas 728 y 1561 de `component-guide.md`). Reales: **165** (`ls src/uix/morfo/components/*.ts` sin tests). Tercera aparición de la misma enfermedad, tras el «8 roles» (nº 21) y la tabla de servicios (nº 22/27). 35. **`channels.md` §3 (línea 89) apunta a una sección de `CLAUDE.md` que no existe**: *«The full canonical narrative lives in `CLAUDE.md` → "Sema: open channel registry"»*. `grep` sobre `CLAUDE.md` y `AGENTS.md`: cero. 36. **Dos documentos actuales dan listas distintas de las mismas 5 superficies con paleta viva.** `demo-authoring.md` §6: *«button, toggle, checkbox, radio-group, switch (5/17 data-color surfaces)»*. `notes.md` §What's next: *«button, checkbox, switch, radio-group, toggle-group»*. Difieren en **toggle vs toggle-group**. El denominador «17» no casa con ningún censo medido: 12 morfos declaran `data-color`, 46 recetas eidos seleccionan por él, 82 componentes exponen prop `color`. → **NO VERIFICADO** cuál de las dos listas es la correcta ni qué contaba el 17; lo verificado es que discrepan entre sí. ### Lo que esta tanda CONFIRMA (no son hallazgos nuevos) 37. **El nº 13 queda cerrado por cuarta vía, y el nº 14 acotado.** `gradient-finish.md` §9 (**D8**, 2026-07-15, rotulado «doctrine» en el registro de decisiones) enuncia la misma regla que `reference.md` §39, con el mismo modo de fallo medido en vivo: *«the morfo declares an attr only when its driving prop crosses the soma boundary»*. Y es **comprobable por componente con una línea**: - `switch.svelte:41` → `color={colorAttrs.dataColor}` — **prop de soma** → su morfo declara `data-color`. ✅ - `checkbox.svelte:45` → `data-color={colorAttrs.dataColor}` — **atributo DOM crudo**, junto a `data-size`/`data-variant` → su morfo NO lo declara, y es correcto. ✅ → Los 46 componentes cuyas recetas seleccionan por `data-color` **no** son 46 infracciones: son la regla funcionando. La población del nº 14 son exactamente los **12 morfos** que lo declaran. 38. **Refuerza el nº 23**: un TERCER documento declara superado el scope `event:*` — `theming/notes.md` líneas 151–158, *«SUPERSEDED (2026-07-11, DOC-4)… never exercised and is no longer the plan»*. Sólo `canon/tsc.md` lo sigue anunciando sin nota. ## Hallazgos RETIRADOS (eran autocita) - ~~«los tests de provider son convención, no guard»~~ — era la edición revertida. El original afirma que el guard existe. **La pregunta sobre el arnés de tests sigue abierta y no está resuelta por el corpus.** → **Cerrada el 2026-07-30 por el hallazgo nº 16**, pero entrando por la puerta buena: el guard no existe *en el código*, y eso se comprueba con `grep`, no citando un documento. El corpus dice lo contrario en dos sitios; quien manda es el código. - ~~«`soma-architecture.md` documenta una API fantasma que `overview.md` desmiente»~~ — la contradicción la creó la edición revertida. → **Sigue retirada tal cual**: los dos documentos coinciden, no se desmienten. Lo que sí se sostiene, y es otra cosa, es el **nº 15**: la API que ambos documentan no existe en el código. **El patrón que dejan los dos.** Una sesión anterior acertó en el hecho y falló en el método, y al revertirla se perdieron los dos. La lección no es «desconfía del hallazgo revertido», es: **un hallazgo doc↔código se verifica contra el código**. Si sólo se puede sostener citando otro documento, no está verificado. Los once hallazgos nuevos de estas sesiones llevan los dos anclajes. ## Cómo continuar ~~1. Releer los 7 marcados `RELEER`~~ · ~~2. `morfo.md` completo~~ — hechos. ~~1. El canon + el núcleo restante~~ · ~~2. `theming/reference.md` entera~~ — hechos en la sesión 3. ~~1. Las guías~~ · ~~2. `book-deviations`~~ · ~~3. el resto de theming~~ — hechos en la sesión 4 (más `demo-authoring`, que no estaba inventariada). 1. **Siguiente tanda**: los 7 RFCs (1860) y **`spec/delegation-contract.md`** (502, NORMATIVO). Después los cuatro cortos (`book-map` · `authoring` · `decisions.md` · `building-a-component`, 480). 2. Luego `theming/changelog.md` (1879) y el resto nunca inventariado (`next-features` · `getting-started` · `comparison` · `design-text-effects`, 634). 3. Al leer `decisions.md` (88), **decidir el estatuto** de los 4 registros de diseño que indexa y que están fuera del mapa (`design-connection` 1987 · `design-timer` 1540 · `design-session` 857 · `design-chat-block` 190). Si entran, el corpus crece 4.574 L más. 4. Actualizar este fichero al final de cada sesión de lectura: estado por documento y línea donde se paró. 5. **No escribir código ni doctrina hasta acabar.** Un hallazgo sólo se reporta si se puede citar documento + línea, y se ha comprobado que esa línea no la escribió una sesión anterior. 6. Un analizador heurístico que no resuelve algo dice «no verificado»; no emite un hallazgo. 7. **Paso 0 de cualquier relectura**: comprobar que el árbol no está contaminado (`git diff ^ HEAD -- `), y para una línea concreta, `git log -L ,:`. Cuesta un comando y es lo que separa la doctrina de la autocita. ## Lo que NO se ha hecho (y por qué) Nada de lo anterior está arreglado — la regla 5 lo prohíbe hasta acabar la lectura, y quedan ≈ 5.355 L del corpus mapeado. En concreto **no se ha tocado**: el enum de `data-color` en los morfos (nº 14, además pide decisión de usuario), los dos docs que documentan `commitState`/`emitEvent` (nº 15), las dos frases sobre el guard inexistente (nº 16), `soma-architecture.md` §12/§8.bis sobre `clsx` (nº 17), `packs.md`/`glossary.md` sobre Aura y `uix.scene` (nº 18), el frontmatter de `CANON.md` (nº 19), el entry huérfano de `package.json` (nº 20), la línea que tiene `docs:check` en rojo (nº 21), la tabla de servicios de `active-app.md` (nº 22/27), la nota que le falta al scope `event:` (nº 23/38), la tabla de alias de `CLAUDE.md` sin `$sound` (nº 26), el conteo de canales de `channels.md` §2 + D.8 + `CLAUDE.md` (nº 28), la tabla de holds de D.9 (nº 29), el §F.4 de `book-deviations` (nº 30), el grep inerte (nº 31), la numeración duplicada de la checklist (nº 32), el puntero al `BUG-…md` borrado (nº 33), el «≈140 morfos» (nº 34), el puntero a la sección inexistente de `CLAUDE.md` (nº 35) ni las dos listas de superficies que discrepan (nº 36). De los veintitrés, **sólo el nº 14 pide decisión de usuario** (enum cerrado del morfo vs. `color` abierto a 42 por diseño; con `morfo:check` cazándolo sólo si una demo elige el valor; población acotada a 12 morfos por el nº 37). Los demás son deriva doc↔código con un único arreglo evidente cada uno — salvo **el nº 28**, que tiene dos salidas legítimas (actualizar D.8 + `channels.md` + `CLAUDE.md` al hecho consumado, o revisar si `AnnounceChannel` debía existir) y conviene plantear antes de tocar nada. **Dos hallazgos son de método, no de contenido, y afectan a cómo se sigue leyendo**: el nº 24 (el inventario estaba truncado — ya corregido arriba) y el nº 25 (una lectura CADUCA cuando el código se mueve debajo; `sema.md` cambió el mismo día). Conviene comprobar `wc -l` contra el inventario al arrancar cada sesión: cuesta un comando y detecta la caducidad antes de citar.