Codifies the doctrinal source for the semantic system per the user's
book at src/docs/Disenando_lo_que_ocurre_manuscrito_completo_revisado_v2.docx.
Canon (book cap. 8 + cap. 22-29 + cap. 10):
- 8th family `delegate` added (offer/plan/authorize/act/review/escalate/return)
- `signal.inform` added (book cap. 24 listed 7 signal verbs; we had 6)
- Commit contextual verbs from case studies: apply, partial, block, move, upload
- `sustain.upload` added
- SemaFamily union, SEMA_FAMILY_POLICY, sema-map, visual chan, morfo schema
all extended to include delegate (intentPolicy: 'optional' — no intent by
default per cap. 29 §4)
Lint (scripts/morfo-vocabulary-check.ts):
- Hard-fails on declared `semantic.verb` not in family canon (the doctrinal
contract: subscribers query by family+verb)
- Soft-warns on event name shape drift (declared verb is the truth, name is
informational)
- Allowlists `button.commit-action` with TODO referencing Plan B commit 2
(Button's intent vs contact question per cap. 22 §11 needs design sign-off)
3 mechanical violation fixes:
- textarea: `shift-count-overflow` (family shift, verb limit) → `signal-warn-count-overflow`
(signal.warn + risk per cap. 24 — corregible warning, not context shift)
- password-field: `shift-toggle-visibility` → `commit-toggle-visibility`
(commit.toggle + affirm per cap. 23 — state fixation, not context shift)
- password-field: `shift-caps-state` → `signal-notify-caps-state`
(signal.notify + neutral per cap. 24 — system orients attention without urgency)
- month-grid + year-grid: `nav-step` → `shift-navigate-step` (family/verb
already canonical, name aligned to {family}-{verb}-{variant})
Verification:
- npm run morfo:vocabulary → EXIT 0 (1 allowlisted, 59 pre-existing name-shape
warns documenting visible drift for future sprints)
- npx vitest src/uix/sema src/uix/morfo → 194/194 pass
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updates the canonical semantic vocabulary to match the latest
formulation in src/docs/libro_semantica_completo.txt:
Families (7, was 6):
contact, commit, signal, handle, emerge, shift, sustain
- `alert` → `signal` (rename: same perceptual function — attention /
salience — but the book's name reflects the broader scope including
notify, warn, emphasize, announce).
- `shift` (NEW, transitional): frame change. Modal mount, navigation,
mode change. Differs from emerge in that it reorganizes planes
(heavier backdrop, deeper shadow) — "cruce de umbral" not just
"aparición". Per book rule, the frame doesn't carry intent on its
own; evaluation belongs to a companion `signal` event.
Intents (6, was 5):
neutral, affirm, fulfill, risk, threat, loss
- `loss` (NEW): consummated negative consequence (negative valence,
low activation, posterior). Differs from `threat` (anterior, high
activation, convokes action): loss registers, doesn't convoke. Per
book §11.5 "la amenaza no es la pérdida".
sema-map.ts perceptual values:
- `signal` family entry: kept the prior `alert` numbers verbatim
(semantics is the same — attention/salience) under the new name.
- `shift` family entry: motion 260ms ease-in-out 0.98→1, sound
pitch 500 ascending gain 0.18 (deliberately quieter than emerge's
0.20 to not compete with companion signal), presence with stronger
backdrop (0.5) + shadow (blur 32 y 12 op 0.2) emphasizing
plane reorganization.
- `loss` intent deltas: descending sound contour (-150 pitch),
contraction (scale.to -0.04, motion duration ×0.95 — brief but
grave), color toward purple-grave (hue replace 280, lower
saturation/intensity), withdrawn presence (backdrop 0.05, no glow).
ARIA `role="alert"` (W3C live region) is unrelated and untouched in
soma/announce, soma/toast and morfo announce/field/form. The toast
morfo gains `loss → role:'status' aria-live:'polite'` mapping —
loss is consummated so it shouldn't interrupt.
Verbs added per the book's worked examples: signal {warn, notify,
emphasize}, shift {enter-mode, exit-mode, navigate}, commit {save,
discard, delete}, contact {press, click, hover}, handle {pick, carry,
drop}, emerge {reveal, show, hide}.
Verified: vitest 9 files / 99 tests, smoke 68/68, morfo-check 66/66.
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