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/resolver.ts

424 lines
15 KiB

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
* resolveSignature(signal, opts) — turns a `SemanticSignal` into the
* per-channel `EffectiveSignature` used by every channel.
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
* Resolution cascade (each layer overrides the previous):
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
* 1. FAMILY base — `SEMA_MAP.families[signal.family].base`
* 2. INTENT deltas — `SEMA_MAP.intents[signal.intent]` when present
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. soundPack URL — `SEMA_MAP.soundPack[<family>-<intent>]`
* 4. MORFO overrides — `signal.overrides` / `signal.channels`
* (from the morfo event, copied by SomaRuntime)
* 5. RUNTIME overrides — `options.runtimeMap` already has
* `engineOpts.overrides.runtime` baked in by the
* engine via `applyMapOverrides()` at construction
* 6. CASCADE rules — `options.cascade` — CSS-style rules matched
* against `signal.target` AFTER the engine has
* stamped `data-event-*` on it (see `stamp.ts`).
* Rules are flat: `{ selector, priority?,
* channels?, sound?, haptic? }`. The selector is
* matched natively via `target.matches()` /
* `target.closest()`.
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
* CSS-like specificity orders cascade rules: ascending order — later
* applied wins. Authors can override with `priority?: number` (replaces
* the computed specificity entirely).
*
* Pure function — `resolveSignature` reads from `SEMA_MAP` (or a passed
* map) and an optional immutable `cascade` array. Runtime path overrides
* are baked into the map up-front by the engine via `applyMapOverrides`,
* so this function never mutates anything.
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
*/
import type { SemanticSignal } from './signal';
import type { Intent } from '../intent';
import type { SemaFamily } from './types';
import { resolveSemaDuration } from './durations';
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
import {
SEMA_MAP,
SEMA_VALENCED_FAMILY_LIST,
type DeltaOp,
type DeltaValue,
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
type HapticSignature,
type SemaChannelId,
type SemaChannelSignatures,
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
type SemaMap,
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
type SemaSignatureOverride,
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
type SoundSignature
} from './sema-map';
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
export type {
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
HapticSignature,
SemaChannelId,
SemaChannelSignatures,
SemaSignatureOverride,
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
SoundSignature
} from './sema-map';
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
/**
* Single cascade rule. Identical shape to a CSS rule — a selector that
* matches the target's data-attrs (state from morfo + event tokens
* stamped by the engine) and a flat block of per-channel deltas.
*
* {
* selector: '[data-dialog-content][data-event-intent="threat"]',
* sound: { sampleUrl: '/sounds/dialog-fail.wav' },
* haptic: { kind: 'error' }
* }
*
* No `overrides: { eventLabel: ... }` middle layer — semantic event
* identity is matched via `[data-event="..."]`,
* `[data-event-family="..."]` or `[data-event-intent="..."]` directly in
* the selector.
*/
export interface SemaCascadeRule extends SemaSignatureOverride {
selector: 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
/**
* Override CSS-computed specificity. When omitted, specificity is
* computed from the selector (IDs × 100, attrs / classes /
* pseudo-classes × 10, elements × 1). Use `priority` for cases that
* need to win without selector gymnastics — typically accessibility
* (`priority: 100+`) or experimental cohort overrides.
*/
priority?: number;
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 SemaResolveOptions {
map?: SemaMap;
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
}
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
export interface EffectiveSignature {
family: SemaFamily | undefined;
intent: Intent | undefined;
activeChannels: readonly SemaChannelId[];
/**
* Resolved hold duration in ms — how long `data-event-*` tokens live
* on the target. Computed from `signal.hold ?? family.hold`. Distinct
* from visual motion timing, which lives in Eidos recipes.
*/
hold?: number;
sound?: SoundSignature;
haptic?: HapticSignature;
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
/** Empty signature returned when input has no family — channels skip. */
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
const EMPTY_SIGNATURE: EffectiveSignature = {
family: undefined,
intent: undefined,
activeChannels: []
};
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
export function resolveSignature(
signal: SemanticSignal,
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
mapOrOptions: SemaMap | SemaResolveOptions = SEMA_MAP
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
): EffectiveSignature {
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
const options: SemaResolveOptions = isResolveOptions(mapOrOptions)
? mapOrOptions
: { map: mapOrOptions };
const map = options.map ?? SEMA_MAP;
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 (!signal.family) return EMPTY_SIGNATURE;
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
const familyEntry = map.families[signal.family];
if (!familyEntry) return EMPTY_SIGNATURE;
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
// Layer 1 — family base
// `hold` is resolved up-front: per-call `signal.hold` wins, otherwise
// the family-declared `hold` from `SEMA_MAP`. Cascade rules can
// override `hold` directly via the `hold` field on the rule (treated
// the same as any other channel-less primitive in `applyOverride`).
const familyHoldMs = resolveSemaDuration(familyEntry.hold);
const initialHold = signal.hold ?? familyHoldMs;
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
let signature: EffectiveSignature = {
family: signal.family,
intent: signal.intent,
activeChannels: [...familyEntry.activeChannels],
...(initialHold !== undefined ? { hold: initialHold } : {}),
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
...cloneBase(familyEntry.base)
};
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
// Layer 2 — intent deltas. Applied whenever the signal carries an
// intent, regardless of family. The doctrine originally restricted
// this to valenced families (contact / commit / signal / handle) but
// real UX needs intent on transitional events too: a `<Dialog
// intent="threat">` should sound threatening even on the `open` event
// (emerge family). SomaRuntime forwards the consumer's intent prop
// when the morfo doesn't declare its own, and the resolver applies
// the deltas to whichever channels the family's base populated.
if (signal.intent) {
const intentEntry = map.intents[signal.intent];
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 (intentEntry) {
signature = applyChannelDeltas(signature, intentEntry.deltas);
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
// Layer 3 — soundPack URL by canonical event label
const eventLabel = composeEventLabel(signal.family, signal.intent);
const sampleUrl = map.soundPack[eventLabel];
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 (sampleUrl && signature.sound) {
signature.sound.sampleUrl = sampleUrl;
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
// Layer 4 — per-event morfo overrides (carried by the signal)
if (signal.channels !== undefined) {
signature.activeChannels = [...signal.channels];
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 (signal.overrides) {
signature = applyOverride(signature, signal.overrides);
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
}
// Layer 6 — CSS-style cascade. Rules are matched via target.matches()
// — channel prepare hooks have already projected `data-event-*` on the
// target, so selectors that read those tokens match correctly.
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 (options.cascade && options.cascade.length > 0 && signal.target) {
const matched = collectCascadeMatches(options.cascade, signal.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
for (const rule of matched) {
signature = applyOverride(signature, ruleAsOverride(rule));
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
}
}
return signature;
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
// ── Cascade matching (CSS-like) ───────────────────────────────────────────
function collectCascadeMatches(
rules: readonly SemaCascadeRule[],
target: HTMLElement
): SemaCascadeRule[] {
const indexed = rules.map((rule, index) => ({
rule,
index,
score: rule.priority ?? cssSpecificity(rule.selector)
}));
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
const matched = indexed.filter(({ rule }) => safeMatches(target, rule.selector));
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
// Ascending — last applied wins.
matched.sort((a, b) => {
if (a.score !== b.score) return a.score - b.score;
return a.index - b.index;
});
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
return matched.map(({ rule }) => rule);
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
}
function safeMatches(target: HTMLElement, selector: string): boolean {
if (selector === '*') return true;
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 {
return target.matches(selector) || target.closest(selector) !== null;
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
} catch {
return 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
}
}
/**
* Approximate CSS specificity. IDs × 100, attribute selectors / classes /
* pseudo-classes × 10, element / pseudo-element × 1. Doesn't model the
* full CSS algorithm (combinators, `:where()`, `:is()`) but suffices for
* the selector vocabulary cascade rules use in practice.
*/
function cssSpecificity(selector: string): number {
let score = 0;
const idMatches = selector.match(/#[\w-]+/g);
score += 100 * (idMatches?.length ?? 0);
const attrMatches = selector.match(/\[[^\]]+\]/g);
score += 10 * (attrMatches?.length ?? 0);
const classMatches = selector.match(/\.[\w-]+/g);
score += 10 * (classMatches?.length ?? 0);
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
// Pseudo-classes — exclude pseudo-elements (::after etc).
const pseudoMatches = selector.match(/(?<!:):[\w-]+(?:\([^)]*\))?/g);
score += 10 * (pseudoMatches?.length ?? 0);
return score;
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
}
function ruleAsOverride(rule: SemaCascadeRule): SemaSignatureOverride {
const { selector: _s, priority: _p, ...override } = rule;
void _s;
void _p;
return override as SemaSignatureOverride;
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 application (layers 4 + 6) ───────────────────────────────────
//
// Overrides use REPLACE-by-default for primitive leaves (numbers, strings,
// booleans). Matches CSS intuition: `sound: { gain: 0.4 }` SETS gain to
// 0.4. To explicitly add, use `{ op: 'add', value: 0.1 }`.
function applyOverride(
signature: EffectiveSignature,
override: SemaSignatureOverride
): EffectiveSignature {
const next: EffectiveSignature = deepClone(signature);
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 (override.channels !== undefined) {
next.activeChannels = [...override.channels];
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 key of Object.keys(override)) {
if (key === 'channels') continue;
const delta = (override as Record<string, unknown>)[key];
if (delta === undefined) continue;
const current = (next as unknown as Record<string, unknown>)[key];
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 (current === undefined) {
if (isRecord(delta) && !isDeltaOp(delta)) {
(next as unknown as Record<string, unknown>)[key] = deepClone(delta);
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
}
continue;
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
}
(next as unknown as Record<string, unknown>)[key] = applyLeaf(
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
current,
delta as DeltaValue,
'replace'
);
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
}
return next;
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
}
// ── Channel deltas (layer 2 — intent) ─────────────────────────────────────
//
// Intent deltas use ADD-by-default for numbers — they're modifiers
// composed on top of the family base. Strings/arrays still replace, ops
// take explicit precedence.
function applyChannelDeltas(
signature: EffectiveSignature,
deltas: Partial<Record<SemaChannelId, Record<string, DeltaValue>>>
): EffectiveSignature {
const next: EffectiveSignature = deepClone(signature);
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 next.activeChannels) {
const delta = deltas[channel];
if (!delta) continue;
const current = (next as unknown as Record<string, unknown>)[channel as string];
if (!current) continue;
(next as unknown as Record<string, unknown>)[channel as string] = applyLeaf(
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
current,
delta,
'add'
);
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
}
return next;
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
}
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
// ── Internals ──────────────────────────────────────────────────────────────
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
function isResolveOptions(value: unknown): value is SemaResolveOptions {
return isRecord(value) && !('families' in value && 'intents' in value);
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
}
function composeEventLabel(family: SemaFamily, intent?: Intent): string {
return intent ? `${family}-${intent}` : family;
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
function cloneBase(base: Partial<SemaChannelSignatures>): Partial<SemaChannelSignatures> {
const out: Partial<SemaChannelSignatures> = {};
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 key of Object.keys(base) as (keyof SemaChannelSignatures)[]) {
const value = base[key];
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 (value !== undefined && value !== null) {
(out as Record<string, unknown>)[key] = deepClone(value);
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
}
}
return out;
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
}
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
function deepClone<T>(value: T): T {
return structuredClone(value);
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
}
function isRecord(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === 'object' && !Array.isArray(value);
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
}
function isDeltaOp(value: unknown): value is DeltaOp {
return isRecord(value) && typeof value.op === 'string';
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
function applyLeaf(
base: unknown,
delta: DeltaValue,
numberMode: 'add' | 'replace' = 'add'
): unknown {
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 (typeof delta === 'number') {
if (numberMode === 'replace') return delta;
return typeof base === 'number' ? base + delta : delta;
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 (typeof delta === 'string' || typeof delta === 'boolean' || delta === null) {
return delta;
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
if (Array.isArray(delta)) {
return [...delta] as unknown;
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
}
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 (isDeltaOp(delta)) {
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 (delta.op === 'replace') {
return Array.isArray(delta.value) ? [...delta.value] : delta.value;
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 (typeof base !== 'number') return base;
if (delta.op === 'multiply') return base * delta.factor;
return base + delta.value;
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 (!isRecord(delta)) return base;
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 (!isRecord(base)) {
return deepClone(delta);
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
}
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
const out: Record<string, unknown> = deepClone(base);
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
for (const [key, nextDelta] of Object.entries(delta)) {
out[key] = applyLeaf(out[key], nextDelta as DeltaValue, numberMode);
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
}
return out;
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
// ── Runtime path-based map mutation (layer 5) ─────────────────────────────
/**
* Apply path-based runtime overrides to the SemaMap, producing a new map
* without mutating the canonical one. Used by the engine at construction.
*/
export function applyMapOverrides(
baseMap: SemaMap,
overrides: Record<string, DeltaValue> | undefined
): SemaMap {
if (!overrides || Object.keys(overrides).length === 0) return baseMap;
let next: unknown = baseMap;
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 [path, delta] of Object.entries(overrides)) {
next = applyPathOverride(next, path.split('.'), delta);
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
}
return next as unknown as SemaMap;
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
}
function applyPathOverride(root: unknown, parts: string[], delta: DeltaValue): unknown {
if (parts.length === 0) return applyLeaf(root, delta, 'replace');
const next = cloneContainer(root);
let cursor = next;
let sourceCursor = root;
for (let i = 0; i < parts.length - 1; i++) {
const key = parts[i];
const sourceChild = readChild(sourceCursor, key);
const child = cloneContainer(sourceChild);
cursor[key] = child;
cursor = child;
sourceCursor = sourceChild;
}
const leafKey = parts[parts.length - 1];
// Runtime path overrides REPLACE primitive leaves (designer setting
// `'families.commit.base.sound.pitch': 850` means SET to 850, not
// add). Use `{ op: 'add' }` if explicit additive semantics are needed.
cursor[leafKey] = applyLeaf(readChild(sourceCursor, leafKey), delta, 'replace');
return next;
}
function cloneContainer(value: unknown): Record<string, unknown> {
if (Array.isArray(value)) return [...value] as unknown as Record<string, unknown>;
if (isRecord(value)) return { ...value };
return {};
}
function readChild(value: unknown, key: string): unknown {
if (!isRecord(value) && !Array.isArray(value)) return undefined;
return (value as Record<string, unknown>)[key];
}

Powered by TurnKey Linux.