docs: la doctrina alcanza a los cambios de la jornada — cinco backlogs y una llave de mas

Auditoria de deriva doc<->codigo sobre los 33 commits del 2026-08-13/14: cinco
sitios seguian describiendo lo que el codigo dejo de hacer ayer. Se corrigen
COMO BITACORA, al final de cada fichero (el cuerpo es el acta de lo que se
firmo, no el estado del codigo; reescribirlo falsearia la firma). El formato
es el que ya existia en `arts/adom` y `soma/textarea`: `## Backlog`.

- `decisions/book-deviations.md` — dos entradas. D.7 nombraba
  `IntentExpectedFamily`, tipo que M6 (13246a2c2) borro, y describia una
  derivacion de DOS cubos que S-33 (1a174d5a6) hizo positiva y TRIPLE. Y el
  caveat SEM-4 de tree-view/tree-grid nombraba `targetOverride` en presente:
  b8aa333fd lo borro, hoy se ancla por identidad (`partInstance`).
- `theming/channels.md` — decia «built-in opt-in channel» de announce.
  S-19(ii) (23b20fff5) lo encendio por defecto en las DOS raices; ese commit
  actualizo `sema.md` y se dejo este atras.
- `architecture/morfo.md` — el §semaSelector no nombraba los dos matchers que
  M5 (b53c93e42) anadio: `state` tipado contra `DataPairOf<M>` y el escape
  `undeclaredState`, que LANZA si el attr resulta estar declarado — es lo que
  mantiene obligatorio el tipado.
- READMEs de virtual-list / virtual-grid — su columna informal «Emitted? NO»
  precede al campo del contrato. c73cae022 hizo que el morfo diga QUIEN
  dispara: `emission: 'host'`. La conducta no cambia; cambia su estatus.

Ademas, `architecture/sema.md`: `{{ announce: false }}` -> `{ announce: false }`,
llave doblada que introduje ayer en 23b20fff5.

Verificado: docs-check 0/625 · prettier sin regresion (los 6 ficheros que
avisan ya avisaban en HEAD, medido con stash). Los ficheros de blocks
(`blocks.md`, `AUDIT-blocks-ledger.md`, `CONTINUE-blocks.md`, `PLAN-blocks.md`)
son de la otra sesion y quedan fuera.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-dir-prefs
dev 2 months ago
parent e468e764b2
commit f1b672da79

@ -1192,3 +1192,22 @@ picker docs still need the event table, targets and `data-event` trace path.
- [component-guide.md](../guides/component-guide.md) — soma component authoring (morfo-specific discipline: translation-namespace grep, DOM-topology audit, smoke validation).
- [sema/README.md](./sema.md) — semantic layer (morfo provides everything Sema needs via `events[].semantic`).
- [`guia-semantica-historica.md`](../decisions/guia-semantica-historica.md) — the original API conventions (historical seed).
## Backlog / Evolution decisions
### 2026-08-13 — `semaSelector`: the `state` matcher speaks the morfo's data contract
Extends §_Typed selector builder — `semaSelector`_ above (M5, `b53c93e42`).
Two slots, deliberately asymmetric:
- **`state?: DataPairOf<M>`** — an `{ attr, value }` pair the part DECLARES in
its `data`. Typed against the morfo, so renaming or dropping the attr breaks
at compile time instead of drifting into a selector that matches nothing.
- **`undeclaredState?: { attr: string; value: string }`** — the escape hatch
for attrs OUTSIDE the contract (a visual wrapper's `data-size`, a
presentation flag like `data-sheet`). It THROWS when the attr turns out to
be declared, which is what keeps the typed slot mandatory: one open slot
with no guard makes the typed one optional in practice.
The pair replaces the free-form state string a cascade rule used to write by
hand — the same drift class M6 closed on names and D.2 closed on emitters.

@ -962,7 +962,7 @@ framework has ONE sink, two emitters, and an app-level component:
late-bound closure, in standalone and attach alike. Default ON because
announce is SUBSTITUTION, not ornament (§channels) — the a11y announcer
ships ambient, the field norm (Angular CDK `LiveAnnouncer`, React Aria).
Opt out with `events: {{ announce: false }}`; an explicitly passed
Opt out with `events: { announce: false }`; an explicitly passed
`announce` wins (the root never overwrites an existing channel). Soma
components still announce exactly once: their runtime keeps `message` out
of the perceptual signal, so the channel no-ops for them.

@ -984,3 +984,38 @@ Este documento mezclaría planos peligrosamente si no se mantiene la disciplina
- **Versionar el documento** cuando cambie un veredicto del autor.
Las familias son el núcleo estable. Los verbos son extensibles bajo criterios. La implementación puede tener aliases y extensiones locales. Solo las extensiones que revelan una diferencia recurrente y general deben pasar al libro.
## Backlog · actualizaciones posteriores
Registro de lo que la implementación cambió DESPUÉS de firmar cada deviación.
El cuerpo de arriba se deja intacto a propósito: es el acta de lo que se firmó,
no el estado del código. Cada entrada nombra la sección que corrige.
### 2026-08-13 — D.7: el eje de intent implementa TRES cubos, y `IntentExpectedFamily` ya no existe
Corrige **§D.7 · Implementación** («TypeScript deriva `IntentExpectedFamily`
desde `intentRequirement === 'required'`»).
- El alias `IntentExpectedFamily` se borró en `13246a2c2` (M6, «un nombre por
concepto»). El nombre vivo es **`IntentRequiredFamily`**.
- La derivación ya no son dos cubos. `1a174d5a6` (S-33) la hizo **positiva y
triple** sobre `intentRequirement`: `IntentRequiredFamily` (`'required'`),
`IntentOptionalFamily` (`'optional'`) e `IntentForbiddenFamily`
(`'forbidden'`). Derivar el segundo cubo con `Exclude<…, Required>` era una
derivación de mundo abierto: cualquier familia nueva caía dentro por
omisión, sin que nadie hubiera decidido su política.
- `intentGuidance` sigue siendo campo doctrinal, tal como se firmó.
### 2026-08-13 — la emisión anclada ya no pasa por `targetOverride`
Corrige el **Caveat (`tree-view` / `tree-grid`) — RESUELTO (SEM-4)**, que
nombra `targetOverride` como el vehículo del anclaje.
La opción de elemento crudo `targetOverride` se **borró** en `b8aa333fd`, una
vez el censo F3 llegó a cero: el estado ilegal del eje quedó inexpresable. El
anclaje se hace hoy por identidad contra el registro —
`runtime.partInstance('branch'|'row', el).trigger` en esos dos providers—,
declarado en el morfo con `semantic.allowedTargets`
([`architecture/morfo.md`](../architecture/morfo.md)). Lo demás del caveat
sigue en pie: los packs construyen sus selectores con `onBranch` / `onRow` y
emisión y cascada casan de punta a punta.

@ -266,3 +266,25 @@ its part. The **visual channel's** 4 facets materialize with eidos tokens
(toggleable so each can be muted one by one); **sound + haptic** are fired by
the real sema engine (`EngineSemantic.emit`, filtered channels). One event,
sema's channels.
## Backlog / Evolution decisions
### 2026-08-13 — `announce` is ON by default in both composition roots
Supersedes the wording in §2 above («`announce` joined on 2026-07-04 as a
built-in **opt-in** channel»). Opt-in described the engine option; it is no
longer what an app gets.
S-19(ii) (`23b20fff5`) wires the channel in `createActiveUix` AND
`attachActiveUix`, default ON. Announce is SUBSTITUTION, not ornament, so the
a11y announcer ships ambient — the field norm (Angular CDK `LiveAnnouncer`,
React Aria). The root is the only place that holds both ends (the shared sink
and the engine), and it registers the channel post-construction with a
late-bound closure, because `uix.announce` does not exist yet at
engine-construction time; any wiring an app wrote itself fell back to the
channel's self-owned regions — a SECOND live `role=status` / `role=alert`
pair, the thing this chapter forbids.
Opt out with `events: { announce: false }`; an explicitly passed `announce`
wins — the root never overwrites an existing channel. Detail:
[`architecture/sema.md`](../architecture/sema.md).

@ -186,3 +186,18 @@ See virtual-list README for the rationale on skipping `handle-scroll*` emission.
- **Visible range** is computed per axis independently (two 1D problems). The cartesian product of visible rows × visible columns gives the cells to render.
- **No ResizeObserver on Cell** — fixed sizes mean we never need to measure. That's the main reason VirtualGrid skips dynamic — measuring would change the axes non-orthogonally.
- **One `Viewport` only** — unlike VirtualList, there's no window-scroll variant for VirtualGrid. 2D window-scroll is niche and the scroll semantics are confusing.
## Backlog / Evolution decisions
### 2026-08-13 — the scroll events declare their emitter: `emission: 'host'`
The **Emitted? NO** column above predates the contract field and only says
that soma stays quiet. `c73cae022` (M1(ii), decision D.2) made the morfo say
WHO fires instead: `handle-scroll-row` and `handle-scroll-column` carry
`emission: 'host'`.
Nothing changed in behaviour — the reasoning in the note above still holds
(the listener fires per pixel; `family.handle` activates haptic; emitting per
event would buzz a phone nonstop). What changed is its status: "not wired" is
now a DECLARED contract, the app owns the cadence, and the D9 inert-event
guard reads the declaration instead of carrying the two events as debt.

@ -275,3 +275,17 @@ Provider. `scrollToIndex` routes automatically to `window.scrollTo` when
- **Initial render**: when `viewportSize === 0` (pre-first-measurement), the provider renders `overscan + 1` rows so first paint has something and the viewport can measure itself.
- **Binary search** (not linear scan) is used to find the first / last visible index against the `offsets` array. For a 10k-row list, that's 14 comparisons per scroll event.
- **CSS containment** is opt-in by the consumer. Adding `contain: strict` (or `contain: layout paint`) on `Viewport` helps the browser skip off-screen layout work. Soma doesn't force it because a few engines have reported regressions in `scrollTo` programmatic scrolling with `contain: strict` — consumers can opt in per their target-browser matrix.
## Backlog / Evolution decisions
### 2026-08-13 — the scroll events declare their emitter: `emission: 'host'`
The **Emitted? NO** column above predates the contract field and only says
that soma stays quiet. `c73cae022` (M1(ii), decision D.2) made the morfo say
WHO fires instead: `handle-scroll` carry `emission: 'host'`.
Nothing changed in behaviour — the reasoning in the note above still holds
(the listener fires per pixel; `family.handle` activates haptic; emitting per
event would buzz a phone nonstop). What changed is its status: "not wired" is
now a DECLARED contract, the app owns the cadence, and the D9 inert-event
guard reads the declaration instead of carrying the event as debt.

Loading…
Cancel
Save

Powered by TurnKey Linux.