# CONTINUE — la superficie perceptual es UNA RANURA > **Kickoff**: _«Lee `docs/process/CONTINUE-perceptual-surface.md` y sigue por §0.»_ > **Fecha**: 2026-08-10, actualizado 2026-08-11 (cierre) · Rama > `alpha-0.1-dir-prefs`, pusheada en `61457baac`. > ⚠️ **COMPARTIDA**: otra sesión trabaja en `blocks` **y** en un eje de > dismissal. `git reset -q` + stagear por lista explícita, y clasificar **por > contenido del diff, nunca por nombre** — hoy un fichero de proceso estaba > mixto (133 líneas, 3 mías). > **Doctrina viva**: [`architecture/sema.md` §The surface is ONE SLOT](../architecture/sema.md) > · [`architecture/morfo.md` §Step 4](../architecture/morfo.md) (las DOS clases > de precedencia de attrs: contract vs naming) · [`§renderAttrs`](../architecture/morfo.md). > Este documento sólo dice **qué se cerró, qué queda y cómo medirlo**. ## 0 · Dónde estás (2026-08-11, cierre de jornada) **PUSHEADO a `gita/alpha-0.1-dir-prefs` en `61457baac`** — la rama está limpia contra el remoto (0 por delante). Cuatro commits de la jornada del nombrado: | Commit | Qué | | --- | --- | | `37c732c3a` | el guard de traducciones deja de cascar **y de ser ciego** a los call sites | | `25e59e18f` | A-85: el attr de nombrado es un DEFAULT del contrato, no una imposición | | `b201f190d` | la consecuencia: espejos, button, las 21 cadenas, cross-element, gemelos | | `61457baac` | el handoff dice lo que pasó (dos diagnósticos heredados eran falsos) | ⚠️ **Rama compartida, y hoy se notó**: otra sesión trabaja en `blocks` **y** en un eje de dismissal (los `types.ts` de color-picker / time-picker / time-range-picker). Clasificar **por contenido del diff**, nunca por nombre: `AUDIT-blocks-ledger.md` estaba MIXTO (133 líneas, 3 mías) y quedó entero fuera. **Mis filas de A-85 en ese ledger siguen SIN COMMITEAR** — cuando esa sesión lo commitee, irán en su commit; no hay que rehacerlas. ### El eje del nombrado: qué queda, y por qué ~~Sólo **UNA** cadena pendiente es mecánica: **`alert-dialog`**~~ **HECHA 2026-08-12** — la receta de los 21 aplicada tal cual (`morfo.md` Step 4): el morfo declara `#?components.alert-dialog.action|Confirm` y `.cancel|Cancel`, fuera la cadena, la fuente, el prop de opts y el destructure de los dos wrappers; `ALERT_DIALOG_LANGS` queda huérfano (su `langs.ts` se suma a la lista de abajo). Medido en navegador (`es`): `aria-label="Confirmar"` / `"Cancelar"` saliendo del contrato. Refs compilados 157 → **159**; el harness del test alineado con producción (`translate` = `ts`, la trampa de editable). **El eje del nombrado queda a CERO cadenas mecánicas.** Las otras 10 cadenas **no son deuda** — están medidas y clasificadas: `clipboard` · `password-field` (toggle) · `table` (row-detail) · `tree-grid` (expand) están BLOQUEADAS por estado; `tag-group` (remove) por interpolación; `waveform` es correcta (su nombre lo lleva un `` compuesto); `progress` · `radio-group` · `rating-group` · `slider` conservan sólo la supresión por labelledby sobre un prop sin default. > **DECISIÓN TOMADA (2026-08-11): NO se ensancha `mapRef`.** Traducir su salida > desbloquearía sólo la clase ESTADO —ni las interpoladas ni las > cross-element— y reintroduce la avería que esta jornada mató: el guard v2 > **no recorre los valores del mapa**, y `normalizeTranslationRef` tampoco los > tocaría, así que una clave relativa llegaría cruda a `ts()` y saldría literal. > Si algún día se hace, la forma correcta NO es traducir la salida sino que los > valores del mapa sean **fuentes** (composicional, sin magia de prefijo, y el > guard los ve por la vía que ya tiene). **Lo siguiente, en orden:** 1. ~~**`alert-dialog`** — la única cadena mecánica que queda (30 min).~~ **HECHA 2026-08-12.** 2. ~~**Dos decisiones del autor** (§4)~~ **FIRMADAS 2026-08-12**: el chip bloqueado (vía b + su letra pequeña) y `mode`/`scope` (retirar). La tercera, `fixedWeeks`, quedó **REVOCADA el 2026-08-13** al aparecer el SPEC del componente — las disposiciones completas viven en §4. 3. **C5 rebanada 3**, y empieza por **subir a soma el comportamiento del chip** — la tabla de §C5-r3 explica por qué no es mecánica. 4. ~~**Arreglar `npm run translations:check`** (casca) y censar los 166 morfos.~~ **HECHO 2026-08-11** — y el diagnóstico era doblemente falso: el crash no era una «url multilínea» sino el stripper de comentarios comiéndose el `//` de un `https://` DENTRO de una cadena; y el guard, incluso sano, **jamás escaneó los call sites** (`v.translationRef('key')` no contiene literal `#?` hasta que el compilador lo normaliza) — los cinco defectos de chronos habrían pasado en verde. Reescrito sobre el contrato COMPILADO (importa morfos + `compileMorfo`, valida cada ref normalizada; catálogos importados, no regex-evaluados; fuentes `.ts` por AST de literales). Sweep: **172 errores → 0** (6 morfos sin catálogo · media-player 6 claves desfasadas · ~130 del chrome de palabras, con dos colisiones hoja↔rama `status`/`placeholder` resueltas a `status.label` / `placeholder.default` y descripciones slash en clave plana `-d`). Detalle: caja ⚠️ de `morfo.md` §common-mistakes. 5. El resto del eje: ~~**F3**~~ **CERRADA 2026-08-13** (censo a 0 y API borrada) · ~~**F5** navegador~~ **CERRADA 2026-08-13** (los tres sitios medidos tras la campaña; los números y los dos artefactos del entorno, en §3.0 F5) · §3.2 restitución de persistentes · §3.3 eje B · §3.4 cola de sema · §3.5 fable · §3.6 ledger de blocks (`A-47`, `A-69`). **Deuda menor, medida y sin urgencia** (no la re-descubras): - ~~⚠️ **file-upload emite `contact-trigger-picker` DOS veces por un gesto**~~ **ARREGLADO 2026-08-13**, y el mecanismo era MÁS ANCHO que el diagnóstico inicial: no es sólo el Trigger burbujeando — el `` oculto también vive DENTRO del Dropzone, así que el `click()` SINTÉTICO que dispara `openPicker` vuelve a entrar por el mismo manejador. Medido: **un** click en el dropzone llegaba **dos** veces, la segunda con `target = INPUT[hidden-input]`; sólo el guard de reentrada que la plataforma pone en `click()` lo paraba en dos. Es decir, TODO click al dropzone duplicaba, no sólo el del botón. Arreglo: guard **por propiedad** en el `onclick` del dropzone (`closest` sobre los marcadores de `trigger` y `hidden-input`, construidos con `partMarkerAttr`, nunca a mano). Test visto fallar con la anidación real; medido después: click en el dropzone → UN `contact-trigger-picker` en DROPZONE, click en el botón → UNO en TRIGGER. ⚠️ Lección de clasificación: lo anoté como «adyacente, no cascadear» y era la clase que este eje persigue —una ocurrencia por gesto—, encontrada por mi propia medida en un fichero que ya estaba editando. Eso se arregla, no se anota. - **10 de 17 harnesses tienen un `ts` que IGNORA su clave** — una clave errónea *pero existente* pasaría su aserción del bag. Lo tapa parcialmente `translations:check` (caza las inexistentes) y hoy se verificó a mano que no hay deriva. Cerrarlo es `expect(ts).toHaveBeenCalledWith(ref)`, decidido una vez para todos. - **15 `langs.ts` sin consumidores** (meter, popover, drawer, splitter, listbox, breadcrumb, anchor-nav, cropper, feed, grid-list, pin-input, progress, editable, carousel; +alert-dialog 2026-08-12, huérfano de su migración clase 1). Basura declarada. **NO borrar sin instrucción explícita.** - **12 avisos de huérfanos** en `translations:check` (los `tab-*` de gradient-builder/picker y 8 de palabras): candidatos a borrar, decisión tuya. - `range-calendar/langs.ts` conserva `PREV_MONTH`/`NEXT_MONTH` muertas **PREEXISTENTES** — no las tocó este pase. **Documentación ya actualizada** (no hace falta re-hacerla): `morfo.md` §Step 4 (las dos clases de precedencia, el anti-patrón del `propRef` + cadena, la tabla de las cuatro exclusiones, el spread condicional y la regla «mira si el elemento es tuyo») · `morfo.md` §common-mistakes (la trampa de `texts` + el guard v2) · `soma-architecture.md` §mergeProps (la fila de naming) · README de soma/chronos · README de eidos/chronos · `CONTINUE.md` de chronos (NO vigente, apunta aquí). **Línea base al cerrar** (mídete contra esto, no contra memoria): `check` **70 = base** con el WIP ajeno presente, **57** sin él — mis commits aportan CERO · soma **1269 ✓ / 1** (el timeout ajeno preexistente de `soma-attr-audit`) · morfo+sema **496 ✓** · `translations:check` **0 errores / 12 avisos** · `docs:check` 0/621 · `rtl:check` 0/177 · refs compilados **157**. --- ## 1 · El hallazgo, y por qué costó tanto verlo `data-event-*` es **una ranura por elemento**: dos ocurrencias sobre un nodo no pueden expresarse las dos. El defecto lo encontraron **tres auditorías independientes y ninguna lo cerró**: | Auditoría | Fecha | Qué dijo | | --- | --- | --- | | fable **S1** | 2026-07-01 | «Race de señales solapadas sobre el mismo target», con el arreglo mínimo escrito. El README de `old-deprecated` afirmaba «findings executed». | | sema **S-17** | 2026-08-05 | Re-descubierto solo. Midió el knob: el estampado de `commit-set` moría a los 24 ms de un hold de 240. | | blocks **A-36 / A-65** | 2026-08-01/09 | Reproducido en NAVEGADOR: dos estampados a 1,1 ms, `press-squeeze` jamás. | Tres veces el mismo defecto, tres veces sin arreglar. Lo que faltaba no era diagnóstico: era que **nadie podía distinguir una redirección legítima de una deriva**, porque la opción que redirigía se llamaba `fallbackTarget` y decía lo contrario de lo que hacía. --- ## 2 · Lo cerrado (todos los guards vistos fallar antes de arreglar) | # | Qué | Dónde | | --- | --- | --- | | 2.1 | **`fallbackTarget` → `targetOverride`** (202 apariciones / 64 ficheros). No cosmético: es la PRECONDICIÓN para auditar la redirección | `soma/runtime.svelte.ts` + todo el árbol | | 2.2 | **El desestampado comprueba propiedad** (`data-event-id`). Rojo: `expected null to be 'sig-B'` | `sema/stamp.ts` · `sema/projection/dom.ts` | | 2.3 | **`assertTargetOverride`** — el runtime lee el marcador de parte del elemento y avisa si no es `target` ni `allowedTargets`. Runtime porque el override es una expresión | `soma/runtime.svelte.ts` | | 2.4 | **A-36 CERRADO, y sin `regime`** | los 4 overlays | | 2.5 | **`regime` definido y cableado**, reducido a `replace \| queue` | `sema/types.ts` · `engine.ts` · `signal.ts` | | 2.6 | **`renderAttrs`** — los attrs que no son partes | `morfo/types.ts` + `schema.ts` | | 2.7 | `ACTIVE_DEV_TRACK` vaciado + deuda de chronos (scope · barrel · README) | `contracts.test.ts` | | 2.8 | **S-36** barrel 68→71 + guard · **S-31** `vibrate(NaN)` · **S5** · **SO2** · **SO4** | varios | ### 2.4 A-36 — la lección: no era arbitrar, era dejar de redirigir Los cuatro overlays cedían el estampado del `open` al trigger. La razón escrita en el código («el content no ha montado») era cierta **cuando `open` era `pre`**. Dialog / Drawer / Popover migraron a `post` hace tiempo y nadie retiró el override; `float-panel` seguía en `pre` (último fuera de la doctrina que `sema.md` escribe). Con `` compuesto sobre `