From 826ca2bf46ba2c8ba43b9a736d6637de3acf1068 Mon Sep 17 00:00:00 2001 From: dev Date: Tue, 26 May 2026 02:24:52 +0200 Subject: [PATCH] =?UTF-8?q?docs(sema):=20add=20D.8=20=E2=80=94=20channels:?= =?UTF-8?q?=20qu=C3=A9=20es=20canal=20y=20qu=C3=A9=20no?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Doctrina arquitectural sobre límites de los canales runtime: - Canales built-in del framework: sound + haptic (los únicos). - ARIA estructural: declarativo en morfo, NO canal. - ARIA dinámico (live regions): soma escribe directo, NO canal. - Visual (motion / color / presence): eidos CSS via data-event-*, NO canal. - Regla operativa: algo es canal sólo si recibe SemanticSignal, acepta modulación por intent.deltas, y tiene signature paramétrica análoga a sound/haptic. ARIA dinámico falla la modulación. - Channels extensibles: declaration merging del registry; opt-in por app (voice, a11y formal, etc.). Framework no envía ninguno. Cierra una tentación que surgió en D.7 — convertir ARIA en canal era confusión categorial. Esta sección fija los límites para que nadie reincida. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md | 67 +++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md b/src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md index 0cce4714f..3e99fba4c 100644 --- a/src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md +++ b/src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md @@ -372,6 +372,72 @@ intentGuidance: 'expected' | 'contextual' | 'discouraged' - **Implementación**: aplicar en `src/uix/sema/types.ts:SEMA_FAMILY_POLICY`. TypeScript deriva `IntentExpectedFamily` desde `intentRequirement === 'required'`. `intentGuidance` queda como campo doctrinal de documentación + posible lint en el futuro. +### D.8 Channels: qué es canal y qué no + +- **Status**: **PROJECT_CANON** (regla arquitectural de límites) +- **Origen**: al revisar la sección D.7 emergió la tentación de promocionar ARIA a "canal a11y". Análisis honesto: era confusión categorial. Esta sección fija los límites para que nadie reincida. + +**Canales runtime declarables en sema (built-in del framework):** + +``` +sound — SoundSignature (synth/sample, modulable por intent.deltas) +haptic — HapticSignature (vibration, modulable por intent.deltas) +``` + +**Lo que NO es canal (y no debe convertirse en canal):** + +| Cosa | Dónde vive | Razón | +|---|---|---| +| ARIA estructural (`aria-label`, `aria-expanded`, `role`, ...) | `Morfo.parts[].aria` + `.role` | Declarativo. Resuelto desde props/states. Promocionarlo a canal sería convertir lo declarativo en post-hoc DOM manipulation. | +| ARIA dinámico (live regions `aria-live`) | Soma escribe directo en el live region DOM | Sólo un morfo lo necesita (`Announce`). Hacer canal añadiría engine surface sin caso plural. | +| Visual — motion (animaciones, transiciones) | Eidos CSS `@keyframes` reaccionando a `data-event-*` stampeado por `VisualChannel` | El stamp es la única responsabilidad de sema; el output visual es CSS. | +| Visual — color/intent (data-color, data-event-intent overlays) | Eidos recipes + design tokens | Idem. | +| Visual — presence (z-index, opacity, layout) | Eidos CSS | Idem. | + +**Channel activation por familia** (en `SEMA_MAP`): + +``` +contact sound + haptic +commit sound + haptic +signal sound + haptic +emerge sound (sin haptic — apariciones no son táctiles) +shift sound (sin haptic — cambio de marco) +handle haptic (sin sound — feedback gestual es táctil) +sustain ninguno (puramente visual) +delegate ninguno (puramente estructural / visual) +``` + +Los packs DEBEN respetar el `activeChannels` del family. Añadir `haptic` a un pack que opera sobre family `emerge` es incoherente; añadir `sound` a `handle` también. El resolver no lo bloquea, pero la doctrina sí. + +**Regla operativa**: algo es canal sólo si cumple las tres: + +``` +(a) recibe SemanticSignal y emite output perceptual, +(b) acepta modulación por intent.deltas (perceptual loading), +(c) tiene signature paramétrica análoga a SoundSignature / HapticSignature. + +Si falla (b), no es canal — es declarativo o ad-hoc. +``` + +ARIA dinámico falla (b): `signal-announce + threat` produce el MISMO texto + el MISMO ARIA. La carga evaluativa del intent vive en sound + visual, no en el texto del anuncio. Por eso no es canal. + +**Channels extensibles (no built-in, opt-in por la app):** + +El registry `SemaChannelSignatures` es OPEN vía declaration merging. Una app puede: + +```ts +declare module '$uix/sema' { + interface SemaChannelSignatures { + voice: { phrase: string; rate?: number; ... }; // TTS + a11y: { ariaPayload: string; politeness: 'polite' | 'assertive' }; // si la app lo quiere formal + } +} +``` + +E implementar un `Channel` con `prepare(signal, target)` + `play(effective)`. El framework no envía ninguno de éstos — son extensión de producto. + +**Pendiente sin urgencia**: si en el futuro `Announce` necesita ser pluggable (apps que quieran enviar a logger, telemetría, voice UI), entonces vale convertirlo en canal formal. Hoy no. + --- ## E. Resumen ejecutivo @@ -414,6 +480,7 @@ intentGuidance: 'expected' | 'contextual' | 'discouraged' - Packs sema soft-tuned para alta frecuencia (D.5, toggles) - Packs sema para superficies de menú y árboles (D.6) - Doctrina sonido canónico vs samples (D.7) + packs tooltip / collapsible +- Channel scope: qué es canal y qué no (D.8) ---