You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/sema/engine.ts

639 lines
23 KiB

sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
/**
* EngineSemantic — registry de canales perceptivos + dispatch.
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
*
* El engine no conoce DOM, ni hold, ni atributos de estado. Su trabajo es:
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
*
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* 1. Asignar un id estable a cada ocurrencia.
* 2. Ejecutar `channel.prepare(...)` antes de la cascade para que los
* canales publiquen su superficie temporal si la necesitan.
fix: address architectural audit findings + sync ecosystem docs Audit: src/audit-opus-4-6-26.md. All non-words findings remediated. Code: - E1: navigation-menu indicator data-state visible -> open (render bug; the active underline was permanently invisible). Clears the only invalid lint selector. - SO1 + E2: raw `new ResizeObserver` -> ActiveDom.observeResize in carousel-provider and the canvas-text useContainerWidth hook (+ s-text / s-text-virtual-list pass eidos.dom). iframe/popup-safe, lifecycle-tracked. - A1: defineUixServices now registers `motion`, so attach-mode app.motion is real and the active-uix fallback becomes the true edge case (test guard updated). - A2/A4: contracts.ts pins `motion` + `announce` in ActiveUixServiceContract + publicSurface; dispose() comment corrected. - SO2: carousel drops the hardcoded `transform 300ms ease-out` (the recipe already handles it via [data-dragging]); also fixed the recipe's undefined `--duration-base` token -> `--duration-slow` (it was masked by the inline). - S1: HapticChannel reduced-motion via an injected ActiveDom port (mirrors SoundChannelDom) instead of global matchMedia. - T1: 10 sites repointed `$libs/dom` -> `$adom` (sema x5 + its tests x3, active-uix value import, arts/prefs). - M2 / S2 / S3: dead code removed (button `states:['idle','loading']`, SemaRuntimeChannelId, SEMA_VALENCED_FAMILY_LIST). Docs: - Motion-as-service reflected across the ecosystem: CLAUDE.md (aliases + arch + service note), arts/README, active_architecture, soma SOMA_ARCHITECTURE, eidos README, eidos-motion.md. - X1: CLAUDE.md "5 canonical channels" (false) -> the single canonical narrative (8 book channels; Sema runs 2 + visual meta-channel, Eidos materializes 5). - X2/X3/X4: sema/README (8 families + intentRequirement/intentGuidance split), types.ts JSDoc (SEMA_INTENT_POLICY -> SEMA_FAMILY_POLICY), engine.ts cascade 6->5, alias table ($frontend out, $lang->$langs, +$clipboard). - E4: codex_audit.md HISTORICO banner. Deferred: SU2 (test-only layering, not a build violation); Words M1/T5/E3 (WIP). Verify: npm run check -> 1 pre-existing error (grafito), 0 new; sema 152/152, contracts 31/32 (1 pre-existing words), carousel 4/4, motion 22/22. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
* 3. Resolver la `EffectiveSignature` con las 5 capas de cascade.
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* 4. Despachar a cada canal registrado.
* 5. Esperar al canal visual y limpiar los handles de preparación.
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
*
* Override layers (ver `resolver.ts`):
* 1. family base — SEMA_MAP.families
* 2. intent deltas — SEMA_MAP.intents (any family with intent)
* 3. morfo per-event — signal.overrides + signal.channels
* 4. runtime path overrides — engineOpts.overrides.runtime
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* (applied to map at construction)
docs(reconciliation): soma deps truth + checklist enforcement column + sema cascade renumber + archetype pointers Phase 1d of PLAN-docs-reconciliation. - soma/README s3 + SOMA_ARCHITECTURE s2/s6/s12 + popover/link-preview READMEs: positioning is the in-house engine (layers/floating + $ethereal); @floating-ui is devDep-only. Layers table gains Stacking / AxialDrag / ZoomPan / ImageProvider / ListSelection (popper/ is an empty leftover dir — not documented). clsx documented as it really is: NOT declared but still imported by props/props.ts via a svelte transitive (phantom dep — flagged for a decision, chip spawned). - COMPONENT_COMPLETION_CHECKLIST: new Enforcement column on every rule table (audit | tool:X | manual) so declared-but-unchecked rules are explicit; morfo rule IDs renamed M-* -> A-* to grep-match what component-audit.ts emits; stale rows fixed against the corrected script (D-1.2 v2 9-tab, D-3.1 single eidosSnippet, E-2.2 wrapper import); implemented-but-undeclared rules added (R-2.7 literal typography, A-3.4b verb/family pairing); A-3.3 family count decopied; sI now requires an honest Enforcement value for new rules. - Sema cascade numbering unified to 1 family - 2 intent - 3 morfo - 4 runtime - 5a packs - 5b app across sema/README, engine.ts, resolver.ts, resolver.test.ts and soma/runtime.svelte.ts (three divergent schemes coexisted; CLAUDE.md already uses the chosen one). - '24 archetypes' copied lists replaced by pointers to ARCHETYPE_VOCABULARY in active_architecture s6 + morfo/README. - TriggerOptions.semantic JSDoc: phantom defaultSemantic removed (implemented shape is additive allowedFamilies, D.11); 'warns via logger' claim corrected — the override is silently ignored on non-polymorphic events (no warn exists in code). npm run check: 61 errors = pre-existing baseline, 0 new. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
* 5a. pack cascade — engineOpts.components (per-component
* packs, prepended)
* 5b. app cascade — engineOpts.overrides.cascade
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* (app-level rules appended; win on tie)
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
*/
import type { Channel, ChannelPreparation } from './chans/types';
fix: address architectural audit findings + sync ecosystem docs Audit: src/audit-opus-4-6-26.md. All non-words findings remediated. Code: - E1: navigation-menu indicator data-state visible -> open (render bug; the active underline was permanently invisible). Clears the only invalid lint selector. - SO1 + E2: raw `new ResizeObserver` -> ActiveDom.observeResize in carousel-provider and the canvas-text useContainerWidth hook (+ s-text / s-text-virtual-list pass eidos.dom). iframe/popup-safe, lifecycle-tracked. - A1: defineUixServices now registers `motion`, so attach-mode app.motion is real and the active-uix fallback becomes the true edge case (test guard updated). - A2/A4: contracts.ts pins `motion` + `announce` in ActiveUixServiceContract + publicSurface; dispose() comment corrected. - SO2: carousel drops the hardcoded `transform 300ms ease-out` (the recipe already handles it via [data-dragging]); also fixed the recipe's undefined `--duration-base` token -> `--duration-slow` (it was masked by the inline). - S1: HapticChannel reduced-motion via an injected ActiveDom port (mirrors SoundChannelDom) instead of global matchMedia. - T1: 10 sites repointed `$libs/dom` -> `$adom` (sema x5 + its tests x3, active-uix value import, arts/prefs). - M2 / S2 / S3: dead code removed (button `states:['idle','loading']`, SemaRuntimeChannelId, SEMA_VALENCED_FAMILY_LIST). Docs: - Motion-as-service reflected across the ecosystem: CLAUDE.md (aliases + arch + service note), arts/README, active_architecture, soma SOMA_ARCHITECTURE, eidos README, eidos-motion.md. - X1: CLAUDE.md "5 canonical channels" (false) -> the single canonical narrative (8 book channels; Sema runs 2 + visual meta-channel, Eidos materializes 5). - X2/X3/X4: sema/README (8 families + intentRequirement/intentGuidance split), types.ts JSDoc (SEMA_INTENT_POLICY -> SEMA_FAMILY_POLICY), engine.ts cascade 6->5, alias table ($frontend out, $lang->$langs, +$clipboard). - E4: codex_audit.md HISTORICO banner. Deferred: SU2 (test-only layering, not a build violation); Words M1/T5/E3 (WIP). Verify: npm run check -> 1 pre-existing error (grafito), 0 new; sema 152/152, contracts 31/32 (1 pre-existing words), carousel 4/4, motion 22/22. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
import type { DomApplier } from '$adom';
import type { Logger } from '$libs/logger';
fix: address architectural audit findings + sync ecosystem docs Audit: src/audit-opus-4-6-26.md. All non-words findings remediated. Code: - E1: navigation-menu indicator data-state visible -> open (render bug; the active underline was permanently invisible). Clears the only invalid lint selector. - SO1 + E2: raw `new ResizeObserver` -> ActiveDom.observeResize in carousel-provider and the canvas-text useContainerWidth hook (+ s-text / s-text-virtual-list pass eidos.dom). iframe/popup-safe, lifecycle-tracked. - A1: defineUixServices now registers `motion`, so attach-mode app.motion is real and the active-uix fallback becomes the true edge case (test guard updated). - A2/A4: contracts.ts pins `motion` + `announce` in ActiveUixServiceContract + publicSurface; dispose() comment corrected. - SO2: carousel drops the hardcoded `transform 300ms ease-out` (the recipe already handles it via [data-dragging]); also fixed the recipe's undefined `--duration-base` token -> `--duration-slow` (it was masked by the inline). - S1: HapticChannel reduced-motion via an injected ActiveDom port (mirrors SoundChannelDom) instead of global matchMedia. - T1: 10 sites repointed `$libs/dom` -> `$adom` (sema x5 + its tests x3, active-uix value import, arts/prefs). - M2 / S2 / S3: dead code removed (button `states:['idle','loading']`, SemaRuntimeChannelId, SEMA_VALENCED_FAMILY_LIST). Docs: - Motion-as-service reflected across the ecosystem: CLAUDE.md (aliases + arch + service note), arts/README, active_architecture, soma SOMA_ARCHITECTURE, eidos README, eidos-motion.md. - X1: CLAUDE.md "5 canonical channels" (false) -> the single canonical narrative (8 book channels; Sema runs 2 + visual meta-channel, Eidos materializes 5). - X2/X3/X4: sema/README (8 families + intentRequirement/intentGuidance split), types.ts JSDoc (SEMA_INTENT_POLICY -> SEMA_FAMILY_POLICY), engine.ts cascade 6->5, alias table ($frontend out, $lang->$langs, +$clipboard). - E4: codex_audit.md HISTORICO banner. Deferred: SU2 (test-only layering, not a build violation); Words M1/T5/E3 (WIP). Verify: npm run check -> 1 pre-existing error (grafito), 0 new; sema 152/152, contracts 31/32 (1 pre-existing words), carousel 4/4, motion 22/22. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
import { HapticChannel, type HapticChannelDom, type HapticChannelOptions } from './chans/haptic';
import { SoundChannel, type SoundChannelDom, type SoundChannelOptions } from './chans/sound';
refactor(sound): extraer el motor Web Audio de sema al art `$sound` `sema/chans/sound.ts` tenía 407 líneas de las que ~255 (≈63%) eran maquinaria Web Audio —ciclo de vida del AudioContext, síntesis, samples, desbloqueo por gesto— mezclada con doctrina perceptiva. Deuda arrastrada, con tres síntomas medidos: el import era estático, así que toda app con sema metía el sintetizador en el bundle aunque el sonido estuviera apagado (que es el default); faltaba ciudadanía que `$scene` ya resuelve; y la costura de inyección (`audioContextFactory`) llevaba ahí sin usar desde el principio. Mismo movimiento que `$motion` hizo desde eidos: el art se lleva el RUNTIME, la capa conserva sus DATOS y su doctrina. - `$sound` / `EngineSound`: UN AudioContext por documento (los navegadores los limitan y el gesto de desbloqueo es por contexto), síntesis de earcon, samples con caché y fallback a síntesis, `autoSuspend` OPT-IN —suspender con la pestaña oculta es correcto para earcons y erróneo para contenido, así que es decisión de quien compone— y aviso cuando un segundo contexto va vivo. Puertos `SoundDom` / `SoundTimers` inyectados; no importa ningún otro art ni nada de `$uix/sema`, que es la prueba objetiva del corte. - `SoundChannel`: 407 → 136 líneas. Solo doctrina: el gate de `prepare`, la política de reducción y el reparto «el canal resuelve el NIVEL, el motor aplica la ganancia». Sema no gana ni un import: recibe el motor por puerto. - `uix.sound` en standalone y attach con `ownsSound` (idioma ya shipped: `ownsMotion` / `ownsScene`), fila `sound` en la tabla ejecutable `contracts.ts`, y `defineEngineSound()` para el camino de app. - Tests nuevos: `engine-sound.test.ts` (11), `sound-port.test.ts` (guard de deriva de tipos + la regla de propiedad) y `sound-e2e.test.ts`, que recorre `emit -> cascada -> canal -> art -> grafo real`: el camino que las 16 suites previas no cubrían porque paraban en canales falsos. Sin `diagnostics.ts` ni `errors.ts`, y es decisión: espejo de `$scene`, aquí todo fallo es degradación documentada, no error de programador. Verificación: 17 suites / 198 tests · `check` en la baseline exacta (73 errores, 0 propios) · `sound.test.ts` verde SIN tocar un solo assert, que era el criterio de que el movimiento fue value-preserving. Planes: `PLAN-sound-engine.md` (completo, con el registro de la revisión adversarial E-1..E-7), `PLAN-audio-player.md` (aparcado tras el análisis del reproductor, con sus correcciones en cabecera) y `CONTINUE-sound-engine.md` (handoff). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
import type { EngineSound } from '$sound';
import { VisualChannel, type VisualChannelOptions } from './chans/visual';
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
import {
AnnounceChannel,
type AnnounceChannelDom,
type AnnounceChannelOptions
} from './chans/announce';
import { SemaDuplicateChannelError } from './errors';
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
import {
applyMapOverrides,
resolveSignature,
type EffectiveSignature,
type SemaCascadeRule
} from './resolver';
import { SEMA_MAP, type SemaMap, type Sema } from './sema-map';
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
import type { DeltaValue, SemaPreferences } from './channels';
import type { SemanticSignal } from './signal';
import type { SignalProjector } from './projection';
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
import { semaDelay, type SemaTimerScheduler, type SemaTimerHandle } from './timers';
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
export interface EngineSemanticOverrides {
/**
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* Path-based mutations of the SEMA_MAP applied at construction. The
* canonical map is not mutated; only the overridden object path is cloned.
*/
runtime?: Record<string, DeltaValue>;
sema: revive resolver + signatures + real SoundChannel Restores the resolver-driven architecture that was deleted in the channel-modules refactor (commit a0a1485b). Each signal now flows through a single resolution that produces an `EffectiveSignature`, and each channel reads its own slice — so the visual hold, the sound earcon, and (future) motion / color / presence channels all share a single family-level perceptual budget defined in `SEMA_MAP`. New surfaces: - `src/uix/sema/sema-map.ts` — typed perceptual map (port of the legacy `sema-map.json`) with per-family `base` (motion / sound / color / presence) + `activeChannels` and per-intent `deltas` using the number / `{op:'add'}` / `{op:'multiply'}` / `{op:'replace'}` / recursive-merge semantics from before. - `src/uix/sema/resolver.ts` — pure `resolveSignature(signal)` that returns an `EffectiveSignature`. Empty signature when family is missing so channels skip rather than throw. Intent deltas only apply to valenced families. Optional `soundPack` per-event sample URLs. - `src/uix/sema/chans/sound.ts` — real `SoundChannel` (~290 lines, ported from a0a1485b^). Single AudioContext + master GainNode, auto-unlock for Safari/iOS via `click` / `touchstart` / `keydown` with capture, ADSR-lite synthesis (sine + 5th → biquad lowpass → envelope, AM modulator when roughness > 0.2), contour via `osc.detune`, optional sample playback with `AudioBuffer` cache. Errors absorbed silently — sema is ornamental. Architectural changes: - `Channel.handle(signal, effective)` — every channel now receives the resolved signature alongside the signal. Channels can ignore it. - `EngineSemantic.emit` resolves once per emit, dispatches both args. - `VisualChannel` hold resolution chain is now (highest priority first): 1. `signal.hold` 2. `effective.motion.duration` ← NEW: from sema-map 3. family fallback table (SEMA_DURATIONS) 4. engine `defaultHold` Behavior change: families with motion in the map (alert: 220ms, contact: 80ms, commit: 180ms, …) now use those values instead of the previous label-based 600ms / 120ms / 240ms fallbacks. The fallback table remains as a safety net for families without motion (sustain). - `EngineSemanticOptions` adds `sound?: true | false | SoundChannelOptions | Channel`. Sound is opt-in (audible side effects). Layouts wire `defineEngineSemantic({ sound: true })`. Tests: - New `resolver.test.ts` (7 tests) — locks down family base, intent deltas, multiplicative ops, transitional-family no-op, deep-clone. - `visual.test.ts` — every `handle` call now passes a stub `EffectiveSignature`. Two new tests: motion.duration takes the hold, and signal.hold overrides effective.motion.duration. - `engine.test.ts` — `makeChannel` mock signature updated. - `emit.test.ts` — alert family default test now expects 220ms (from sema-map) instead of 600ms. Verified: - vitest: 6 files / 53 tests green - smoke: 68/68 soma routes 200 OK - morfo-check: 66/66 morfos validate
5 months ago
/**
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* App-level cascade rules (CSS-style). Concatenated AFTER all
* per-component packs so app rules win on tie (declaration order).
sema: revive resolver + signatures + real SoundChannel Restores the resolver-driven architecture that was deleted in the channel-modules refactor (commit a0a1485b). Each signal now flows through a single resolution that produces an `EffectiveSignature`, and each channel reads its own slice — so the visual hold, the sound earcon, and (future) motion / color / presence channels all share a single family-level perceptual budget defined in `SEMA_MAP`. New surfaces: - `src/uix/sema/sema-map.ts` — typed perceptual map (port of the legacy `sema-map.json`) with per-family `base` (motion / sound / color / presence) + `activeChannels` and per-intent `deltas` using the number / `{op:'add'}` / `{op:'multiply'}` / `{op:'replace'}` / recursive-merge semantics from before. - `src/uix/sema/resolver.ts` — pure `resolveSignature(signal)` that returns an `EffectiveSignature`. Empty signature when family is missing so channels skip rather than throw. Intent deltas only apply to valenced families. Optional `soundPack` per-event sample URLs. - `src/uix/sema/chans/sound.ts` — real `SoundChannel` (~290 lines, ported from a0a1485b^). Single AudioContext + master GainNode, auto-unlock for Safari/iOS via `click` / `touchstart` / `keydown` with capture, ADSR-lite synthesis (sine + 5th → biquad lowpass → envelope, AM modulator when roughness > 0.2), contour via `osc.detune`, optional sample playback with `AudioBuffer` cache. Errors absorbed silently — sema is ornamental. Architectural changes: - `Channel.handle(signal, effective)` — every channel now receives the resolved signature alongside the signal. Channels can ignore it. - `EngineSemantic.emit` resolves once per emit, dispatches both args. - `VisualChannel` hold resolution chain is now (highest priority first): 1. `signal.hold` 2. `effective.motion.duration` ← NEW: from sema-map 3. family fallback table (SEMA_DURATIONS) 4. engine `defaultHold` Behavior change: families with motion in the map (alert: 220ms, contact: 80ms, commit: 180ms, …) now use those values instead of the previous label-based 600ms / 120ms / 240ms fallbacks. The fallback table remains as a safety net for families without motion (sustain). - `EngineSemanticOptions` adds `sound?: true | false | SoundChannelOptions | Channel`. Sound is opt-in (audible side effects). Layouts wire `defineEngineSemantic({ sound: true })`. Tests: - New `resolver.test.ts` (7 tests) — locks down family base, intent deltas, multiplicative ops, transitional-family no-op, deep-clone. - `visual.test.ts` — every `handle` call now passes a stub `EffectiveSignature`. Two new tests: motion.duration takes the hold, and signal.hold overrides effective.motion.duration. - `engine.test.ts` — `makeChannel` mock signature updated. - `emit.test.ts` — alert family default test now expects 220ms (from sema-map) instead of 600ms. Verified: - vitest: 6 files / 53 tests green - smoke: 68/68 soma routes 200 OK - morfo-check: 66/66 morfos validate
5 months ago
*/
cascade?: readonly SemaCascadeRule[];
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
export interface EngineSemanticOptions {
/** VisualChannel built-in. `false` to disable; object for opts; Channel to replace. */
visual?: false | VisualChannelOptions | Channel;
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
/** SoundChannel built-in. `true`/object/Channel to enable; `false`/undef to skip. */
sound?: true | false | SoundChannelOptions | Channel;
refactor(sound): extraer el motor Web Audio de sema al art `$sound` `sema/chans/sound.ts` tenía 407 líneas de las que ~255 (≈63%) eran maquinaria Web Audio —ciclo de vida del AudioContext, síntesis, samples, desbloqueo por gesto— mezclada con doctrina perceptiva. Deuda arrastrada, con tres síntomas medidos: el import era estático, así que toda app con sema metía el sintetizador en el bundle aunque el sonido estuviera apagado (que es el default); faltaba ciudadanía que `$scene` ya resuelve; y la costura de inyección (`audioContextFactory`) llevaba ahí sin usar desde el principio. Mismo movimiento que `$motion` hizo desde eidos: el art se lleva el RUNTIME, la capa conserva sus DATOS y su doctrina. - `$sound` / `EngineSound`: UN AudioContext por documento (los navegadores los limitan y el gesto de desbloqueo es por contexto), síntesis de earcon, samples con caché y fallback a síntesis, `autoSuspend` OPT-IN —suspender con la pestaña oculta es correcto para earcons y erróneo para contenido, así que es decisión de quien compone— y aviso cuando un segundo contexto va vivo. Puertos `SoundDom` / `SoundTimers` inyectados; no importa ningún otro art ni nada de `$uix/sema`, que es la prueba objetiva del corte. - `SoundChannel`: 407 → 136 líneas. Solo doctrina: el gate de `prepare`, la política de reducción y el reparto «el canal resuelve el NIVEL, el motor aplica la ganancia». Sema no gana ni un import: recibe el motor por puerto. - `uix.sound` en standalone y attach con `ownsSound` (idioma ya shipped: `ownsMotion` / `ownsScene`), fila `sound` en la tabla ejecutable `contracts.ts`, y `defineEngineSound()` para el camino de app. - Tests nuevos: `engine-sound.test.ts` (11), `sound-port.test.ts` (guard de deriva de tipos + la regla de propiedad) y `sound-e2e.test.ts`, que recorre `emit -> cascada -> canal -> art -> grafo real`: el camino que las 16 suites previas no cubrían porque paraban en canales falsos. Sin `diagnostics.ts` ni `errors.ts`, y es decisión: espejo de `$scene`, aquí todo fallo es degradación documentada, no error de programador. Verificación: 17 suites / 198 tests · `check` en la baseline exacta (73 errores, 0 propios) · `sound.test.ts` verde SIN tocar un solo assert, que era el criterio de que el movimiento fue value-preserving. Planes: `PLAN-sound-engine.md` (completo, con el registro de la revisión adversarial E-1..E-7), `PLAN-audio-player.md` (aparcado tras el análisis del reproductor, con sus correcciones en cabecera) y `CONTINUE-sound-engine.md` (handoff). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
/**
* The shared Web Audio runtime (`$sound`), injected by the composition root
* as `uix.sound`. Forwarded to the built-in `SoundChannel` so the whole
* document meets at ONE `AudioContext`: the browser caps them and the
* autoplay unlock gesture is per-context, so a second engine would leave
* one of the two mute. The engine is NOT owned here — whoever created it
* disposes it (`ownsSound` in `active-uix`). When absent, the channel
* creates and owns a private one (direct construction / unit tests).
*/
soundEngine?: EngineSound;
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
/** HapticChannel built-in. `true`/object/Channel to enable; `false`/undef to skip. */
haptic?: true | false | HapticChannelOptions | Channel;
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
/**
* AnnounceChannel built-in (accessible ARIA live region). `true`/object to
* enable; `false`/undef to skip. Opt-in like sound / haptic. Pass
* `{ announce }` to delegate to an existing announcer (e.g.
* `ActiveUix.announce`); otherwise it owns a pair of live regions built from
* the injected `dom`. Book `BK-SIGNAL-A11Y` / `BK-A11Y-CRITICAL`.
*/
announce?: true | false | AnnounceChannelOptions | Channel;
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
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
/**
* Optional diagnostics logger. When omitted, channel failures stay
* silent — a deliberate purity rule: the engine knows no platform, so it
* never falls back to `console` (the layer contract guards this). In
* practice the failures are NOT silent in apps: the ActiveUix
* composition root always injects its logger
* (`active-uix.svelte.ts` — `logger: eventsOpts.logger ?? logger`), so a
* silent engine only exists in bare constructions (unit tests /
* headless), by choice (SEM-2 adjudication, 2026-07-11).
*/
logger?: Logger;
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
/**
* Per-component packs of perceptual defaults. Each pack ships with
* the component (`src/uix/sema/components/{name}.ts`) and is
* concatenated into the cascade BEFORE `overrides.cascade`. Tree-
* shakable: only packs the app imports get included.
*/
components?: readonly Sema[];
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
docs(reconciliation): soma deps truth + checklist enforcement column + sema cascade renumber + archetype pointers Phase 1d of PLAN-docs-reconciliation. - soma/README s3 + SOMA_ARCHITECTURE s2/s6/s12 + popover/link-preview READMEs: positioning is the in-house engine (layers/floating + $ethereal); @floating-ui is devDep-only. Layers table gains Stacking / AxialDrag / ZoomPan / ImageProvider / ListSelection (popper/ is an empty leftover dir — not documented). clsx documented as it really is: NOT declared but still imported by props/props.ts via a svelte transitive (phantom dep — flagged for a decision, chip spawned). - COMPONENT_COMPLETION_CHECKLIST: new Enforcement column on every rule table (audit | tool:X | manual) so declared-but-unchecked rules are explicit; morfo rule IDs renamed M-* -> A-* to grep-match what component-audit.ts emits; stale rows fixed against the corrected script (D-1.2 v2 9-tab, D-3.1 single eidosSnippet, E-2.2 wrapper import); implemented-but-undeclared rules added (R-2.7 literal typography, A-3.4b verb/family pairing); A-3.3 family count decopied; sI now requires an honest Enforcement value for new rules. - Sema cascade numbering unified to 1 family - 2 intent - 3 morfo - 4 runtime - 5a packs - 5b app across sema/README, engine.ts, resolver.ts, resolver.test.ts and soma/runtime.svelte.ts (three divergent schemes coexisted; CLAUDE.md already uses the chosen one). - '24 archetypes' copied lists replaced by pointers to ARCHETYPE_VOCABULARY in active_architecture s6 + morfo/README. - TriggerOptions.semantic JSDoc: phantom defaultSemantic removed (implemented shape is additive allowedFamilies, D.11); 'warns via logger' claim corrected — the override is silently ignored on non-polymorphic events (no warn exists in code). npm run check: 61 errors = pre-existing baseline, 0 new. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
/** Override layers 4 (runtime path) + 5b (app cascade). */
overrides?: EngineSemanticOverrides;
/**
* Projector forwarded to the built-in VisualChannel. The engine stores no
* projector itself; channels own their prepare/materialization details.
*/
projector?: SignalProjector;
/**
* DOM writer forwarded to the built-in VisualChannel's default projector.
* In `active-uix` this is `uix.dom`, so sema projects event attrs through
* the same DOM owner as soma.
*/
dom?: DomApplier | (DomApplier & SoundChannelDom);
/**
* Managed timer scheduler (`uix.timers`) forwarded to the built-in
* channels. Every perceptual timer — the visual hold, the haptic delay,
* the sound earcon completion — runs on it instead of a raw `setTimeout`,
* so timing is cancellable, observable and test-deterministic. `active-uix`
* injects `uix.timers`; omitting it makes the channels fall back to
* `setTimeout` (unit-test / degraded path only).
*/
timers?: SemaTimerScheduler;
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
/**
* Per-channel reduction preferences (BK-REDUCTIONS, book ch. 32 §12 / ch.
* 33 §7). Forwarded to the built-in `sound` + `haptic` channels so an app
* can attenuate (`'reduce'`) or silence (`'off'`) a modality symmetrically.
* Read at dispatch, so backing it with reactive state makes it live.
* Visual-motion reduction lives in eidos CSS, not here.
*/
preferences?: SemaPreferences;
/**
* Frequency memory (BK-FREQ-MEMORY, book ch. 32 §11): repeated occurrences
* of the SAME event attenuate their non-visual channels so the 50th
* autosave does not sound like the first. `threat` is never damped. Default
* ON; set `false` to disable. Requires a `timers` scheduler for the decay
* window (falls back to a raw timeout in unit tests).
*/
frequencyMemory?: boolean;
/**
* Dominance arbiter (BK-DOMINANCE, book ch. 30 §7): when signals overlap in
* time, a signal dominated by a still-active one (evaluable > structural;
* higher activation; recency) has its non-visual channels muted while its
* visual hold survives. `threat` is never muted. Default ON; set `false` to
* disable.
*/
dominance?: boolean;
}
feat(uix): persistence + a11ySemantic + polymorphic core (book §5.3, §6, §9) Three book canon items codified in code: **Persistence (libro §6.1)** — separa `hold` (perceptual min) de `persistence` (lifecycle real): - New `SignalPersistence = 'transient' | 'untilAction' | 'untilFix' | 'stateBound'` - New `SEMA_HOLDS_BY_INTENT` canonical lookup table (`src/uix/sema/holds.ts`) - `EngineSemantic.emit()` returns the resolved signal id; keeps projection alive past hold for non-transient. Exposes `clear(id)`, `clearTarget(target)`, `hasActive(id)`. - `SomaRuntime.trigger()` returns `TriggerResult { id?, persistence? }`. Exposes `clearSignal(id)`, `clearTarget(target)`, `partRef(part)`. **a11ySemantic (libro §9.1)**: - New `MorfoA11ySemantic` (requiresPersistentTrace, requiresLiveRegion, requiresFocusMove, keyboardEquivalent, reducedMotionFallback) on MorfoEvent. - `ActiveDom.prefersReducedMotion`: reactive tracker via media query (`src/arts/adom/reduced-motion.svelte.ts`). - `ActiveUix.announce(msg, priority?, timeout?)`: lazy-created live region via `dom.writeNode`. - `Soma.runtime()` auto-wires `sources.announce`. - `SomaRuntime.trigger()` honors a11ySemantic after emit (live region, focus, reduced-motion fallback including `channels: []` for 'state'). **Polymorphic events (libro §5.3)** — ADITIVO sobre shape concreto: - `allowedFamilies?: readonly SemaFamily[]` opcional en MorfoEventSemantic. - Provider override via `runtime.trigger(name, { semantic })`. - Default family of the morfo is implicitly allowed. - New `SomaRuntimePolymorphicError`. - New `isPolymorphicSemantic` type guard. Synthetic `prewriteFixtureMorfo` (src/uix/morfo/test-fixtures.ts) decouples compile + runtime tests from the production morfo catalogue. 671 tests pass in sema + morfo + soma + adom scopes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
interface PersistentSignalEntry {
readonly preparations: readonly ChannelPreparation[];
readonly target: HTMLElement | undefined;
}
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
// ── Frequency memory (C-2) + dominance (C-3) tuning ───────────────────────────
/** Non-threat signals of the same key start attenuating after this count. */
const FREQ_THRESHOLD = 2;
/** Attenuation lost per repeat past the threshold. */
const FREQ_STEP = 0.15;
/** Attenuation floor — repeated signals never fall below this fraction. */
const FREQ_FLOOR = 0.25;
/** Window of silence (ms) after which a key's repeat count resets to full. */
const FREQ_WINDOW_MS = 2000;
/** How long (ms) an occurrence counts as "active" for dominance overlap. */
const DOMINANCE_WINDOW_MS = 400;
/**
* Scale (or, at `factor <= 0`, mute) the non-visual channels. Muting DROPS
* `sound` / `haptic` from `activeChannels` rather than scaling to zero, because
* haptic intensity is floored (a `0` still buzzes). The visual hold and the
* accessible `announce` channel are never touched.
*/
function attenuateNonVisual(effective: EffectiveSignature, factor: number): EffectiveSignature {
if (factor >= 1) return effective;
if (factor <= 0) {
return {
...effective,
activeChannels: effective.activeChannels.filter((c) => c !== 'sound' && c !== 'haptic')
};
}
const next: EffectiveSignature = { ...effective };
if (next.sound) next.sound = { ...next.sound, gain: next.sound.gain * factor };
if (next.haptic) next.haptic = { ...next.haptic, intensity: next.haptic.intensity * factor };
return next;
}
/** Activation level implied by an intent (book ch. 31 §5 / ch. 32 §2). */
function activationOf(intent: EffectiveSignature['intent']): number {
switch (intent) {
case 'threat':
return 4;
case 'fulfill':
return 3;
case 'risk':
return 2;
case 'loss':
case 'affirm':
return 1;
default:
return 0;
}
}
/**
* Dominance rank of an occurrence (higher wins). One comparable number for the
* book's criteria: an evaluable event (commit / signal) outranks a structural
* one; within that, higher activation outranks lower. Recency is handled by the
* caller using a strict `>` so an equal-ranked newcomer stays audible.
*/
function occurrenceRank(effective: EffectiveSignature): number {
const evaluable = effective.family === 'commit' || effective.family === 'signal' ? 1 : 0;
return evaluable * 10 + activationOf(effective.intent);
}
export class EngineSemantic {
private readonly channels = new Map<string, Channel>();
private nextSignalId = 0;
private readonly map: SemaMap;
private readonly cascade: readonly SemaCascadeRule[];
private readonly logger: Logger | undefined;
feat(uix): persistence + a11ySemantic + polymorphic core (book §5.3, §6, §9) Three book canon items codified in code: **Persistence (libro §6.1)** — separa `hold` (perceptual min) de `persistence` (lifecycle real): - New `SignalPersistence = 'transient' | 'untilAction' | 'untilFix' | 'stateBound'` - New `SEMA_HOLDS_BY_INTENT` canonical lookup table (`src/uix/sema/holds.ts`) - `EngineSemantic.emit()` returns the resolved signal id; keeps projection alive past hold for non-transient. Exposes `clear(id)`, `clearTarget(target)`, `hasActive(id)`. - `SomaRuntime.trigger()` returns `TriggerResult { id?, persistence? }`. Exposes `clearSignal(id)`, `clearTarget(target)`, `partRef(part)`. **a11ySemantic (libro §9.1)**: - New `MorfoA11ySemantic` (requiresPersistentTrace, requiresLiveRegion, requiresFocusMove, keyboardEquivalent, reducedMotionFallback) on MorfoEvent. - `ActiveDom.prefersReducedMotion`: reactive tracker via media query (`src/arts/adom/reduced-motion.svelte.ts`). - `ActiveUix.announce(msg, priority?, timeout?)`: lazy-created live region via `dom.writeNode`. - `Soma.runtime()` auto-wires `sources.announce`. - `SomaRuntime.trigger()` honors a11ySemantic after emit (live region, focus, reduced-motion fallback including `channels: []` for 'state'). **Polymorphic events (libro §5.3)** — ADITIVO sobre shape concreto: - `allowedFamilies?: readonly SemaFamily[]` opcional en MorfoEventSemantic. - Provider override via `runtime.trigger(name, { semantic })`. - Default family of the morfo is implicitly allowed. - New `SomaRuntimePolymorphicError`. - New `isPolymorphicSemantic` type guard. Synthetic `prewriteFixtureMorfo` (src/uix/morfo/test-fixtures.ts) decouples compile + runtime tests from the production morfo catalogue. 671 tests pass in sema + morfo + soma + adom scopes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
/**
* Active non-transient signals — projection handles kept alive past the
* hold for `untilAction` / `untilFix` / `stateBound`. Cleared via
* `clear(id)` or `clearTarget(target)`. Per book §6.1: persistence is
* caller-managed; the engine only holds the cleanup handle.
*/
private readonly active = new Map<string, PersistentSignalEntry>();
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
private readonly timers: SemaTimerScheduler | undefined;
private readonly frequencyMemory: boolean;
private readonly dominance: boolean;
/** Per-event-key repeat counts for frequency memory (C-2). */
private readonly freqCounts = new Map<string, { count: number; reset?: SemaTimerHandle }>();
/** Occurrences still "active" for dominance (C-3), keyed by occurrence id. */
private readonly activeOccurrences = new Map<string, { rank: number; expire: SemaTimerHandle }>();
constructor(opts: EngineSemanticOptions = {}) {
this.logger = opts.logger;
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
this.timers = opts.timers;
this.frequencyMemory = opts.frequencyMemory ?? true;
this.dominance = opts.dominance ?? true;
docs(reconciliation): soma deps truth + checklist enforcement column + sema cascade renumber + archetype pointers Phase 1d of PLAN-docs-reconciliation. - soma/README s3 + SOMA_ARCHITECTURE s2/s6/s12 + popover/link-preview READMEs: positioning is the in-house engine (layers/floating + $ethereal); @floating-ui is devDep-only. Layers table gains Stacking / AxialDrag / ZoomPan / ImageProvider / ListSelection (popper/ is an empty leftover dir — not documented). clsx documented as it really is: NOT declared but still imported by props/props.ts via a svelte transitive (phantom dep — flagged for a decision, chip spawned). - COMPONENT_COMPLETION_CHECKLIST: new Enforcement column on every rule table (audit | tool:X | manual) so declared-but-unchecked rules are explicit; morfo rule IDs renamed M-* -> A-* to grep-match what component-audit.ts emits; stale rows fixed against the corrected script (D-1.2 v2 9-tab, D-3.1 single eidosSnippet, E-2.2 wrapper import); implemented-but-undeclared rules added (R-2.7 literal typography, A-3.4b verb/family pairing); A-3.3 family count decopied; sI now requires an honest Enforcement value for new rules. - Sema cascade numbering unified to 1 family - 2 intent - 3 morfo - 4 runtime - 5a packs - 5b app across sema/README, engine.ts, resolver.ts, resolver.test.ts and soma/runtime.svelte.ts (three divergent schemes coexisted; CLAUDE.md already uses the chosen one). - '24 archetypes' copied lists replaced by pointers to ARCHETYPE_VOCABULARY in active_architecture s6 + morfo/README. - TriggerOptions.semantic JSDoc: phantom defaultSemantic removed (implemented shape is additive allowedFamilies, D.11); 'warns via logger' claim corrected — the override is silently ignored on non-polymorphic events (no warn exists in code). npm run check: 61 errors = pre-existing baseline, 0 new. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
// Layer 4 — runtime path overrides baked into the map at construction.
this.map = applyMapOverrides(SEMA_MAP, opts.overrides?.runtime);
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
docs(reconciliation): soma deps truth + checklist enforcement column + sema cascade renumber + archetype pointers Phase 1d of PLAN-docs-reconciliation. - soma/README s3 + SOMA_ARCHITECTURE s2/s6/s12 + popover/link-preview READMEs: positioning is the in-house engine (layers/floating + $ethereal); @floating-ui is devDep-only. Layers table gains Stacking / AxialDrag / ZoomPan / ImageProvider / ListSelection (popper/ is an empty leftover dir — not documented). clsx documented as it really is: NOT declared but still imported by props/props.ts via a svelte transitive (phantom dep — flagged for a decision, chip spawned). - COMPONENT_COMPLETION_CHECKLIST: new Enforcement column on every rule table (audit | tool:X | manual) so declared-but-unchecked rules are explicit; morfo rule IDs renamed M-* -> A-* to grep-match what component-audit.ts emits; stale rows fixed against the corrected script (D-1.2 v2 9-tab, D-3.1 single eidosSnippet, E-2.2 wrapper import); implemented-but-undeclared rules added (R-2.7 literal typography, A-3.4b verb/family pairing); A-3.3 family count decopied; sI now requires an honest Enforcement value for new rules. - Sema cascade numbering unified to 1 family - 2 intent - 3 morfo - 4 runtime - 5a packs - 5b app across sema/README, engine.ts, resolver.ts, resolver.test.ts and soma/runtime.svelte.ts (three divergent schemes coexisted; CLAUDE.md already uses the chosen one). - '24 archetypes' copied lists replaced by pointers to ARCHETYPE_VOCABULARY in active_architecture s6 + morfo/README. - TriggerOptions.semantic JSDoc: phantom defaultSemantic removed (implemented shape is additive allowedFamilies, D.11); 'warns via logger' claim corrected — the override is silently ignored on non-polymorphic events (no warn exists in code). npm run check: 61 errors = pre-existing baseline, 0 new. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
// Layers 5a/5b — flatten per-component packs (lower precedence) +
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// app-level cascade (higher precedence on tie).
const packCascade: SemaCascadeRule[] = [];
const preloadUrls = new Set<string>();
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
for (const pack of opts.components ?? []) {
for (const rule of pack.cascade) {
packCascade.push(rule as SemaCascadeRule);
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
for (const url of pack.preloadSamples ?? []) {
preloadUrls.add(url);
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
}
this.cascade = [...packCascade, ...(opts.overrides?.cascade ?? [])];
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// Channels.
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
if (opts.visual !== false) {
const visualChannel: Channel = isChannel(opts.visual)
? opts.visual
: new VisualChannel({
...(opts.visual ?? {}),
dom: opts.dom,
projector: opts.projector,
timers: opts.timers
});
this.register(visualChannel);
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
}
sema: revive resolver + signatures + real SoundChannel Restores the resolver-driven architecture that was deleted in the channel-modules refactor (commit a0a1485b). Each signal now flows through a single resolution that produces an `EffectiveSignature`, and each channel reads its own slice — so the visual hold, the sound earcon, and (future) motion / color / presence channels all share a single family-level perceptual budget defined in `SEMA_MAP`. New surfaces: - `src/uix/sema/sema-map.ts` — typed perceptual map (port of the legacy `sema-map.json`) with per-family `base` (motion / sound / color / presence) + `activeChannels` and per-intent `deltas` using the number / `{op:'add'}` / `{op:'multiply'}` / `{op:'replace'}` / recursive-merge semantics from before. - `src/uix/sema/resolver.ts` — pure `resolveSignature(signal)` that returns an `EffectiveSignature`. Empty signature when family is missing so channels skip rather than throw. Intent deltas only apply to valenced families. Optional `soundPack` per-event sample URLs. - `src/uix/sema/chans/sound.ts` — real `SoundChannel` (~290 lines, ported from a0a1485b^). Single AudioContext + master GainNode, auto-unlock for Safari/iOS via `click` / `touchstart` / `keydown` with capture, ADSR-lite synthesis (sine + 5th → biquad lowpass → envelope, AM modulator when roughness > 0.2), contour via `osc.detune`, optional sample playback with `AudioBuffer` cache. Errors absorbed silently — sema is ornamental. Architectural changes: - `Channel.handle(signal, effective)` — every channel now receives the resolved signature alongside the signal. Channels can ignore it. - `EngineSemantic.emit` resolves once per emit, dispatches both args. - `VisualChannel` hold resolution chain is now (highest priority first): 1. `signal.hold` 2. `effective.motion.duration` ← NEW: from sema-map 3. family fallback table (SEMA_DURATIONS) 4. engine `defaultHold` Behavior change: families with motion in the map (alert: 220ms, contact: 80ms, commit: 180ms, …) now use those values instead of the previous label-based 600ms / 120ms / 240ms fallbacks. The fallback table remains as a safety net for families without motion (sustain). - `EngineSemanticOptions` adds `sound?: true | false | SoundChannelOptions | Channel`. Sound is opt-in (audible side effects). Layouts wire `defineEngineSemantic({ sound: true })`. Tests: - New `resolver.test.ts` (7 tests) — locks down family base, intent deltas, multiplicative ops, transitional-family no-op, deep-clone. - `visual.test.ts` — every `handle` call now passes a stub `EffectiveSignature`. Two new tests: motion.duration takes the hold, and signal.hold overrides effective.motion.duration. - `engine.test.ts` — `makeChannel` mock signature updated. - `emit.test.ts` — alert family default test now expects 220ms (from sema-map) instead of 600ms. Verified: - vitest: 6 files / 53 tests green - smoke: 68/68 soma routes 200 OK - morfo-check: 66/66 morfos validate
5 months ago
if (opts.sound !== undefined && opts.sound !== false) {
let soundChannel: Channel;
if (isChannel(opts.sound)) {
soundChannel = opts.sound;
} else {
const soundOptions = opts.sound === true ? {} : opts.sound;
soundChannel = new SoundChannel({
...soundOptions,
refactor(sound): extraer el motor Web Audio de sema al art `$sound` `sema/chans/sound.ts` tenía 407 líneas de las que ~255 (≈63%) eran maquinaria Web Audio —ciclo de vida del AudioContext, síntesis, samples, desbloqueo por gesto— mezclada con doctrina perceptiva. Deuda arrastrada, con tres síntomas medidos: el import era estático, así que toda app con sema metía el sintetizador en el bundle aunque el sonido estuviera apagado (que es el default); faltaba ciudadanía que `$scene` ya resuelve; y la costura de inyección (`audioContextFactory`) llevaba ahí sin usar desde el principio. Mismo movimiento que `$motion` hizo desde eidos: el art se lleva el RUNTIME, la capa conserva sus DATOS y su doctrina. - `$sound` / `EngineSound`: UN AudioContext por documento (los navegadores los limitan y el gesto de desbloqueo es por contexto), síntesis de earcon, samples con caché y fallback a síntesis, `autoSuspend` OPT-IN —suspender con la pestaña oculta es correcto para earcons y erróneo para contenido, así que es decisión de quien compone— y aviso cuando un segundo contexto va vivo. Puertos `SoundDom` / `SoundTimers` inyectados; no importa ningún otro art ni nada de `$uix/sema`, que es la prueba objetiva del corte. - `SoundChannel`: 407 → 136 líneas. Solo doctrina: el gate de `prepare`, la política de reducción y el reparto «el canal resuelve el NIVEL, el motor aplica la ganancia». Sema no gana ni un import: recibe el motor por puerto. - `uix.sound` en standalone y attach con `ownsSound` (idioma ya shipped: `ownsMotion` / `ownsScene`), fila `sound` en la tabla ejecutable `contracts.ts`, y `defineEngineSound()` para el camino de app. - Tests nuevos: `engine-sound.test.ts` (11), `sound-port.test.ts` (guard de deriva de tipos + la regla de propiedad) y `sound-e2e.test.ts`, que recorre `emit -> cascada -> canal -> art -> grafo real`: el camino que las 16 suites previas no cubrían porque paraban en canales falsos. Sin `diagnostics.ts` ni `errors.ts`, y es decisión: espejo de `$scene`, aquí todo fallo es degradación documentada, no error de programador. Verificación: 17 suites / 198 tests · `check` en la baseline exacta (73 errores, 0 propios) · `sound.test.ts` verde SIN tocar un solo assert, que era el criterio de que el movimiento fue value-preserving. Planes: `PLAN-sound-engine.md` (completo, con el registro de la revisión adversarial E-1..E-7), `PLAN-audio-player.md` (aparcado tras el análisis del reproductor, con sus correcciones en cabecera) y `CONTINUE-sound-engine.md` (handoff). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
engine: soundOptions.engine ?? opts.soundEngine,
dom: soundOptions.dom ?? (isSoundChannelDom(opts.dom) ? opts.dom : undefined),
timers: soundOptions.timers ?? opts.timers,
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
preferences: soundOptions.preferences ?? opts.preferences,
logger: opts.logger
});
}
this.register(soundChannel);
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// Pre-decode the WAVs declared by component packs so the first
refactor(sound): extraer el motor Web Audio de sema al art `$sound` `sema/chans/sound.ts` tenía 407 líneas de las que ~255 (≈63%) eran maquinaria Web Audio —ciclo de vida del AudioContext, síntesis, samples, desbloqueo por gesto— mezclada con doctrina perceptiva. Deuda arrastrada, con tres síntomas medidos: el import era estático, así que toda app con sema metía el sintetizador en el bundle aunque el sonido estuviera apagado (que es el default); faltaba ciudadanía que `$scene` ya resuelve; y la costura de inyección (`audioContextFactory`) llevaba ahí sin usar desde el principio. Mismo movimiento que `$motion` hizo desde eidos: el art se lleva el RUNTIME, la capa conserva sus DATOS y su doctrina. - `$sound` / `EngineSound`: UN AudioContext por documento (los navegadores los limitan y el gesto de desbloqueo es por contexto), síntesis de earcon, samples con caché y fallback a síntesis, `autoSuspend` OPT-IN —suspender con la pestaña oculta es correcto para earcons y erróneo para contenido, así que es decisión de quien compone— y aviso cuando un segundo contexto va vivo. Puertos `SoundDom` / `SoundTimers` inyectados; no importa ningún otro art ni nada de `$uix/sema`, que es la prueba objetiva del corte. - `SoundChannel`: 407 → 136 líneas. Solo doctrina: el gate de `prepare`, la política de reducción y el reparto «el canal resuelve el NIVEL, el motor aplica la ganancia». Sema no gana ni un import: recibe el motor por puerto. - `uix.sound` en standalone y attach con `ownsSound` (idioma ya shipped: `ownsMotion` / `ownsScene`), fila `sound` en la tabla ejecutable `contracts.ts`, y `defineEngineSound()` para el camino de app. - Tests nuevos: `engine-sound.test.ts` (11), `sound-port.test.ts` (guard de deriva de tipos + la regla de propiedad) y `sound-e2e.test.ts`, que recorre `emit -> cascada -> canal -> art -> grafo real`: el camino que las 16 suites previas no cubrían porque paraban en canales falsos. Sin `diagnostics.ts` ni `errors.ts`, y es decisión: espejo de `$scene`, aquí todo fallo es degradación documentada, no error de programador. Verificación: 17 suites / 198 tests · `check` en la baseline exacta (73 errores, 0 propios) · `sound.test.ts` verde SIN tocar un solo assert, que era el criterio de que el movimiento fue value-preserving. Planes: `PLAN-sound-engine.md` (completo, con el registro de la revisión adversarial E-1..E-7), `PLAN-audio-player.md` (aparcado tras el análisis del reproductor, con sus correcciones en cabecera) y `CONTINUE-sound-engine.md` (handoff). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
// emit doesn't pay the fetch + decode latency. The built-in channel
// delegates to its engine; a REPLACEMENT channel (`opts.sound` as a
// Channel) has no such method, so the shared engine — when the root
// injected one — is preloaded directly. Before the extraction that
// case simply skipped the preload.
if (preloadUrls.size > 0) {
if (soundChannel instanceof SoundChannel) {
void soundChannel.preloadSamples([...preloadUrls]);
} else if (opts.soundEngine) {
void opts.soundEngine.preload([...preloadUrls]);
}
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
}
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
if (opts.announce !== undefined && opts.announce !== false) {
let announceChannel: Channel;
if (isChannel(opts.announce)) {
announceChannel = opts.announce;
} else {
const announceOptions = opts.announce === true ? {} : opts.announce;
announceChannel = new AnnounceChannel({
...announceOptions,
dom: announceOptions.dom ?? (isAnnounceChannelDom(opts.dom) ? opts.dom : undefined)
});
}
this.register(announceChannel);
}
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
if (opts.haptic !== undefined && opts.haptic !== false) {
fix: address architectural audit findings + sync ecosystem docs Audit: src/audit-opus-4-6-26.md. All non-words findings remediated. Code: - E1: navigation-menu indicator data-state visible -> open (render bug; the active underline was permanently invisible). Clears the only invalid lint selector. - SO1 + E2: raw `new ResizeObserver` -> ActiveDom.observeResize in carousel-provider and the canvas-text useContainerWidth hook (+ s-text / s-text-virtual-list pass eidos.dom). iframe/popup-safe, lifecycle-tracked. - A1: defineUixServices now registers `motion`, so attach-mode app.motion is real and the active-uix fallback becomes the true edge case (test guard updated). - A2/A4: contracts.ts pins `motion` + `announce` in ActiveUixServiceContract + publicSurface; dispose() comment corrected. - SO2: carousel drops the hardcoded `transform 300ms ease-out` (the recipe already handles it via [data-dragging]); also fixed the recipe's undefined `--duration-base` token -> `--duration-slow` (it was masked by the inline). - S1: HapticChannel reduced-motion via an injected ActiveDom port (mirrors SoundChannelDom) instead of global matchMedia. - T1: 10 sites repointed `$libs/dom` -> `$adom` (sema x5 + its tests x3, active-uix value import, arts/prefs). - M2 / S2 / S3: dead code removed (button `states:['idle','loading']`, SemaRuntimeChannelId, SEMA_VALENCED_FAMILY_LIST). Docs: - Motion-as-service reflected across the ecosystem: CLAUDE.md (aliases + arch + service note), arts/README, active_architecture, soma SOMA_ARCHITECTURE, eidos README, eidos-motion.md. - X1: CLAUDE.md "5 canonical channels" (false) -> the single canonical narrative (8 book channels; Sema runs 2 + visual meta-channel, Eidos materializes 5). - X2/X3/X4: sema/README (8 families + intentRequirement/intentGuidance split), types.ts JSDoc (SEMA_INTENT_POLICY -> SEMA_FAMILY_POLICY), engine.ts cascade 6->5, alias table ($frontend out, $lang->$langs, +$clipboard). - E4: codex_audit.md HISTORICO banner. Deferred: SU2 (test-only layering, not a build violation); Words M1/T5/E3 (WIP). Verify: npm run check -> 1 pre-existing error (grafito), 0 new; sema 152/152, contracts 31/32 (1 pre-existing words), carousel 4/4, motion 22/22. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
let hapticChannel: Channel;
if (isChannel(opts.haptic)) {
hapticChannel = opts.haptic;
} else {
const hapticOptions = opts.haptic === true ? {} : opts.haptic;
hapticChannel = new HapticChannel({
...hapticOptions,
dom: hapticOptions.dom ?? (isHapticChannelDom(opts.dom) ? opts.dom : undefined),
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
timers: hapticOptions.timers ?? opts.timers,
preferences: hapticOptions.preferences ?? opts.preferences
fix: address architectural audit findings + sync ecosystem docs Audit: src/audit-opus-4-6-26.md. All non-words findings remediated. Code: - E1: navigation-menu indicator data-state visible -> open (render bug; the active underline was permanently invisible). Clears the only invalid lint selector. - SO1 + E2: raw `new ResizeObserver` -> ActiveDom.observeResize in carousel-provider and the canvas-text useContainerWidth hook (+ s-text / s-text-virtual-list pass eidos.dom). iframe/popup-safe, lifecycle-tracked. - A1: defineUixServices now registers `motion`, so attach-mode app.motion is real and the active-uix fallback becomes the true edge case (test guard updated). - A2/A4: contracts.ts pins `motion` + `announce` in ActiveUixServiceContract + publicSurface; dispose() comment corrected. - SO2: carousel drops the hardcoded `transform 300ms ease-out` (the recipe already handles it via [data-dragging]); also fixed the recipe's undefined `--duration-base` token -> `--duration-slow` (it was masked by the inline). - S1: HapticChannel reduced-motion via an injected ActiveDom port (mirrors SoundChannelDom) instead of global matchMedia. - T1: 10 sites repointed `$libs/dom` -> `$adom` (sema x5 + its tests x3, active-uix value import, arts/prefs). - M2 / S2 / S3: dead code removed (button `states:['idle','loading']`, SemaRuntimeChannelId, SEMA_VALENCED_FAMILY_LIST). Docs: - Motion-as-service reflected across the ecosystem: CLAUDE.md (aliases + arch + service note), arts/README, active_architecture, soma SOMA_ARCHITECTURE, eidos README, eidos-motion.md. - X1: CLAUDE.md "5 canonical channels" (false) -> the single canonical narrative (8 book channels; Sema runs 2 + visual meta-channel, Eidos materializes 5). - X2/X3/X4: sema/README (8 families + intentRequirement/intentGuidance split), types.ts JSDoc (SEMA_INTENT_POLICY -> SEMA_FAMILY_POLICY), engine.ts cascade 6->5, alias table ($frontend out, $lang->$langs, +$clipboard). - E4: codex_audit.md HISTORICO banner. Deferred: SU2 (test-only layering, not a build violation); Words M1/T5/E3 (WIP). Verify: npm run check -> 1 pre-existing error (grafito), 0 new; sema 152/152, contracts 31/32 (1 pre-existing words), carousel 4/4, motion 22/22. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
});
}
this.register(hapticChannel);
sema: revive resolver + signatures + real SoundChannel Restores the resolver-driven architecture that was deleted in the channel-modules refactor (commit a0a1485b). Each signal now flows through a single resolution that produces an `EffectiveSignature`, and each channel reads its own slice — so the visual hold, the sound earcon, and (future) motion / color / presence channels all share a single family-level perceptual budget defined in `SEMA_MAP`. New surfaces: - `src/uix/sema/sema-map.ts` — typed perceptual map (port of the legacy `sema-map.json`) with per-family `base` (motion / sound / color / presence) + `activeChannels` and per-intent `deltas` using the number / `{op:'add'}` / `{op:'multiply'}` / `{op:'replace'}` / recursive-merge semantics from before. - `src/uix/sema/resolver.ts` — pure `resolveSignature(signal)` that returns an `EffectiveSignature`. Empty signature when family is missing so channels skip rather than throw. Intent deltas only apply to valenced families. Optional `soundPack` per-event sample URLs. - `src/uix/sema/chans/sound.ts` — real `SoundChannel` (~290 lines, ported from a0a1485b^). Single AudioContext + master GainNode, auto-unlock for Safari/iOS via `click` / `touchstart` / `keydown` with capture, ADSR-lite synthesis (sine + 5th → biquad lowpass → envelope, AM modulator when roughness > 0.2), contour via `osc.detune`, optional sample playback with `AudioBuffer` cache. Errors absorbed silently — sema is ornamental. Architectural changes: - `Channel.handle(signal, effective)` — every channel now receives the resolved signature alongside the signal. Channels can ignore it. - `EngineSemantic.emit` resolves once per emit, dispatches both args. - `VisualChannel` hold resolution chain is now (highest priority first): 1. `signal.hold` 2. `effective.motion.duration` ← NEW: from sema-map 3. family fallback table (SEMA_DURATIONS) 4. engine `defaultHold` Behavior change: families with motion in the map (alert: 220ms, contact: 80ms, commit: 180ms, …) now use those values instead of the previous label-based 600ms / 120ms / 240ms fallbacks. The fallback table remains as a safety net for families without motion (sustain). - `EngineSemanticOptions` adds `sound?: true | false | SoundChannelOptions | Channel`. Sound is opt-in (audible side effects). Layouts wire `defineEngineSemantic({ sound: true })`. Tests: - New `resolver.test.ts` (7 tests) — locks down family base, intent deltas, multiplicative ops, transitional-family no-op, deep-clone. - `visual.test.ts` — every `handle` call now passes a stub `EffectiveSignature`. Two new tests: motion.duration takes the hold, and signal.hold overrides effective.motion.duration. - `engine.test.ts` — `makeChannel` mock signature updated. - `emit.test.ts` — alert family default test now expects 220ms (from sema-map) instead of 600ms. Verified: - vitest: 6 files / 53 tests green - smoke: 68/68 soma routes 200 OK - morfo-check: 66/66 morfos validate
5 months ago
}
}
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
register(channel: Channel): void {
if (this.channels.has(channel.id)) {
throw new SemaDuplicateChannelError(channel.id);
}
this.channels.set(channel.id, channel);
}
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
/**
* Despacha una señal a todos los canales registrados.
*
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* Lifecycle:
* 1. Generate id and run channel prepare hooks.
* VisualChannel.prepare() projects `data-event-*` on target.
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* 2. resolveSignature → cascade selectors match against the now-
* projected target via native `target.matches()`.
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* 3. Fire-and-forget non-visual channels.
* 4. Await visual channel hold.
* 5. Cleanup prepare handles.
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
*
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
* Sequential strict: the caller's structural commit happens AFTER
* cleanup. State change is strictly ordered after the perceptual window.
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
*/
feat(uix): persistence + a11ySemantic + polymorphic core (book §5.3, §6, §9) Three book canon items codified in code: **Persistence (libro §6.1)** — separa `hold` (perceptual min) de `persistence` (lifecycle real): - New `SignalPersistence = 'transient' | 'untilAction' | 'untilFix' | 'stateBound'` - New `SEMA_HOLDS_BY_INTENT` canonical lookup table (`src/uix/sema/holds.ts`) - `EngineSemantic.emit()` returns the resolved signal id; keeps projection alive past hold for non-transient. Exposes `clear(id)`, `clearTarget(target)`, `hasActive(id)`. - `SomaRuntime.trigger()` returns `TriggerResult { id?, persistence? }`. Exposes `clearSignal(id)`, `clearTarget(target)`, `partRef(part)`. **a11ySemantic (libro §9.1)**: - New `MorfoA11ySemantic` (requiresPersistentTrace, requiresLiveRegion, requiresFocusMove, keyboardEquivalent, reducedMotionFallback) on MorfoEvent. - `ActiveDom.prefersReducedMotion`: reactive tracker via media query (`src/arts/adom/reduced-motion.svelte.ts`). - `ActiveUix.announce(msg, priority?, timeout?)`: lazy-created live region via `dom.writeNode`. - `Soma.runtime()` auto-wires `sources.announce`. - `SomaRuntime.trigger()` honors a11ySemantic after emit (live region, focus, reduced-motion fallback including `channels: []` for 'state'). **Polymorphic events (libro §5.3)** — ADITIVO sobre shape concreto: - `allowedFamilies?: readonly SemaFamily[]` opcional en MorfoEventSemantic. - Provider override via `runtime.trigger(name, { semantic })`. - Default family of the morfo is implicitly allowed. - New `SomaRuntimePolymorphicError`. - New `isPolymorphicSemantic` type guard. Synthetic `prewriteFixtureMorfo` (src/uix/morfo/test-fixtures.ts) decouples compile + runtime tests from the production morfo catalogue. 671 tests pass in sema + morfo + soma + adom scopes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
async emit(signal: SemanticSignal): Promise<string> {
const id = signal.id ?? `sig-${this.nextSignalId++}`;
const enriched: SemanticSignal = { ...signal, id };
docs(reconciliation): soma deps truth + checklist enforcement column + sema cascade renumber + archetype pointers Phase 1d of PLAN-docs-reconciliation. - soma/README s3 + SOMA_ARCHITECTURE s2/s6/s12 + popover/link-preview READMEs: positioning is the in-house engine (layers/floating + $ethereal); @floating-ui is devDep-only. Layers table gains Stacking / AxialDrag / ZoomPan / ImageProvider / ListSelection (popper/ is an empty leftover dir — not documented). clsx documented as it really is: NOT declared but still imported by props/props.ts via a svelte transitive (phantom dep — flagged for a decision, chip spawned). - COMPONENT_COMPLETION_CHECKLIST: new Enforcement column on every rule table (audit | tool:X | manual) so declared-but-unchecked rules are explicit; morfo rule IDs renamed M-* -> A-* to grep-match what component-audit.ts emits; stale rows fixed against the corrected script (D-1.2 v2 9-tab, D-3.1 single eidosSnippet, E-2.2 wrapper import); implemented-but-undeclared rules added (R-2.7 literal typography, A-3.4b verb/family pairing); A-3.3 family count decopied; sI now requires an honest Enforcement value for new rules. - Sema cascade numbering unified to 1 family - 2 intent - 3 morfo - 4 runtime - 5a packs - 5b app across sema/README, engine.ts, resolver.ts, resolver.test.ts and soma/runtime.svelte.ts (three divergent schemes coexisted; CLAUDE.md already uses the chosen one). - '24 archetypes' copied lists replaced by pointers to ARCHETYPE_VOCABULARY in active_architecture s6 + morfo/README. - TriggerOptions.semantic JSDoc: phantom defaultSemantic removed (implemented shape is additive allowedFamilies, D.11); 'warns via logger' claim corrected — the override is silently ignored on non-polymorphic events (no warn exists in code). npm run check: 61 errors = pre-existing baseline, 0 new. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
// Explicit silence (capa 3 morfo override): `signal.channels: []`
// skips EVERYTHING — no projection, no dispatch, no hold. The morfo
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// declared this event has no perceptual surface. Other paths to an
// empty `effective.activeChannels` (e.g. signal with no family —
// degenerate case) still go through dispatch so individual channels
// can self-skip via their own activeChannels check.
if (signal.channels !== undefined && signal.channels.length === 0) {
feat(uix): persistence + a11ySemantic + polymorphic core (book §5.3, §6, §9) Three book canon items codified in code: **Persistence (libro §6.1)** — separa `hold` (perceptual min) de `persistence` (lifecycle real): - New `SignalPersistence = 'transient' | 'untilAction' | 'untilFix' | 'stateBound'` - New `SEMA_HOLDS_BY_INTENT` canonical lookup table (`src/uix/sema/holds.ts`) - `EngineSemantic.emit()` returns the resolved signal id; keeps projection alive past hold for non-transient. Exposes `clear(id)`, `clearTarget(target)`, `hasActive(id)`. - `SomaRuntime.trigger()` returns `TriggerResult { id?, persistence? }`. Exposes `clearSignal(id)`, `clearTarget(target)`, `partRef(part)`. **a11ySemantic (libro §9.1)**: - New `MorfoA11ySemantic` (requiresPersistentTrace, requiresLiveRegion, requiresFocusMove, keyboardEquivalent, reducedMotionFallback) on MorfoEvent. - `ActiveDom.prefersReducedMotion`: reactive tracker via media query (`src/arts/adom/reduced-motion.svelte.ts`). - `ActiveUix.announce(msg, priority?, timeout?)`: lazy-created live region via `dom.writeNode`. - `Soma.runtime()` auto-wires `sources.announce`. - `SomaRuntime.trigger()` honors a11ySemantic after emit (live region, focus, reduced-motion fallback including `channels: []` for 'state'). **Polymorphic events (libro §5.3)** — ADITIVO sobre shape concreto: - `allowedFamilies?: readonly SemaFamily[]` opcional en MorfoEventSemantic. - Provider override via `runtime.trigger(name, { semantic })`. - Default family of the morfo is implicitly allowed. - New `SomaRuntimePolymorphicError`. - New `isPolymorphicSemantic` type guard. Synthetic `prewriteFixtureMorfo` (src/uix/morfo/test-fixtures.ts) decouples compile + runtime tests from the production morfo catalogue. 671 tests pass in sema + morfo + soma + adom scopes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
return id;
}
const preparations: ChannelPreparation[] = [];
for (const channel of this.channels.values()) {
const handle = channel.prepare?.(enriched);
if (handle) preparations.push(handle);
}
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
feat(uix): persistence + a11ySemantic + polymorphic core (book §5.3, §6, §9) Three book canon items codified in code: **Persistence (libro §6.1)** — separa `hold` (perceptual min) de `persistence` (lifecycle real): - New `SignalPersistence = 'transient' | 'untilAction' | 'untilFix' | 'stateBound'` - New `SEMA_HOLDS_BY_INTENT` canonical lookup table (`src/uix/sema/holds.ts`) - `EngineSemantic.emit()` returns the resolved signal id; keeps projection alive past hold for non-transient. Exposes `clear(id)`, `clearTarget(target)`, `hasActive(id)`. - `SomaRuntime.trigger()` returns `TriggerResult { id?, persistence? }`. Exposes `clearSignal(id)`, `clearTarget(target)`, `partRef(part)`. **a11ySemantic (libro §9.1)**: - New `MorfoA11ySemantic` (requiresPersistentTrace, requiresLiveRegion, requiresFocusMove, keyboardEquivalent, reducedMotionFallback) on MorfoEvent. - `ActiveDom.prefersReducedMotion`: reactive tracker via media query (`src/arts/adom/reduced-motion.svelte.ts`). - `ActiveUix.announce(msg, priority?, timeout?)`: lazy-created live region via `dom.writeNode`. - `Soma.runtime()` auto-wires `sources.announce`. - `SomaRuntime.trigger()` honors a11ySemantic after emit (live region, focus, reduced-motion fallback including `channels: []` for 'state'). **Polymorphic events (libro §5.3)** — ADITIVO sobre shape concreto: - `allowedFamilies?: readonly SemaFamily[]` opcional en MorfoEventSemantic. - Provider override via `runtime.trigger(name, { semantic })`. - Default family of the morfo is implicitly allowed. - New `SomaRuntimePolymorphicError`. - New `isPolymorphicSemantic` type guard. Synthetic `prewriteFixtureMorfo` (src/uix/morfo/test-fixtures.ts) decouples compile + runtime tests from the production morfo catalogue. 671 tests pass in sema + morfo + soma + adom scopes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
const persistence = signal.persistence ?? 'transient';
const shouldAutoCleanup = persistence === 'transient';
let cleanedUp = false;
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
try {
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
let effective = resolveSignature(enriched, {
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
map: this.map,
cascade: this.cascade
});
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
// Post-resolution perceptual modulation. Both touch ONLY the
// non-visual channels (sound / haptic); the visual hold is never
// altered, so eidos's CSS reactions are unaffected.
// C-2 frequency memory — repeated same-key signals attenuate.
// C-3 dominance — a signal dominated by a still-active one is muted.
effective = this.applyFrequencyMemory(enriched, effective);
effective = this.applyDominance(enriched, effective);
const visualChannel = this.channels.get('visual');
const otherChannels: Channel[] = [];
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
for (const channel of this.channels.values()) {
if (channel.id !== 'visual') otherChannels.push(channel);
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
for (const channel of otherChannels) {
channel.handle(enriched, effective).catch((err) => {
this.logger?.error('sema', `channel "${channel.id}" failed`, {
error: err,
context: { channel: channel.id, signal: enriched.id }
});
});
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
// Visual owns the hold timing. It runs whenever there are ANY
// active channels — the empty-channels case was already handled
// by the early return above. Visual isn't in family activeChannels
// (those describe real runtime channels like sound / haptic) —
// it's a meta channel that gates the data-event-* hold for eidos
// CSS reactions.
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
if (visualChannel) {
await visualChannel.handle(enriched, effective);
sema/morfo/eidos: typed selector builder + dialog wrapper + emerge color sema architecture - sema-map: 5-channel registry (motion / sound / color / presence / haptic) via SemaChannelSignatures declaration merging; engine stamps data-event-* tokens; flat CSS-style cascade replaces the eventLabel-overrides middle layer. - sema-map: emerge family gains base.color so intent deltas can shift hue / saturation / intensity. Without a base, the resolver was skipping the channel and Dialog open with intent='threat' rendered as neutral blue in the Sema tab visualisation. - types: SEMA_FAMILY_POLICY const drives compile-time + runtime intent requirements per family. Object shape so future per-family policy fields fit alongside. Emerge events MAY now declare intent (canon update — a Dialog confirming threat carries it in its very appearance). - chans: rename vibra→haptic, add HapticChannel V1 (Vibration API); SoundChannel eager-init on first user gesture (autoplay race fix). morfo selector discipline - morfo/selectors.ts (new): semaSelector(morfo, partKebab, matchers?) — type-checked against morfo.parts and morfo.events. Sema cascade rules MUST use it; hand-written strings are an architecture violation that breaks silently when morfo renames a part. - dialog morfo: open carries intent via fromProp; close-cancel / close-dismiss / close-dismiss-outside drop intent (cancellation has no evaluative load); close-save stays hardcoded fulfill (commit fulfils the user's decision regardless of dialog context). eidos dialog migration (4th pilot) - eidos/components/dialog/: full subdirectory wrapper — flat <Dialog> + compound Provider/Trigger/Overlay/Content/Title/Description/Close/Header/ Footer; size + position responsive props; sheet auto-form on narrow viewports; closePosition for the auto-X. - sema/components/dialog.ts: cascade rules use semaSelector(dialogMorfo, 'content', matchers?). Intent block adds character (haptic kind / pattern) but never overrides pitch / gain / contour — those are intent.deltas signature ownership and overriding flattens per-intent perceptual difference. demo controls + dialog page - src/lib/_demo (DemoSwitch / DemoEnum / DemoText / DemoRange) — unified controls reused across all component demos. - web/routes/dialog: live preview always rendered above tabs; per-event Sema tab with independent intent probe; signature visualisation + Play buttons with fallback target chain. documentation - CLAUDE.md: intent.deltas signature ownership rule; eidos drift defense doctrine (types over lint); 2026-05-09 hand-off entry. - morfo/README: Typed selector builder section. - eidos/README: linter section reframed as opt-in safety net for plain CSS recipes; the architectural mechanism is compile-time typing. - sema/README: open channel registry + flat cascade docs + override semantics + intent policy const + selector builder discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
} finally {
feat(uix): persistence + a11ySemantic + polymorphic core (book §5.3, §6, §9) Three book canon items codified in code: **Persistence (libro §6.1)** — separa `hold` (perceptual min) de `persistence` (lifecycle real): - New `SignalPersistence = 'transient' | 'untilAction' | 'untilFix' | 'stateBound'` - New `SEMA_HOLDS_BY_INTENT` canonical lookup table (`src/uix/sema/holds.ts`) - `EngineSemantic.emit()` returns the resolved signal id; keeps projection alive past hold for non-transient. Exposes `clear(id)`, `clearTarget(target)`, `hasActive(id)`. - `SomaRuntime.trigger()` returns `TriggerResult { id?, persistence? }`. Exposes `clearSignal(id)`, `clearTarget(target)`, `partRef(part)`. **a11ySemantic (libro §9.1)**: - New `MorfoA11ySemantic` (requiresPersistentTrace, requiresLiveRegion, requiresFocusMove, keyboardEquivalent, reducedMotionFallback) on MorfoEvent. - `ActiveDom.prefersReducedMotion`: reactive tracker via media query (`src/arts/adom/reduced-motion.svelte.ts`). - `ActiveUix.announce(msg, priority?, timeout?)`: lazy-created live region via `dom.writeNode`. - `Soma.runtime()` auto-wires `sources.announce`. - `SomaRuntime.trigger()` honors a11ySemantic after emit (live region, focus, reduced-motion fallback including `channels: []` for 'state'). **Polymorphic events (libro §5.3)** — ADITIVO sobre shape concreto: - `allowedFamilies?: readonly SemaFamily[]` opcional en MorfoEventSemantic. - Provider override via `runtime.trigger(name, { semantic })`. - Default family of the morfo is implicitly allowed. - New `SomaRuntimePolymorphicError`. - New `isPolymorphicSemantic` type guard. Synthetic `prewriteFixtureMorfo` (src/uix/morfo/test-fixtures.ts) decouples compile + runtime tests from the production morfo catalogue. 671 tests pass in sema + morfo + soma + adom scopes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
if (shouldAutoCleanup) {
for (const handle of [...preparations].reverse()) {
handle.cleanup();
}
cleanedUp = true;
}
}
// Persistent signals: keep projection alive past the hold. Caller
// (typically a soma provider) clears via `clear(id)` or
// `clearTarget(target)` when the relevant condition is met. If the
// caller never clears, the projection lingers until dispose() — by
// design: persistence is caller-managed per book §6.1.
if (!cleanedUp) {
this.active.set(id, { preparations, target: signal.target });
}
return id;
}
/**
* Clear an active persistent signal by id. Returns `true` if the signal
* was found and cleared, `false` if it didn't exist (already cleared,
* was transient, or never emitted).
*
* Call this from the caller that originally emitted the signal when
* the underlying condition is met:
* - `untilAction` — user acknowledged the alert.
* - `untilFix` — the validation error was corrected.
* - `stateBound` — the state ended.
*/
clear(id: string): boolean {
const entry = this.active.get(id);
if (!entry) return false;
this.active.delete(id);
for (const handle of [...entry.preparations].reverse()) {
handle.cleanup();
}
return true;
}
/**
* Clear all active persistent signals whose `target` matches the given
* element. Returns the count of signals cleared. Useful when a single
* gesture invalidates multiple persistent signals on the same surface
* (e.g. a form with three field warnings: one form-valid clears all).
*/
clearTarget(target: HTMLElement): number {
let count = 0;
for (const [id, entry] of this.active) {
if (entry.target === target) {
this.active.delete(id);
for (const handle of [...entry.preparations].reverse()) {
handle.cleanup();
}
count++;
}
}
feat(uix): persistence + a11ySemantic + polymorphic core (book §5.3, §6, §9) Three book canon items codified in code: **Persistence (libro §6.1)** — separa `hold` (perceptual min) de `persistence` (lifecycle real): - New `SignalPersistence = 'transient' | 'untilAction' | 'untilFix' | 'stateBound'` - New `SEMA_HOLDS_BY_INTENT` canonical lookup table (`src/uix/sema/holds.ts`) - `EngineSemantic.emit()` returns the resolved signal id; keeps projection alive past hold for non-transient. Exposes `clear(id)`, `clearTarget(target)`, `hasActive(id)`. - `SomaRuntime.trigger()` returns `TriggerResult { id?, persistence? }`. Exposes `clearSignal(id)`, `clearTarget(target)`, `partRef(part)`. **a11ySemantic (libro §9.1)**: - New `MorfoA11ySemantic` (requiresPersistentTrace, requiresLiveRegion, requiresFocusMove, keyboardEquivalent, reducedMotionFallback) on MorfoEvent. - `ActiveDom.prefersReducedMotion`: reactive tracker via media query (`src/arts/adom/reduced-motion.svelte.ts`). - `ActiveUix.announce(msg, priority?, timeout?)`: lazy-created live region via `dom.writeNode`. - `Soma.runtime()` auto-wires `sources.announce`. - `SomaRuntime.trigger()` honors a11ySemantic after emit (live region, focus, reduced-motion fallback including `channels: []` for 'state'). **Polymorphic events (libro §5.3)** — ADITIVO sobre shape concreto: - `allowedFamilies?: readonly SemaFamily[]` opcional en MorfoEventSemantic. - Provider override via `runtime.trigger(name, { semantic })`. - Default family of the morfo is implicitly allowed. - New `SomaRuntimePolymorphicError`. - New `isPolymorphicSemantic` type guard. Synthetic `prewriteFixtureMorfo` (src/uix/morfo/test-fixtures.ts) decouples compile + runtime tests from the production morfo catalogue. 671 tests pass in sema + morfo + soma + adom scopes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
return count;
}
/**
* Whether a persistent signal id is currently active. Read-only helper
* for diagnostics and tests; production code should not branch on this.
*/
hasActive(id: string): boolean {
return this.active.has(id);
}
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
/**
* C-2 — frequency memory (BK-FREQ-MEMORY, book ch. 32 §11). Attenuates the
* non-visual channels of a signal repeated within a short window so a burst
* of the same event fades instead of hammering. `threat` is exempt. A pause
* of `FREQ_WINDOW_MS` resets the key to full intensity.
*/
private applyFrequencyMemory(
signal: SemanticSignal,
effective: EffectiveSignature
): EffectiveSignature {
if (!this.frequencyMemory) return effective;
if (effective.intent === 'threat') return effective;
const key = signal.name || `${effective.family ?? ''}:${effective.intent ?? ''}`;
const entry = this.freqCounts.get(key) ?? { count: 0 };
entry.count += 1;
entry.reset?.cancel();
entry.reset = semaDelay(this.timers, FREQ_WINDOW_MS, () => this.freqCounts.delete(key), {
channel: 'frequency'
});
this.freqCounts.set(key, entry);
const over = entry.count - FREQ_THRESHOLD;
if (over <= 0) return effective;
const atten = Math.max(FREQ_FLOOR, 1 - over * FREQ_STEP);
return attenuateNonVisual(effective, atten);
}
/**
* C-3 — dominance arbiter (BK-DOMINANCE, book ch. 30 §7). Registers each
* occurrence as active for a short overlap window and mutes the non-visual
* channels of an INCOMING signal that a still-active occurrence out-ranks
* (evaluable > structural; higher activation; recency breaks ties in favour
* of the newcomer). `threat` is never muted. The visual hold survives so the
* dominated event still reads structurally.
*/
private applyDominance(
signal: SemanticSignal,
effective: EffectiveSignature
): EffectiveSignature {
if (!this.dominance) return effective;
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
// `emit` always enriches the signal with an id before this runs. The
// old `?? sig-N++` fallback here was dead — and, had it ever fired, it
// would have minted an id DIFFERENT from the one the rest of the
// pipeline uses, desyncing the occurrence bookkeeping (SEM-3). A
// degenerate un-enriched call now skips dominance instead.
const id = signal.id;
if (id === undefined) return effective;
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
const rank = occurrenceRank(effective);
let dominated = false;
if (effective.intent !== 'threat') {
for (const active of this.activeOccurrences.values()) {
if (active.rank > rank) {
dominated = true;
break;
}
}
}
this.activeOccurrences.get(id)?.expire.cancel();
const expire = semaDelay(
this.timers,
DOMINANCE_WINDOW_MS,
() => this.activeOccurrences.delete(id),
{ channel: 'dominance' }
);
this.activeOccurrences.set(id, { rank, expire });
return dominated ? attenuateNonVisual(effective, 0) : effective;
}
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
getChannel(id: string): Channel | undefined {
return this.channels.get(id);
}
dispose(): void {
feat(uix): persistence + a11ySemantic + polymorphic core (book §5.3, §6, §9) Three book canon items codified in code: **Persistence (libro §6.1)** — separa `hold` (perceptual min) de `persistence` (lifecycle real): - New `SignalPersistence = 'transient' | 'untilAction' | 'untilFix' | 'stateBound'` - New `SEMA_HOLDS_BY_INTENT` canonical lookup table (`src/uix/sema/holds.ts`) - `EngineSemantic.emit()` returns the resolved signal id; keeps projection alive past hold for non-transient. Exposes `clear(id)`, `clearTarget(target)`, `hasActive(id)`. - `SomaRuntime.trigger()` returns `TriggerResult { id?, persistence? }`. Exposes `clearSignal(id)`, `clearTarget(target)`, `partRef(part)`. **a11ySemantic (libro §9.1)**: - New `MorfoA11ySemantic` (requiresPersistentTrace, requiresLiveRegion, requiresFocusMove, keyboardEquivalent, reducedMotionFallback) on MorfoEvent. - `ActiveDom.prefersReducedMotion`: reactive tracker via media query (`src/arts/adom/reduced-motion.svelte.ts`). - `ActiveUix.announce(msg, priority?, timeout?)`: lazy-created live region via `dom.writeNode`. - `Soma.runtime()` auto-wires `sources.announce`. - `SomaRuntime.trigger()` honors a11ySemantic after emit (live region, focus, reduced-motion fallback including `channels: []` for 'state'). **Polymorphic events (libro §5.3)** — ADITIVO sobre shape concreto: - `allowedFamilies?: readonly SemaFamily[]` opcional en MorfoEventSemantic. - Provider override via `runtime.trigger(name, { semantic })`. - Default family of the morfo is implicitly allowed. - New `SomaRuntimePolymorphicError`. - New `isPolymorphicSemantic` type guard. Synthetic `prewriteFixtureMorfo` (src/uix/morfo/test-fixtures.ts) decouples compile + runtime tests from the production morfo catalogue. 671 tests pass in sema + morfo + soma + adom scopes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// Clean up any lingering persistent signal projections before tearing
// down channels, so DOM doesn't keep stale data-event-* attrs.
for (const entry of this.active.values()) {
for (const handle of [...entry.preparations].reverse()) {
handle.cleanup();
}
}
this.active.clear();
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
// Cancel the frequency-memory and dominance decay timers.
for (const entry of this.freqCounts.values()) entry.reset?.cancel();
this.freqCounts.clear();
for (const entry of this.activeOccurrences.values()) entry.expire.cancel();
this.activeOccurrences.clear();
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
for (const channel of this.channels.values()) {
channel.dispose?.();
}
this.channels.clear();
}
}
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
function isChannel(value: unknown): value is Channel {
return (
value !== null &&
typeof value === 'object' &&
typeof (value as Channel).id === 'string' &&
typeof (value as Channel).handle === 'function'
);
sema: refactor into channel modules — engine becomes registry + dispatch Reorganización estructural según el spec acordado: cada canal perceptivo vive como módulo simétrico dentro de `chans/`. El engine queda mínimo (registry + dispatch); la lógica de escribir `data-event*` al DOM, mantener hold y retirar pasa al `VisualChannel`. La API pública que ven los providers (`semantic.emit(signal)`) se mantiene idéntica. Estructura nueva src/uix/sema/ ├── engine.ts registry + dispatch (ya no conoce DOM ni hold) ├── signal.ts SemanticSignal + nuevo campo opcional `id?` ├── exports.ts barrel actualizado (chans + drop publish + drop perception) └── chans/ ├── types.ts interfaz Channel { id, handle, dispose? } ├── visual.ts VisualChannel — escribe data-event*, hold, cleanup ├── visual.test.ts 9 tests del canal aislado ├── sound.ts SoundChannel placeholder (V1 no-op) └── vibra.ts VibraChannel placeholder (V1 no-op) Cambios al engine - Constructor: `new SemanticEngine(opts?)`. `opts.visual` controla el built-in (false / VisualChannelOptions / Channel custom). `opts.dom` desaparece — ya no es responsabilidad del engine. - Métodos: `register(channel)`, `getChannel(id)`, `emit(signal)`, `destroy()`. Nada más. - emit despacha a TODOS los canales registrados: - Canales no-visuales (sound, vibra, futuros): fire-and-forget. Errores se loguean pero no propagan (Sema es ornamental). - Canal visual: el único cuya Promise se awaitea — comparte plano DOM con el commit estructural posterior. - emit genera el `id` de la ocurrencia y lo pasa a todos los canales, garantizando coherencia cross-canal para tooling futuro. Cambios al VisualChannel (lógica heredada del emit() anterior) - 5 atributos: `data-event` + `data-event-id` + `data-event-phase` (siempre) + `data-event-family` y `data-intent` (opcionales si vienen en el signal). Los opcionales son la proyección al DOM de la metadata semántica que Eidos consume. - Hold defaults internos por familia (no exportados): emerge/commit/handle: 240ms · alert/sustain: 600ms · contact: 120ms Justificación técnica (rangos típicos de CSS transitions), no escala perceptiva universal. Override per signal vía `signal.hold` o global vía `new SemanticEngine({ visual: { defaultHold } })`. - Semántica secuencial estricta: cleanup ANTES del resolve. Eliminado - `src/uix/sema/perception.ts` — escalas perceptivas no son concepto cross-canal. La tabla de defaults por familia se movió al VisualChannel como detalle interno con justificación técnica. - `engine.publish()` y todo el sistema legacy: - `SemanticEventDecl`, `SemanticComponentContract`, `SemanticPublishContext`, `PublishedSemanticEvent`, `SemanticEventFilter` - `onEvent()` y subscribers - `applyPrewrites()` interno - dependencia opcional de `ActiveDom` por construcción - Tests de `publish()` reemplazados por tests del nuevo registry/dispatch. Bootstrap (App + defaults) - `src/lib/ext/app/app.svelte.ts:52` — `new SemanticEngine({ dom: this.dom })` → `new SemanticEngine()` - `src/lib/ext/app/defaults.ts:45` — mismo cambio Tests - emit.test.ts ajustado: sin createActiveDom, sin SEMA_PERCEPTION/HOLD_DEFAULTS, test "throws without dom" eliminado, números literales (240, 600). Añadido test "resolves immediately when visual: false". 7 tests. - engine.test.ts reescrito: register / getChannel / emit dispatch a múltiples canales / id propagation / id override / fire-and-forget para non-visual / error en canal no aborta dispatch / visual: false desactiva built-in / custom Channel reemplaza built-in / destroy. 13 tests. - chans/visual.test.ts nuevo: 5 attrs vs 3 / signal.hold respetado / family default fallback / global default fallback / constructor defaultHold / cleanup / sequential strict (resolve tras cleanup) / id field. 9 tests. - 39 tests sema verdes en total. Documentación - src/uix/sema/README.md — reescrito alrededor del modelo de canales - src/uix/eidos/events.css — comentario actualizado, referencia a chans/visual.ts - src/uix/active_architecture.md — sección Sema reescrita Componentes NO requieren cambios — la API `semantic.emit(signal)` es idéntica externamente. Toast / runtime.trigger / providers funcionan igual. Verificación - 121/121 tests focused suite (sema 39 + adom + lib/dom + morfo + soma/morfo) - 66/66 morfo-check - 68/68 smoke routes - svelte-check: 155 errors (baseline, sin nuevos) Pendientes en doc del spec marcados como "no-objetivos": - Implementación real de SoundChannel / VibraChannel (placeholders V1) - Arbitraje propio del VisualChannel (deuda heredada del emit anterior) - Cancelación si el target se desconecta durante el hold
6 months ago
}
function isSoundChannelDom(value: unknown): value is SoundChannelDom {
return (
value !== null &&
typeof value === 'object' &&
typeof (value as SoundChannelDom).getDocument === 'function' &&
typeof (value as SoundChannelDom).listen === 'function'
);
}
fix: address architectural audit findings + sync ecosystem docs Audit: src/audit-opus-4-6-26.md. All non-words findings remediated. Code: - E1: navigation-menu indicator data-state visible -> open (render bug; the active underline was permanently invisible). Clears the only invalid lint selector. - SO1 + E2: raw `new ResizeObserver` -> ActiveDom.observeResize in carousel-provider and the canvas-text useContainerWidth hook (+ s-text / s-text-virtual-list pass eidos.dom). iframe/popup-safe, lifecycle-tracked. - A1: defineUixServices now registers `motion`, so attach-mode app.motion is real and the active-uix fallback becomes the true edge case (test guard updated). - A2/A4: contracts.ts pins `motion` + `announce` in ActiveUixServiceContract + publicSurface; dispose() comment corrected. - SO2: carousel drops the hardcoded `transform 300ms ease-out` (the recipe already handles it via [data-dragging]); also fixed the recipe's undefined `--duration-base` token -> `--duration-slow` (it was masked by the inline). - S1: HapticChannel reduced-motion via an injected ActiveDom port (mirrors SoundChannelDom) instead of global matchMedia. - T1: 10 sites repointed `$libs/dom` -> `$adom` (sema x5 + its tests x3, active-uix value import, arts/prefs). - M2 / S2 / S3: dead code removed (button `states:['idle','loading']`, SemaRuntimeChannelId, SEMA_VALENCED_FAMILY_LIST). Docs: - Motion-as-service reflected across the ecosystem: CLAUDE.md (aliases + arch + service note), arts/README, active_architecture, soma SOMA_ARCHITECTURE, eidos README, eidos-motion.md. - X1: CLAUDE.md "5 canonical channels" (false) -> the single canonical narrative (8 book channels; Sema runs 2 + visual meta-channel, Eidos materializes 5). - X2/X3/X4: sema/README (8 families + intentRequirement/intentGuidance split), types.ts JSDoc (SEMA_INTENT_POLICY -> SEMA_FAMILY_POLICY), engine.ts cascade 6->5, alias table ($frontend out, $lang->$langs, +$clipboard). - E4: codex_audit.md HISTORICO banner. Deferred: SU2 (test-only layering, not a build violation); Words M1/T5/E3 (WIP). Verify: npm run check -> 1 pre-existing error (grafito), 0 new; sema 152/152, contracts 31/32 (1 pre-existing words), carousel 4/4, motion 22/22. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress) Re-audit of the whole component catalog at pilot depth (91 fichas + the checkpoint verdicts in docs/audit/components/) and the executed fix packages. - P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 + API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d, component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts (role=application removed ×4, aria-selected off the Day, drp translationRef, field data-state prune, pin-input commit-set, media-player renames), 13 new sema packs + 12 morfos family-default → pack. - P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar); onValueCommit terminal-callback norm (pin-input/search/password/textarea + date/time/color-field add); typed validation reason + onInvalid (tags-input, css-field); index/onIndexChange (carousel); deselectable; openDelay/ groupSkipDelay; allowCustomValue; defaultValue prune. - P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel); touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual, 44 AAA). S6 (Field composition) + S8 (calendar-surface) pending. Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline); morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched components; per-component vitest suites green. Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md Excluded (broken by the N1 rename, left broken per user decision, not staged): words/**, palabras/**, chronos, web/routes/alpha/**. Reconciliation pending: the touch-rows ::before for checkbox/switch reverses changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed markers to a labeled-row/Field task — flagged for the user in the handoff. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
function isAnnounceChannelDom(value: unknown): value is AnnounceChannelDom {
return (
value !== null &&
typeof value === 'object' &&
typeof (value as AnnounceChannelDom).getDocument === 'function'
);
}
fix: address architectural audit findings + sync ecosystem docs Audit: src/audit-opus-4-6-26.md. All non-words findings remediated. Code: - E1: navigation-menu indicator data-state visible -> open (render bug; the active underline was permanently invisible). Clears the only invalid lint selector. - SO1 + E2: raw `new ResizeObserver` -> ActiveDom.observeResize in carousel-provider and the canvas-text useContainerWidth hook (+ s-text / s-text-virtual-list pass eidos.dom). iframe/popup-safe, lifecycle-tracked. - A1: defineUixServices now registers `motion`, so attach-mode app.motion is real and the active-uix fallback becomes the true edge case (test guard updated). - A2/A4: contracts.ts pins `motion` + `announce` in ActiveUixServiceContract + publicSurface; dispose() comment corrected. - SO2: carousel drops the hardcoded `transform 300ms ease-out` (the recipe already handles it via [data-dragging]); also fixed the recipe's undefined `--duration-base` token -> `--duration-slow` (it was masked by the inline). - S1: HapticChannel reduced-motion via an injected ActiveDom port (mirrors SoundChannelDom) instead of global matchMedia. - T1: 10 sites repointed `$libs/dom` -> `$adom` (sema x5 + its tests x3, active-uix value import, arts/prefs). - M2 / S2 / S3: dead code removed (button `states:['idle','loading']`, SemaRuntimeChannelId, SEMA_VALENCED_FAMILY_LIST). Docs: - Motion-as-service reflected across the ecosystem: CLAUDE.md (aliases + arch + service note), arts/README, active_architecture, soma SOMA_ARCHITECTURE, eidos README, eidos-motion.md. - X1: CLAUDE.md "5 canonical channels" (false) -> the single canonical narrative (8 book channels; Sema runs 2 + visual meta-channel, Eidos materializes 5). - X2/X3/X4: sema/README (8 families + intentRequirement/intentGuidance split), types.ts JSDoc (SEMA_INTENT_POLICY -> SEMA_FAMILY_POLICY), engine.ts cascade 6->5, alias table ($frontend out, $lang->$langs, +$clipboard). - E4: codex_audit.md HISTORICO banner. Deferred: SU2 (test-only layering, not a build violation); Words M1/T5/E3 (WIP). Verify: npm run check -> 1 pre-existing error (grafito), 0 new; sema 152/152, contracts 31/32 (1 pre-existing words), carousel 4/4, motion 22/22. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
function isHapticChannelDom(value: unknown): value is HapticChannelDom {
return (
value !== null &&
typeof value === 'object' &&
typeof (value as HapticChannelDom).prefersReducedMotion === 'object' &&
(value as HapticChannelDom).prefersReducedMotion !== null
);
}

Powered by TurnKey Linux.