diff --git a/README.md b/README.md index d691d2aa1..2fde0da20 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,19 @@ # Name + ### vicen # Synopsis - # Description # Example # Install: + `npm install vicen` # Test: + `npm test` #License: - diff --git a/STUMBLES.md b/STUMBLES.md index 7e7030f52..744ba56e9 100644 --- a/STUMBLES.md +++ b/STUMBLES.md @@ -13,17 +13,17 @@ el ejercicio: 8 de 9 cerrados; de **#7** (contrato CSS-vars soma→eidos) se cer la dirección recipe→tema con un guard (opción C, cazó 3 fantasmas reales), y queda solo el sub-contrato provider→recipe (opción A, opcional). -| # | Tropiezo | Estado | Cómo | -|---|---|---|---| -| 1 | Vocabularios invisibles | ✅ RESUELTO | `npm run docs:vocabularies` → `docs/canon/vocabularies.md` desde los consts + guard docs-check I7 (`116594e0`) | -| 2 | Drift `kind` public/virtual vs internal | ✅ RESUELTO | `MorfoPartKind = 'public' \| 'private' \| 'virtual'` (types.ts) y checklist A-2.2 alineado | -| 3 | No hay capa de gesto radial | ✅ RESUELTO | `Gesture.rotate` 4ª especialización en `soma/layers/gesture` (`18b0d44e`); el Knob lo consume | -| 4 | Doctrina `trigger()` continuo sin cerrar | ✅ RESUELTO | sección `## Continuous components` en `sema.md` (`1da36ca6`), fact-check adversarial | -| 5 | Falta condición `part-absent` | ✅ RESUELTO | `{ when: 'part-absent', part }` en morfo (`11504043`); el morfo del Knob la usa | -| 6 | Formato de `langs/components/{kebab}.ts` | ✅ RESUELTO | forma `LangNode` (`{ key: { es, en } }`, anidable, `satisfies`) documentada en morfo/soma/checklist | -| 7 | CSS-vars del provider sin contrato | 🟡 CASI | dirección **recipe→tema** guardada (opción C: guard de fantasmas de tema en `recipe-css-contract` — pilló 3 bugs reales); queda solo el sub-contrato **provider→recipe** (opción A, opcional) | -| 8 | Docs imprescindibles fuera del paquete | ✅ RESUELTO | `component-audit.md §0` lista el paquete mínimo como archivos exactos | -| 9 | Fricciones menores (docs) | ✅ RESUELTO | `### Authoring notes` en `soma.md` §6: `state()` vs `$state`, `role` opcional en Provider, `Without<>`/`PrimitiveDivAttributes`, ownership de pointermove/up del gesture | +| # | Tropiezo | Estado | Cómo | +| --- | ---------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Vocabularios invisibles | ✅ RESUELTO | `npm run docs:vocabularies` → `docs/canon/vocabularies.md` desde los consts + guard docs-check I7 (`116594e0`) | +| 2 | Drift `kind` public/virtual vs internal | ✅ RESUELTO | `MorfoPartKind = 'public' \| 'private' \| 'virtual'` (types.ts) y checklist A-2.2 alineado | +| 3 | No hay capa de gesto radial | ✅ RESUELTO | `Gesture.rotate` 4ª especialización en `soma/layers/gesture` (`18b0d44e`); el Knob lo consume | +| 4 | Doctrina `trigger()` continuo sin cerrar | ✅ RESUELTO | sección `## Continuous components` en `sema.md` (`1da36ca6`), fact-check adversarial | +| 5 | Falta condición `part-absent` | ✅ RESUELTO | `{ when: 'part-absent', part }` en morfo (`11504043`); el morfo del Knob la usa | +| 6 | Formato de `langs/components/{kebab}.ts` | ✅ RESUELTO | forma `LangNode` (`{ key: { es, en } }`, anidable, `satisfies`) documentada en morfo/soma/checklist | +| 7 | CSS-vars del provider sin contrato | 🟡 CASI | dirección **recipe→tema** guardada (opción C: guard de fantasmas de tema en `recipe-css-contract` — pilló 3 bugs reales); queda solo el sub-contrato **provider→recipe** (opción A, opcional) | +| 8 | Docs imprescindibles fuera del paquete | ✅ RESUELTO | `component-audit.md §0` lista el paquete mínimo como archivos exactos | +| 9 | Fricciones menores (docs) | ✅ RESUELTO | `### Authoring notes` en `soma.md` §6: `state()` vs `$state`, `role` opcional en Provider, `Without<>`/`PrimitiveDivAttributes`, ownership de pointermove/up del gesture | ## 1. Vocabularios canónicos invisibles desde los docs @@ -32,11 +32,11 @@ explícitamente copiar la lista ("a copied list survived at 24 entries while the code grew to 26"). Correcto contra el drift humano — pero un agente que trabaja desde el docs-book **no puede asignar archetypes**: no sé si existe `control`, `handle` o `indicator` para las partes del Knob, y A-2.2 los pide -en *todas* las partes (error-level en el audit). Lo mismo pasa con +en _todas_ las partes (error-level en el audit). Lo mismo pasa con `SEMA_MAP.families[*].hold` (¿cuánto dura el hold de `handle`?), los `kind` categóricos del HapticChannel, y el contenido de `commonLangs`. -**Fix sugerido**: apéndice *generado desde código* en el docs-book +**Fix sugerido**: apéndice _generado desde código_ en el docs-book (`npm run docs:vocabularies`). Generado = sin objeción de drift. Para un agente, ese apéndice es la diferencia entre declarar y adivinar. @@ -90,8 +90,8 @@ contrato-fuera-del-morfo que el framework existe para eliminar. ## 6. El formato del catálogo `langs/components/{kebab}.ts` nunca se muestra -Todos los docs referencian su *ubicación* (morfo.md, soma.md, guide A3, -checklist A-1.3) pero ninguno enseña su *forma*: ¿export nombrado?, ¿record +Todos los docs referencian su _ubicación_ (morfo.md, soma.md, guide A3, +checklist A-1.3) pero ninguno enseña su _forma_: ¿export nombrado?, ¿record `{ key: { en, es } }` o `{ en: { key } }`?, ¿claves anidadas o planas con puntos? Mi `knob.ts` de langs es una conjetura. A-1.3 es error-level: un agente fallará el audit por un archivo cuyo formato no está documentado en @@ -135,10 +135,10 @@ selectors vía `style` no, pero soma+eidos ya son 2). > `recipe-css-contract.test.ts`: toda `var(--x)` **sin fallback** de un recipe > debe resolver a un token declarado en algún sitio del árbol CSS de eidos > (foundation + capas + auto-declaraciones del componente); las `var(--x, -> default)` son runtime-opcionales (vars provider/floating con default) y quedan +default)` son runtime-opcionales (vars provider/floating con default) y quedan > exentas por construcción. El guard cazó **3 fantasmas reales** además de los > del Knob — `card-group --color-content-default`→`-primary`, `link-preview -> --leading-body`→`-normal`, `textarea --font-family-body`→`-primary` — +--leading-body`→`-normal`, `textarea --font-family-body`→`-primary` — > arreglados. Queda solo la dirección **provider→recipe** (opción A). > > **A2 (grep) descartado — probado ambiguo (2026-07-03).** Un `var(--{c}-x)` que diff --git a/docs/CANON.md b/docs/CANON.md index 7af9de3af..19ecb7407 100644 --- a/docs/CANON.md +++ b/docs/CANON.md @@ -21,17 +21,17 @@ three docs after the canon moved to 8). Every other doc links here. > default hold, the intents, haptic kinds, part archetypes, palette scales, > sizes, variants, shared strings)? They are spelled out — GENERATED from the > code, so they never drift — in [`canon/vocabularies.md`](./canon/vocabularies.md). -> This file is the *doctrine*; that one is the *enumerated lists*. +> This file is the _doctrine_; that one is the _enumerated lists_. Two anchors hold this canon honest: - **The book** — `docs/Disenando_lo_que_ocurre_HOMOGENEIZADO.pdf` ("Diseñando lo que ocurre", edición homogeneizada). The editorial source. Each entry cites its chapter. -- **The code** — the runtime is the executable form. Concrete *values* (per-family +- **The code** — the runtime is the executable form. Concrete _values_ (per-family holds, channel signatures, the full verb lists with author extensions) live in code and are **linked, not copied here**, on purpose. This doc fixes the - *doctrine*; the code fixes the *numbers*. + _doctrine_; the code fixes the _numbers_. > Rule: if you need to state a family, an intent, a verb, or a composition rule > in any other doc, **link this file** — do not paste a copy. A second copy is a @@ -51,14 +51,14 @@ The framework encodes this chain literally: **morfo declares** it, **soma executes** it (`runtime.trigger`), **sema** carries the perceptual vocabulary and dispatches channels, **eidos** materializes the visual channels. See [architecture/active-architecture.md](./architecture/active-architecture.md) for the layer wiring; -this file is only the *vocabulary* that flows through it. +this file is only the _vocabulary_ that flows through it. --- ## 2. The 8 families -A family is **not** a component or an effect. It is *a recurring class of event -that answers one perceptual question* (book ch. 8, ch. 21). The eight are the +A family is **not** a component or an effect. It is _a recurring class of event +that answers one perceptual question_ (book ch. 8, ch. 21). The eight are the reduction of a wider exploratory set down to a minimal-yet-sufficient grammar (book ch. 8 §1–3). @@ -67,26 +67,26 @@ Canonical list: `SemaFamily` in [`src/uix/sema/types.ts`](../src/uix/sema/types. active channels and hold: `SEMA_MAP.families` in [`src/uix/sema/sema-map.ts`](../src/uix/sema/sema-map.ts). -| Family | Perceptual question | Accepts intent | Book | -| --- | --- | --- | --- | -| `contact` | ¿el sistema ha sentido mi acción? | leve / anticipatory | ch. 22 | -| `commit` | ¿qué quedó fijado o tuvo consecuencia? | **fully** | ch. 23 | -| `signal` | ¿algo reclama mi atención? | **fully** | ch. 24 | -| `handle` | ¿estoy manipulando directamente este objeto? | only on `drop` | ch. 25 | -| `emerge` | ¿algo entró o salió del campo perceptivo? | normally no | ch. 26 | -| `shift` | ¿cambió el contexto / régimen? | normally no | ch. 27 | -| `sustain` | ¿esto sigue ocurriendo? | normally no | ch. 28 | -| `delegate` | ¿quién actúa ahora? | no by default | ch. 29 | +| Family | Perceptual question | Accepts intent | Book | +| ---------- | -------------------------------------------- | ------------------- | ------ | +| `contact` | ¿el sistema ha sentido mi acción? | leve / anticipatory | ch. 22 | +| `commit` | ¿qué quedó fijado o tuvo consecuencia? | **fully** | ch. 23 | +| `signal` | ¿algo reclama mi atención? | **fully** | ch. 24 | +| `handle` | ¿estoy manipulando directamente este objeto? | only on `drop` | ch. 25 | +| `emerge` | ¿algo entró o salió del campo perceptivo? | normally no | ch. 26 | +| `shift` | ¿cambió el contexto / régimen? | normally no | ch. 27 | +| `sustain` | ¿esto sigue ocurriendo? | normally no | ch. 28 | +| `delegate` | ¿quién actúa ahora? | no by default | ch. 29 | `contact`/`commit`/`signal`/`handle` are the **valenced** families; `emerge`/`shift`/`sustain`/`delegate` are the **transitional** ones (book ch. 9). -That split is a *classification*; it no longer dictates the intent rule — the +That split is a _classification_; it no longer dictates the intent rule — the policy (§4) does. The perceptual question is a **demand on the expression**, not a label: a family that fails to answer its own question has failed at the only thing it exists for. `shift` is the case that proves it — its question IS the crossing, so a -`shift` nobody perceives crossing is the *shift invisible* of the antipattern +`shift` nobody perceives crossing is the _shift invisible_ of the antipattern catalogue (ch. 34 §14), and it was exactly that here until 2026-08-11: it sounded like a slide and did not slide (§8 rule 5; the sense it now travels in, `data-event-direction`, in §9). @@ -102,20 +102,20 @@ Canonical list: `INTENTS` in [`src/uix/intent.ts`](../src/uix/intent.ts) — own by UIX itself, not by a layer. Per-intent perceptual deltas: `SEMA_MAP.intents` in [`sema-map.ts`](../src/uix/sema/sema-map.ts). -| Intent | Valence / activation | Reading | Book | -| --- | --- | --- | --- | -| `neutral` | low activation, no strong valence | notar sin tono | ch. 10 | -| `affirm` | positive, low activation | "todo va bien" (confirmación suave) | ch. 10 | -| `fulfill` | positive, mid-high activation | "objetivo cumplido" (resuelve tensión) | ch. 10 | -| `risk` | negative, moderate activation | "revisa esto" (corregible) | ch. 10 | -| `threat` | negative, high activation | "atiende ahora" (aún evitable) | ch. 10 | -| `loss` | negative, lower activation, posterior | "ya ocurrió" (consecuencia consumada) | ch. 10 | +| Intent | Valence / activation | Reading | Book | +| --------- | ------------------------------------- | -------------------------------------- | ------ | +| `neutral` | low activation, no strong valence | notar sin tono | ch. 10 | +| `affirm` | positive, low activation | "todo va bien" (confirmación suave) | ch. 10 | +| `fulfill` | positive, mid-high activation | "objetivo cumplido" (resuelve tensión) | ch. 10 | +| `risk` | negative, moderate activation | "revisa esto" (corregible) | ch. 10 | +| `threat` | negative, high activation | "atiende ahora" (aún evitable) | ch. 10 | +| `loss` | negative, lower activation, posterior | "ya ocurrió" (consecuencia consumada) | ch. 10 | Three distinctions the inherited semaphore (`success`/`warning`/`danger`/`info`) collapses, and that this canon keeps apart (book ch. 10 §4): -- **`threat` ≠ `loss`** — threat convokes action *before* the consequence; loss - registers it *after*. They must not look/sound the same. +- **`threat` ≠ `loss`** — threat convokes action _before_ the consequence; loss + registers it _after_. They must not look/sound the same. - **`affirm` ≠ `fulfill`** — soft confirmation vs resolution of a tension. - **`info` is not an intent** — it is attentional salience → `signal.* + neutral`. @@ -131,10 +131,10 @@ checkpoint (verdicts C1/C3 — historical record in selecting it (calendar, select, combobox, grid-list, listbox). Un-marking a binary (the uncheck direction of `commit.toggle`) = **`neutral`**. Abandoning or clearing (`commit.reset`, `commit.cancel`) = **`neutral`**. -- **Binary toggles — two sanctioned models.** *Mark-done* semantics, where +- **Binary toggles — two sanctioned models.** _Mark-done_ semantics, where the direction itself carries the evaluative load, use two static directional events (checkbox: `commit-toggle-check` affirm / - `commit-toggle-uncheck` neutral). *Mode-switch* semantics, where the weight + `commit-toggle-uncheck` neutral). _Mode-switch_ semantics, where the weight depends on WHAT is being switched, use one event with `intent: { fromProp: 'intent', default: 'neutral' }` so the consumer grades it (switch, toggle, toggle-group). Choose by the question: does the ON @@ -145,7 +145,7 @@ checkpoint (verdicts C1/C3 — historical record in ## 4. Intent policy — `SEMA_FAMILY_POLICY` -Whether intent is *required* is set **per family by policy**, not by the +Whether intent is _required_ is set **per family by policy**, not by the valenced/transitional split (book ch. 8 §6, ch. 9). Two independent axes, in [`src/uix/sema/types.ts`](../src/uix/sema/types.ts) (`SEMA_FAMILY_POLICY`): @@ -155,7 +155,7 @@ valenced/transitional split (book ch. 8 §6, ch. 9). Two independent axes, in it today). Drives the `SemaEvent` / `MorfoEventSemantic` discriminated unions. - **`intentGuidance`** (`'expected' | 'contextual' | 'discouraged'`) — doctrinal hint, no type effect. `commit`/`signal` = `expected`; `contact` = `discouraged` - (book ch. 22 §11: *"el intent fuerte no debería vivir en el contacto"*); the + (book ch. 22 §11: _"el intent fuerte no debería vivir en el contacto"_); the rest = `contextual`. Runtime: `validateSemaEvent` throws when a `required` family is built without @@ -166,8 +166,8 @@ intent. ## 5. The evaluable-vs-structural rule > **The intent lives in the evaluable event (the message), not in the structural -> container (the frame).** (book ch. 9 — *"Qué eventos son mensaje y qué eventos -> son marco"*; restated in Appendix A.) +> container (the frame).** (book ch. 9 — _"Qué eventos son mensaje y qué eventos +> son marco"_; restated in Appendix A.) A modal that opens is `shift.enter-mode` (frame); the warning inside it is `signal.warn + threat` (message). A spinner is `sustain.progress` (frame); the @@ -175,11 +175,11 @@ failure after it is `commit.fail + risk` (message). Cargar el intent en el marco es el antipatrón capital (book ch. 34 §5 "Modal rojo", §4 "todo lo que aparece es alerta"). -In the framework this is enforced declaratively: the event that *carries* the +In the framework this is enforced declaratively: the event that _carries_ the intent declares it in its morfo `events[].semantic.intent`; the frame event does -not. The runtime never infers intent (book **Appendix A**: *"El intent no se +not. The runtime never infers intent (book **Appendix A**: _"El intent no se hereda implícitamente… El runtime no debe adivinar. La semántica debe estar -declarada."*). This is exactly the morfo → soma → sema → eidos contract. +declarada."_). This is exactly the morfo → soma → sema → eidos contract. --- @@ -214,8 +214,8 @@ Verbs that look like one family but belong to another (book canon): `select` / ## 7. Expression channels + the owner split The book defines **8 expression channels** (book ch. 11–20): tiempo · motion · -presencia · profundidad · forma · color · sonido · háptica. *"El color expresa el -intent, no lo define"* (ch. 16); *"ningún canal agota la semántica"* — robustness +presencia · profundidad · forma · color · sonido · háptica. _"El color expresa el +intent, no lo define"_ (ch. 16); _"ningún canal agota la semántica"_ — robustness = a reading that survives the loss of a channel (ch. 19, ch. 33). The framework **splits these channels by owner** (decision recorded in @@ -232,11 +232,11 @@ The framework **splits these channels by owner** (decision recorded in channel; this says what a component actually WRITES, which is the part authors get wrong: -| Channel | The component writes | Where the values live | -| --- | --- | --- | -| visual (motion / color / presence / depth / shape) | nothing — it declares the EVENT; eidos reacts in CSS to `data-event-*` | eidos recipes + the token engines | -| `sound` | **one NAME**, and only when it differs from its family's default | `SEMA_MAP.sounds` — the sound pack | -| `haptic` | a categorical `kind` (`tick` / `tap` / `pulse` / `success` / …) | `HapticChannel` maps kinds to patterns | +| Channel | The component writes | Where the values live | +| -------------------------------------------------- | ---------------------------------------------------------------------- | -------------------------------------- | +| visual (motion / color / presence / depth / shape) | nothing — it declares the EVENT; eidos reacts in CSS to `data-event-*` | eidos recipes + the token engines | +| `sound` | **one NAME**, and only when it differs from its family's default | `SEMA_MAP.sounds` — the sound pack | +| `haptic` | a categorical `kind` (`tick` / `tap` / `pulse` / `success` / …) | `HapticChannel` maps kinds to patterns | The rule is the same in all three rows and it is the point: **a component says WHAT occurs, never how loud, how bright or how long.** @@ -286,10 +286,10 @@ ch. 6 §13). The rules are derived from perceptual need, not decree (ch. 30 §3) 3. **threat precedes loss** — never `commit.delete + threat` (before/after the consequence). 4. **emerge does not absorb the content's intent** — `emerge.open → signal.warn + - risk`, not `emerge.open + risk` (frame ≠ message). +risk`, not `emerge.open + risk` (frame ≠ message). 5. **shift must orient** the context change (focus + title + landmark) — and it must be PERCEIVED crossing it. A frame that swaps with nothing moving is the - *shift invisible* of the antipattern catalogue (ch. 34 §14): the user is + _shift invisible_ of the antipattern catalogue (ch. 34 §14): the user is somewhere else and was never told they travelled. Until 2026-08-11 `shift` was exactly that in this framework — it sounded (`slide`) and did not slide, because `BUILTIN_SIGNATURES` had a FAMILY-keyed visual signature for diff --git a/docs/README.md b/docs/README.md index 8720c639f..ca8b478ed 100644 --- a/docs/README.md +++ b/docs/README.md @@ -34,15 +34,15 @@ architecture in [`architecture/active-architecture.md`](./architecture/active-ar The corpus is organized in layers of permanence, not by folder: -| Stratum | What it is | Where | -| --- | --- | --- | -| **E0 — orientation** | this file; the narrative entry; the glossary | `docs/README.md`, `architecture/overview.md`, `docs/glossary.md` | -| **E1 — architecture** | how the layers fit | `docs/architecture/` (the book chapters) + in-place stubs | -| **E2 — canon** | the fixed vocabulary & contracts | `CANON.md`, `canon/tsc.md`, `canon/recipe-contract.md` | -| **E3 — decisions / RFC** | *why* it is built this way | `decisions.md` + the RFCs & decision logs | -| **E4 — guides** | how to do a thing | `guides/`, `theming/guide.md` | -| **E5 — module reference** | per-artifact docs | `arts/*/README`, `libs/*`, `svrs/*`, `packs/*/README` | -| **process** | ephemeral (hand-offs, snapshots, audits) | `docs/process/` — never a source of truth | +| Stratum | What it is | Where | +| ------------------------- | -------------------------------------------- | ---------------------------------------------------------------- | +| **E0 — orientation** | this file; the narrative entry; the glossary | `docs/README.md`, `architecture/overview.md`, `docs/glossary.md` | +| **E1 — architecture** | how the layers fit | `docs/architecture/` (the book chapters) + in-place stubs | +| **E2 — canon** | the fixed vocabulary & contracts | `CANON.md`, `canon/tsc.md`, `canon/recipe-contract.md` | +| **E3 — decisions / RFC** | _why_ it is built this way | `decisions.md` + the RFCs & decision logs | +| **E4 — guides** | how to do a thing | `guides/`, `theming/guide.md` | +| **E5 — module reference** | per-artifact docs | `arts/*/README`, `libs/*`, `svrs/*`, `packs/*/README` | +| **process** | ephemeral (hand-offs, snapshots, audits) | `docs/process/` — never a source of truth | Writing or editing docs? The conventions that keep this corpus drift-free — link the canon, don't copy it; keep reference docs timeless; one source per @@ -62,60 +62,60 @@ invented vocabulary (morfo, archetype, hold, TSC, …) one line each. ### E1 — Architecture -| Doc | Layer | -| --- | --- | -| [`architecture/active-architecture.md`](./architecture/active-architecture.md) | The whole system — start here for depth | -| [`architecture/morfo.md`](./architecture/morfo.md) | The declarative contract (DNA) | -| [`architecture/soma.md`](./architecture/soma.md) · [`SOMA_ARCHITECTURE.md`](./architecture/soma-architecture.md) | Headless behavior — soma.md onboards, ARCHITECTURE is the deep reference | -| [`architecture/sema.md`](./architecture/sema.md) | Perceptual engine + channels (sound/haptic) + cascade | -| [`architecture/eidos.md`](./architecture/eidos.md) | The visual layer | -| [`architecture/active-uix.md`](./architecture/active-uix.md) | Composition root (boot modes) — first chapter migrated into the book tree ([`docs/process/PLAN-docs-book.md`](./process/PLAN-docs-book.md)) | -| [`architecture/active-app.md`](./architecture/active-app.md) | The arts composition root — fixed core (logger/bus/timers/orca/prefs) + declared services + orchestration; the App-side analogue of `active-uix` | -| [`arts/README.md`](../src/arts/README.md) | Runtime artifacts (`Engine*`/`Active*`) | -| [`architecture/packs.md`](./architecture/packs.md) | The pack tier — encapsulated opt-in collections above the layers (the canon-vs-pack admission rule, the P contract, the `Aura` promotion path) | -| [`architecture/agent.md`](./architecture/agent.md) | The agentic axis — the delegation model (`delegate` family), the actor primitive, the per-component participation contract, minimum contracts, a11y + threat doctrine | -| [`spec/delegation-contract.md`](./spec/delegation-contract.md) | **NORMATIVE** — the delegation contract as a citable specification (RFC-2119, stable `AG-n` requirement ids, date-versioned). `agent.md` is the WHY; this is the WHAT a conformant implementation must do. Status: DRAFT | -| [`architecture/blocks.md`](./architecture/blocks.md) | The blocks tier — page-function compositions above the canon (the canon-vs-block admission rule, the B contract, `blocks:check`) | +| Doc | Layer | +| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [`architecture/active-architecture.md`](./architecture/active-architecture.md) | The whole system — start here for depth | +| [`architecture/morfo.md`](./architecture/morfo.md) | The declarative contract (DNA) | +| [`architecture/soma.md`](./architecture/soma.md) · [`SOMA_ARCHITECTURE.md`](./architecture/soma-architecture.md) | Headless behavior — soma.md onboards, ARCHITECTURE is the deep reference | +| [`architecture/sema.md`](./architecture/sema.md) | Perceptual engine + channels (sound/haptic) + cascade | +| [`architecture/eidos.md`](./architecture/eidos.md) | The visual layer | +| [`architecture/active-uix.md`](./architecture/active-uix.md) | Composition root (boot modes) — first chapter migrated into the book tree ([`docs/process/PLAN-docs-book.md`](./process/PLAN-docs-book.md)) | +| [`architecture/active-app.md`](./architecture/active-app.md) | The arts composition root — fixed core (logger/bus/timers/orca/prefs) + declared services + orchestration; the App-side analogue of `active-uix` | +| [`arts/README.md`](../src/arts/README.md) | Runtime artifacts (`Engine*`/`Active*`) | +| [`architecture/packs.md`](./architecture/packs.md) | The pack tier — encapsulated opt-in collections above the layers (the canon-vs-pack admission rule, the P contract, the `Aura` promotion path) | +| [`architecture/agent.md`](./architecture/agent.md) | The agentic axis — the delegation model (`delegate` family), the actor primitive, the per-component participation contract, minimum contracts, a11y + threat doctrine | +| [`spec/delegation-contract.md`](./spec/delegation-contract.md) | **NORMATIVE** — the delegation contract as a citable specification (RFC-2119, stable `AG-n` requirement ids, date-versioned). `agent.md` is the WHY; this is the WHAT a conformant implementation must do. Status: DRAFT | +| [`architecture/blocks.md`](./architecture/blocks.md) | The blocks tier — page-function compositions above the canon (the canon-vs-block admission rule, the B contract, `blocks:check`) | ### E2 — Canon -| Doc | What it fixes | -| --- | --- | -| [`docs/CANON.md`](./CANON.md) | The semantic vocabulary — authoritative | -| [`canon/vocabularies.md`](./canon/vocabularies.md) | **The closed sets, GENERATED from the code** — archetypes, families+holds, verbs, intents, haptic kinds, palette scales, sizes, variants, shared strings. Build a component from exactly these (`npm run docs:vocabularies`; guarded by `docs:check`) | -| [`canon/tsc.md`](./canon/tsc.md) | Token Scope Contract — where every eidos token may be emitted | -| [`canon/recipe-contract.md`](./canon/recipe-contract.md) | Recipe Contract — which transversal theming systems every recipe must consume (enforced by `component-audit` R-4.x) | -| [`canon/direction-contract.md`](./canon/direction-contract.md) | Direction Contract — the `prop → prefs → 'ltr'` resolution chain, which attribute carries the direction (raw `dir` vs resolved `data-dir`) and which selector form may read it (`:dir()`; `[dir='rtl']` is forbidden) (enforced by `rtl-lint` RTL-1) | +| Doc | What it fixes | +| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`docs/CANON.md`](./CANON.md) | The semantic vocabulary — authoritative | +| [`canon/vocabularies.md`](./canon/vocabularies.md) | **The closed sets, GENERATED from the code** — archetypes, families+holds, verbs, intents, haptic kinds, palette scales, sizes, variants, shared strings. Build a component from exactly these (`npm run docs:vocabularies`; guarded by `docs:check`) | +| [`canon/tsc.md`](./canon/tsc.md) | Token Scope Contract — where every eidos token may be emitted | +| [`canon/recipe-contract.md`](./canon/recipe-contract.md) | Recipe Contract — which transversal theming systems every recipe must consume (enforced by `component-audit` R-4.x) | +| [`canon/direction-contract.md`](./canon/direction-contract.md) | Direction Contract — the `prop → prefs → 'ltr'` resolution chain, which attribute carries the direction (raw `dir` vs resolved `data-dir`) and which selector form may read it (`:dir()`; `[dir='rtl']` is forbidden) (enforced by `rtl-lint` RTL-1) | ### E3 — Decisions / RFC -| Doc | What it records | -| --- | --- | -| [`docs/decisions.md`](./decisions.md) | The RFC/design index — entry to all rationale | -| [`docs/next-features.md`](./next-features.md) | The initiative registry — user-decided future work (scope, sequencing, dependencies), fed by audits and sessions | -| [`rfcs/`](./rfcs/) | The eidos engine RFCs: [`rfc-color-model`](./rfcs/rfc-color-model.md) · [`rfc-color-engine`](./rfcs/rfc-color-engine.md) · [`rfc-typography`](./rfcs/rfc-typography.md) · [`rfc-depth`](./rfcs/rfc-depth.md) · [`rfc-shape`](./rfcs/rfc-shape.md) · [`rfc-structure`](./rfcs/rfc-structure.md) · [`rfc-scaling`](./rfcs/rfc-scaling.md) | -| [`decisions/design-text-effects.md`](./decisions/design-text-effects.md) | The text-effects component family — why text animations are canon (not the pack tier), the CountUp-service vs `Text*`-decorative split, and the a11y / measurement doctrine every member obeys | -| [`decisions/book-deviations.md`](./decisions/book-deviations.md) | Where the implementation deviates from / extends the book (Spanish — the author's decision logbook) | -| [`decisions/guia-semantica-historica.md`](./decisions/guia-semantica-historica.md) | The founding implementation guide (Spanish, historical seed — superseded by `CANON.md` + code) | -| [`theming/channels.md`](./theming/channels.md) | The eight expression channels, synthesized | -| [`theming/notes.md`](./theming/notes.md) | Theming: comparison vs reference libs + FAQ | -| [`theming/changelog.md`](./theming/changelog.md) | The theming chronicle — the dated history behind the reference's standing decisions (§13, §20–§38) | +| Doc | What it records | +| ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`docs/decisions.md`](./decisions.md) | The RFC/design index — entry to all rationale | +| [`docs/next-features.md`](./next-features.md) | The initiative registry — user-decided future work (scope, sequencing, dependencies), fed by audits and sessions | +| [`rfcs/`](./rfcs/) | The eidos engine RFCs: [`rfc-color-model`](./rfcs/rfc-color-model.md) · [`rfc-color-engine`](./rfcs/rfc-color-engine.md) · [`rfc-typography`](./rfcs/rfc-typography.md) · [`rfc-depth`](./rfcs/rfc-depth.md) · [`rfc-shape`](./rfcs/rfc-shape.md) · [`rfc-structure`](./rfcs/rfc-structure.md) · [`rfc-scaling`](./rfcs/rfc-scaling.md) | +| [`decisions/design-text-effects.md`](./decisions/design-text-effects.md) | The text-effects component family — why text animations are canon (not the pack tier), the CountUp-service vs `Text*`-decorative split, and the a11y / measurement doctrine every member obeys | +| [`decisions/book-deviations.md`](./decisions/book-deviations.md) | Where the implementation deviates from / extends the book (Spanish — the author's decision logbook) | +| [`decisions/guia-semantica-historica.md`](./decisions/guia-semantica-historica.md) | The founding implementation guide (Spanish, historical seed — superseded by `CANON.md` + code) | +| [`theming/channels.md`](./theming/channels.md) | The eight expression channels, synthesized | +| [`theming/notes.md`](./theming/notes.md) | Theming: comparison vs reference libs + FAQ | +| [`theming/changelog.md`](./theming/changelog.md) | The theming chronicle — the dated history behind the reference's standing decisions (§13, §20–§38) | ### E4 — Guides -| Doc | How to | -| --- | --- | -| [`docs/building-a-component.md`](./building-a-component.md) | **Build a component — start here.** The cross-layer route (9 phases, one doc per phase, one guard per phase) | -| [`guides/component-guide.md`](./guides/component-guide.md) | The soma phase in depth (ordered steps + rules A1–A37) | -| [`guides/completion-checklist.md`](./guides/completion-checklist.md) | Decide when a component is *done* (machine-audited) | -| [`theming/reference.md`](./theming/reference.md) · [`theming/guide.md`](./theming/guide.md) | Theming reference (E1) + the add-component / define-theme how-tos (E4) | -| [`theming/motion.md`](./theming/motion.md) | The motion model — two moments, F1–F7, the preset/signature system (reference) | -| [`theming/gradient-finish.md`](./theming/gradient-finish.md) | The gradient finish — a gradient is a MATERIAL of the fill, never a color identity: the anchored ramp («la rampa huye de la tinta»), the dial, the executable guard, and the full decision record (D1–D9) | -| [`eidos/components/README.md`](../src/uix/eidos/components/README.md) | The eidos component pattern | -| [`theming/motion-guide.md`](./theming/motion-guide.md) | Animate it — the `motion` prop, the preset catalog, loops, stagger, reduced-motion (links the model + the motion RFC) | -| [`guides/demo-authoring.md`](./guides/demo-authoring.md) | Author an interactive demo page | -| [`guides/component-audit.md`](./guides/component-audit.md) | The binding pre-flight audit before touching any component | -| [`docs/consuming.md`](./consuming.md) | Consume the framework from an app in this workspace — aliases, runes, composition root, CSS and assets, the pre-hydration boot | +| Doc | How to | +| ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`docs/building-a-component.md`](./building-a-component.md) | **Build a component — start here.** The cross-layer route (9 phases, one doc per phase, one guard per phase) | +| [`guides/component-guide.md`](./guides/component-guide.md) | The soma phase in depth (ordered steps + rules A1–A37) | +| [`guides/completion-checklist.md`](./guides/completion-checklist.md) | Decide when a component is _done_ (machine-audited) | +| [`theming/reference.md`](./theming/reference.md) · [`theming/guide.md`](./theming/guide.md) | Theming reference (E1) + the add-component / define-theme how-tos (E4) | +| [`theming/motion.md`](./theming/motion.md) | The motion model — two moments, F1–F7, the preset/signature system (reference) | +| [`theming/gradient-finish.md`](./theming/gradient-finish.md) | The gradient finish — a gradient is a MATERIAL of the fill, never a color identity: the anchored ramp («la rampa huye de la tinta»), the dial, the executable guard, and the full decision record (D1–D9) | +| [`eidos/components/README.md`](../src/uix/eidos/components/README.md) | The eidos component pattern | +| [`theming/motion-guide.md`](./theming/motion-guide.md) | Animate it — the `motion` prop, the preset catalog, loops, stagger, reduced-motion (links the model + the motion RFC) | +| [`guides/demo-authoring.md`](./guides/demo-authoring.md) | Author an interactive demo page | +| [`guides/component-audit.md`](./guides/component-audit.md) | The binding pre-flight audit before touching any component | +| [`docs/consuming.md`](./consuming.md) | Consume the framework from an app in this workspace — aliases, runes, composition root, CSS and assets, the pre-hydration boot | ### E5 — Module reference @@ -125,33 +125,33 @@ and the server-authoritative engines in `src/svrs/`. ## "I want to…" -| Goal | Go to | -| --- | --- | -| Run it and make a first change | [`docs/getting-started.md`](./getting-started.md) | -| Build an app on the framework | [`docs/consuming.md`](./consuming.md) — the workspace contract | -| Understand the framework | [`architecture/overview.md`](./architecture/overview.md) → [`architecture/active-architecture.md`](./architecture/active-architecture.md) | -| Know why UIX, not Radix / Mantine | [`docs/comparison.md`](./comparison.md) | -| Know what a family / intent / verb means | `docs/CANON.md` (doctrine) · [`canon/vocabularies.md`](./canon/vocabularies.md) (the generated closed sets) | -| Know which archetype / hold / haptic kind / scale a part or event may use | [`canon/vocabularies.md`](./canon/vocabularies.md) — the authoritative lists, generated from the code | -| **Build a new component** | [`docs/building-a-component.md`](./building-a-component.md) — the one door: the 9-phase route across all layers, with the guard for each phase and the known superseded-doc traps | -| Know if a component is finished | [`guides/completion-checklist.md`](./guides/completion-checklist.md) (`npm run component:audit`) | -| Theme it / add a token | [`theming/reference.md`](./theming/reference.md) + [`canon/tsc.md`](./canon/tsc.md) | -| Animate it (motion · loops · stagger · reduced-motion) | [`theming/motion-guide.md`](./theming/motion-guide.md) | -| Make it work in RTL (assert a direction · mirror the paint) | [`canon/direction-contract.md`](./canon/direction-contract.md) (`npm run rtl:check`) | -| Understand why a decision was made | `docs/decisions.md` → the relevant RFC / [`decisions/book-deviations.md`](./decisions/book-deviations.md) | -| Use a runtime artifact (auth, cache, http, …) | [`architecture/active-app.md`](./architecture/active-app.md) (the composition root) → [`arts/README.md`](../src/arts/README.md) (the map) → `src/arts/{name}/README.md` (per-artifact) | -| **Write or edit documentation** | [`docs/authoring.md`](./authoring.md) — the authoring rules | -| Test or validate a change | [`docs/testing-and-tooling.md`](./testing-and-tooling.md) — tests, validators, codegen, SSR | +| Goal | Go to | +| ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Run it and make a first change | [`docs/getting-started.md`](./getting-started.md) | +| Build an app on the framework | [`docs/consuming.md`](./consuming.md) — the workspace contract | +| Understand the framework | [`architecture/overview.md`](./architecture/overview.md) → [`architecture/active-architecture.md`](./architecture/active-architecture.md) | +| Know why UIX, not Radix / Mantine | [`docs/comparison.md`](./comparison.md) | +| Know what a family / intent / verb means | `docs/CANON.md` (doctrine) · [`canon/vocabularies.md`](./canon/vocabularies.md) (the generated closed sets) | +| Know which archetype / hold / haptic kind / scale a part or event may use | [`canon/vocabularies.md`](./canon/vocabularies.md) — the authoritative lists, generated from the code | +| **Build a new component** | [`docs/building-a-component.md`](./building-a-component.md) — the one door: the 9-phase route across all layers, with the guard for each phase and the known superseded-doc traps | +| Know if a component is finished | [`guides/completion-checklist.md`](./guides/completion-checklist.md) (`npm run component:audit`) | +| Theme it / add a token | [`theming/reference.md`](./theming/reference.md) + [`canon/tsc.md`](./canon/tsc.md) | +| Animate it (motion · loops · stagger · reduced-motion) | [`theming/motion-guide.md`](./theming/motion-guide.md) | +| Make it work in RTL (assert a direction · mirror the paint) | [`canon/direction-contract.md`](./canon/direction-contract.md) (`npm run rtl:check`) | +| Understand why a decision was made | `docs/decisions.md` → the relevant RFC / [`decisions/book-deviations.md`](./decisions/book-deviations.md) | +| Use a runtime artifact (auth, cache, http, …) | [`architecture/active-app.md`](./architecture/active-app.md) (the composition root) → [`arts/README.md`](../src/arts/README.md) (the map) → `src/arts/{name}/README.md` (per-artifact) | +| **Write or edit documentation** | [`docs/authoring.md`](./authoring.md) — the authoring rules | +| Test or validate a change | [`docs/testing-and-tooling.md`](./testing-and-tooling.md) — tests, validators, codegen, SSR | ## Authoritative sources & rules -- **Editorial source**: [`docs/Disenando_lo_que_ocurre_FINAL.pdf`](./Disenando_lo_que_ocurre_FINAL.pdf) — the book *Diseñando lo que ocurre* (FINAL edition, 426 pp., tracked in-repo since 2026-07-11; the `.docx` sibling is the editable master), the origin of the semantic canon. +- **Editorial source**: [`docs/Disenando_lo_que_ocurre_FINAL.pdf`](./Disenando_lo_que_ocurre_FINAL.pdf) — the book _Diseñando lo que ocurre_ (FINAL edition, 426 pp., tracked in-repo since 2026-07-11; the `.docx` sibling is the editable master), the origin of the semantic canon. - **Agent rules**: [`CLAUDE.md`](../CLAUDE.md) and [`AGENTS.md`](../AGENTS.md) — the build/test commands, code style, and the hard rules. Read these before editing. ## Process (ephemeral — not a source of truth) [`docs/process/`](./process/) holds session hand-offs, architecture snapshots and -audits. They record *what happened*, not *what is true* — the docs above are the +audits. They record _what happened_, not _what is true_ — the docs above are the truth. The active corpus-migration state is in [`docs/process/CONTINUE-docs-corpus.md`](./process/CONTINUE-docs-corpus.md). diff --git a/docs/architecture/active-app.md b/docs/architecture/active-app.md index 11638a1b3..e30dd2591 100644 --- a/docs/architecture/active-app.md +++ b/docs/architecture/active-app.md @@ -168,24 +168,24 @@ error — the wrapping is cheap and uniform. ## Available services -| Factory | Slot | Notes | -| ---------------------------------------- | ------------- | ------------------------------------------------------------------------------------- | -| `defineActiveLangs(options)` | `langs` | Schema is required; follows `core.prefs.language` when that dimension exists. | -| `defineActiveStorage(options)` | `storage` | Memory adapter by default. | -| `defineActiveClipboard(options)` | `clipboard` | Lazy capability wrapper around `navigator.clipboard.writeText` or an injected writer. | -| `defineActiveDom(props)` | `dom` | Inert on the server. | -| `defineActiveFormat(options)` | `format` | Reads `core.prefs.locale` when that dimension exists. | -| `defineActiveCache(options)` | `cache` | Passive — invalidation is driven by orca presets. | -| `defineActiveSession(options)` | `session` | Publishes `SESSION_EVENT_*` on the bus. | -| `defineActivePerm(options)` | `perm` | Auto-invalidation is OFF; use orca preset. | -| `defineActiveAuth(options)` | `auth` | Requires an HTTP client in `options`. | -| `defineActiveConnections(options)` | `connections` | Identity tracking via orca preset. | -| `defineEngineHttp(options)` | `http` | Engine only — no Active wrapper. | -| `defineEngineSium(options)` | `sium` | Wires to `langs` automatically when declared. | +| Factory | Slot | Notes | +| ---------------------------------------- | ------------- | ---------------------------------------------------------------------------------------- | +| `defineActiveLangs(options)` | `langs` | Schema is required; follows `core.prefs.language` when that dimension exists. | +| `defineActiveStorage(options)` | `storage` | Memory adapter by default. | +| `defineActiveClipboard(options)` | `clipboard` | Lazy capability wrapper around `navigator.clipboard.writeText` or an injected writer. | +| `defineActiveDom(props)` | `dom` | Inert on the server. | +| `defineActiveFormat(options)` | `format` | Reads `core.prefs.locale` when that dimension exists. | +| `defineActiveCache(options)` | `cache` | Passive — invalidation is driven by orca presets. | +| `defineActiveSession(options)` | `session` | Publishes `SESSION_EVENT_*` on the bus. | +| `defineActivePerm(options)` | `perm` | Auto-invalidation is OFF; use orca preset. | +| `defineActiveAuth(options)` | `auth` | Requires an HTTP client in `options`. | +| `defineActiveConnections(options)` | `connections` | Identity tracking via orca preset. | +| `defineEngineHttp(options)` | `http` | Engine only — no Active wrapper. | +| `defineEngineSium(options)` | `sium` | Wires to `langs` automatically when declared. | | `defineActiveAgent(options)` | `agent` | `timers` + `logger` come from the App core; identity / policy / transport are the app's. | -| `defineEngineMotion(options)` | `motion` | Engine only. Declares `serviceDependencies: ['dom']`; degrades without it. | -| `defineEngineScene(options)` | `scene` | Engine only — the ambient-scene runtime the canon shares as `uix.scene`. | -| `defineEngineSound(options)` | `sound` | Engine only — the Web Audio runtime extracted from sema's `SoundChannel`. | +| `defineEngineMotion(options)` | `motion` | Engine only. Declares `serviceDependencies: ['dom']`; degrades without it. | +| `defineEngineScene(options)` | `scene` | Engine only — the ambient-scene runtime the canon shares as `uix.scene`. | +| `defineEngineSound(options)` | `sound` | Engine only — the Web Audio runtime extracted from sema's `SoundChannel`. | > This table is checked against the directory: `docs:check` (`I1-catalog`) > fails when `service-factories/` grows a slot this list does not mention. It diff --git a/docs/architecture/active-uix.md b/docs/architecture/active-uix.md index 51d2fb147..73ba6305b 100644 --- a/docs/architecture/active-uix.md +++ b/docs/architecture/active-uix.md @@ -55,14 +55,14 @@ services they need — without components ever knowing `ActiveApp` directly. > Executable source: [`src/uix/contracts.ts`](../../src/uix/contracts.ts). -| Module | Minimum required | Optional | Fallback when missing | Error when missing | -| --- | --- | --- | --- | --- | -| `ActiveUix` standalone | `langs` config | `clipboard:false`, `format`, `events`, `portal`, `dom:false` | creates `prefs`, core services and, with `dom:false`, a local `disabledDom` | missing `langs` config | -| `ActiveUix` attach | `ActiveApp` core + `langs`, `dom` services | `app.clipboard`, `app.format`, event engine, `portal` | none for required services | missing `langs` or `dom` on the app; the getter of an absent optional service fails explicitly | -| `SomaRuntime` | `dom` from `ActiveUix` | event engine, `langs`, `format` | none of its own | nonexistent morfo/event/part | -| `Sema` direct | `dom` or `projector` when `visual` is active | `sound`, `haptic`, `visual:false` | none of its own for UIX services | `SemaConfigError` without `dom/projector` while visual is active | -| `Eidos` | `dom` when `applyDom` | `langs`, `format`, `prefs`, mode/density sources | `applyDom:false` allows render/serialize without DOM | missing `dom` with `applyDom` active | -| `ADom` direct | caller's target/window/document | breakpoints/window | `disabledDom` only when the caller asks for it | ADom's own errors without a real DOM | +| Module | Minimum required | Optional | Fallback when missing | Error when missing | +| ---------------------- | -------------------------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| `ActiveUix` standalone | `langs` config | `clipboard:false`, `format`, `events`, `portal`, `dom:false` | creates `prefs`, core services and, with `dom:false`, a local `disabledDom` | missing `langs` config | +| `ActiveUix` attach | `ActiveApp` core + `langs`, `dom` services | `app.clipboard`, `app.format`, event engine, `portal` | none for required services | missing `langs` or `dom` on the app; the getter of an absent optional service fails explicitly | +| `SomaRuntime` | `dom` from `ActiveUix` | event engine, `langs`, `format` | none of its own | nonexistent morfo/event/part | +| `Sema` direct | `dom` or `projector` when `visual` is active | `sound`, `haptic`, `visual:false` | none of its own for UIX services | `SemaConfigError` without `dom/projector` while visual is active | +| `Eidos` | `dom` when `applyDom` | `langs`, `format`, `prefs`, mode/density sources | `applyDom:false` allows render/serialize without DOM | missing `dom` with `applyDom` active | +| `ADom` direct | caller's target/window/document | breakpoints/window | `disabledDom` only when the caller asks for it | ADom's own errors without a real DOM | ## Ownership and degradation rules diff --git a/docs/architecture/eidos.md b/docs/architecture/eidos.md index a7741bc09..6502dfbc6 100644 --- a/docs/architecture/eidos.md +++ b/docs/architecture/eidos.md @@ -232,7 +232,7 @@ Radix Themes, Ark/Panda, Chakra, Tailwind or shadcn as foundation tokens: `--border-style`, `--border` + `--ring-inset-width`. - `opacity`: a dual scale — numeric plus semantic `ghost · disabled · scrim · muted · overlay · subtle · press · hover · - full` (changelog §29). +full` (changelog §29). - `zIndex`: `base · raised · sticky · dropdown · popover · tooltip · modal · toast`. - `shadow`: a physical `1..6` scale plus per-theme semantic aliases `none · subtle · raised · overlay`. @@ -496,15 +496,15 @@ components keep working headless because behavior belongs to Soma. ### From morfo (declaration) -| Piece | Eidos uses it for | -| -------------------------------------------------------------- | ----------------------------------------------------------- | -| `parts[].kebab` | `[data-{component}-{kebab}]` selectors | -| `parts[].archetype` | transversal rules `[data-archetype=trigger]` | -| `parts[].states` + `data[].values` | variants `[data-state=open]` | -| `parts[].data` with `data-starting-style` / `data-ending-style` | enter/exit animation hooks | -| `events[].name` | selectors `[data-event=emerge-dismiss]`, `[data-event^=commit]` | -| `events[].semantic.family` + `.intent` | semantic tinting of transitions | -| `events[].prewrite[]` (e.g. `data-last-action`) | tinting the exit anim by cause | +| Piece | Eidos uses it for | +| --------------------------------------------------------------- | --------------------------------------------------------------- | +| `parts[].kebab` | `[data-{component}-{kebab}]` selectors | +| `parts[].archetype` | transversal rules `[data-archetype=trigger]` | +| `parts[].states` + `data[].values` | variants `[data-state=open]` | +| `parts[].data` with `data-starting-style` / `data-ending-style` | enter/exit animation hooks | +| `events[].name` | selectors `[data-event=emerge-dismiss]`, `[data-event^=commit]` | +| `events[].semantic.family` + `.intent` | semantic tinting of transitions | +| `events[].prewrite[]` (e.g. `data-last-action`) | tinting the exit anim by cause | ### From Soma @@ -568,7 +568,7 @@ The third rule is where the layer split earns its keep. `shift` **sounds** like a slide (`SEMA_MAP.families.shift.sounds.default = 'slide'`) and until 2026-08-11 it did not slide, because sema owns no motion and eidos had written FAMILY-keyed signatures for `contact`, `commit` and `delegate` only (`emerge` -and `signal` are covered, but keyed by EVENT name) — the *shift invisible* +and `signal` are covered, but keyed by EVENT name) — the _shift invisible_ antipattern (book ch. 34 §14) living inside the framework that names it. The missing half was always eidos's to write: sema stamps the SENSE (`data-event-direction`, decided per emit, `forward` | `backward`) and eidos @@ -585,8 +585,7 @@ routinely needs to paint something the stamp never touches. That is a ```css /* Splitter commits on the provider; the handle is what pulses. */ -[data-splitter][data-event='commit-set'][data-event-phase='active'] - [data-splitter-resize-trigger] { +[data-splitter][data-event='commit-set'][data-event-phase='active'] [data-splitter-resize-trigger] { background: var(--splitter-active-handle-bg, var(--color-primary-solid)); } ``` @@ -657,34 +656,34 @@ the R-4.x rules of `scripts/component-audit.ts`. Quick map of what THEMING.md covers, to avoid duplicating here: -| Topic | Section in THEMING.md | -| --- | --- | -| Why theming lives in Eidos and not in Morfo | §1.bis | -| 7 token layers and override points | §3 | -| The 9 canonical color roles | §4 | -| The canonical sizes | §5 | -| Naming conventions | §6 | -| Token Scope Contract (TSC): type, scope algebra, cross-axis | §7 | -| TSC v2.2: multi-part scope (`parts: [...]`) + cross-recipe composition | §7 | -| How to add a new component | §8 | -| How to define a theme | §9 | -| How to override tokens at runtime | §10 | -| Bundle strategy + `eidos:purge` | §11 | -| Sema integration via `event:*` (superseded — see the stub) | §13 | -| Anti-patterns and FAQ | §16, §17 | -| Universal TSC coverage (no exceptions) | §18 | -| Variants are eidos canon, NOT the theme's (with `EIDOS_VARIANTS`) | §19 | -| Engine corrections: live density + on-solid `contrast` | §20 | -| The `scaling` axis (global zoom), separate from density | §23 | -| P2 corrections: on-solid text by luminance + translucent surfaces | §24 | -| Color model: palette (33 scales) + roles (aliases) + intents (auto-derived) | §25 | -| Runtime theme builder: `eidos.applyColorScheme(seed)` (`uix.color` engine) | §26 | -| Wide-gamut OKLCH output, default-on (hex fallback + `oklch()` sibling) | §27 | -| a11y forced-colors (focus outline fallback) + border ramp (slot 6→7) | §28 | -| Scale canon — the theming audit: blur · inset-shadow/ring · gradients · breakpoints+container · opacity · border-width · tracking | §35 | -| Focus ring: two parameterized rings (fields, box-shadow) + `outline` on surfaces | §32 | -| Touch-target — 44px on touch, gated by `pointer: coarse` | §37 | -| State layer `--state-*` — unified neutral feedback (MD3, theme-adaptive) | §38 | +| Topic | Section in THEMING.md | +| --------------------------------------------------------------------------------------------------------------------------------- | --------------------- | +| Why theming lives in Eidos and not in Morfo | §1.bis | +| 7 token layers and override points | §3 | +| The 9 canonical color roles | §4 | +| The canonical sizes | §5 | +| Naming conventions | §6 | +| Token Scope Contract (TSC): type, scope algebra, cross-axis | §7 | +| TSC v2.2: multi-part scope (`parts: [...]`) + cross-recipe composition | §7 | +| How to add a new component | §8 | +| How to define a theme | §9 | +| How to override tokens at runtime | §10 | +| Bundle strategy + `eidos:purge` | §11 | +| Sema integration via `event:*` (superseded — see the stub) | §13 | +| Anti-patterns and FAQ | §16, §17 | +| Universal TSC coverage (no exceptions) | §18 | +| Variants are eidos canon, NOT the theme's (with `EIDOS_VARIANTS`) | §19 | +| Engine corrections: live density + on-solid `contrast` | §20 | +| The `scaling` axis (global zoom), separate from density | §23 | +| P2 corrections: on-solid text by luminance + translucent surfaces | §24 | +| Color model: palette (33 scales) + roles (aliases) + intents (auto-derived) | §25 | +| Runtime theme builder: `eidos.applyColorScheme(seed)` (`uix.color` engine) | §26 | +| Wide-gamut OKLCH output, default-on (hex fallback + `oklch()` sibling) | §27 | +| a11y forced-colors (focus outline fallback) + border ramp (slot 6→7) | §28 | +| Scale canon — the theming audit: blur · inset-shadow/ring · gradients · breakpoints+container · opacity · border-width · tracking | §35 | +| Focus ring: two parameterized rings (fields, box-shadow) + `outline` on surfaces | §32 | +| Touch-target — 44px on touch, gated by `pointer: coarse` | §37 | +| State layer `--state-*` — unified neutral feedback (MD3, theme-adaptive) | §38 | What follows in this chapter are the operational decisions of the **visual layer as a module** (typography sourcing, picker patterns, API conventions, @@ -715,10 +714,10 @@ src/uix/eidos/lib/primitives/typography.ts font-size,line-height,letter-spacing,font-weight,color} ``` -| Layer | Who consumes it | For what | -|---|---|---| -| **Numerical foundation** (`--font-size-*`, `--font-family-primary`, …) | recipe tokens in `lib/recipes/base.ts` (+ the semantic leading `--leading-ui`, config data) | Component internals (Field labels, Combobox triggers, Button text, …) — they need **t-shirt scaling** (`xs/sm/md/lg/xl`) that does NOT map cleanly to a fixed semantic. | -| **Named styles** (`--style-label-*`, `--style-body-*`, `--style-caption-*`, `--style-h{1..6}-*`, `--style-{hero,prose,code}-*`) | typography primitives (``, ``, ``, ``, ``, …) | The user-facing API to compose content — the USER picked "label" or "body" and wants that semantic role honored. | +| Layer | Who consumes it | For what | +| ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Numerical foundation** (`--font-size-*`, `--font-family-primary`, …) | recipe tokens in `lib/recipes/base.ts` (+ the semantic leading `--leading-ui`, config data) | Component internals (Field labels, Combobox triggers, Button text, …) — they need **t-shirt scaling** (`xs/sm/md/lg/xl`) that does NOT map cleanly to a fixed semantic. | +| **Named styles** (`--style-label-*`, `--style-body-*`, `--style-caption-*`, `--style-h{1..6}-*`, `--style-{hero,prose,code}-*`) | typography primitives (``, ``, ``, ``, ``, …) | The user-facing API to compose content — the USER picked "label" or "body" and wants that semantic role honored. | **The two layers are not redundant**: they serve different contexts. The numerical one vertebrates the system's _interior_; the semantic one @@ -738,25 +737,25 @@ authored value references the style.) ```css /* generated/base.css (via render-css.ts) */ :root { - /* Named style — source of truth */ - --style-label-font-family: var(--font-family-primary); - --style-label-line-height: 1.25; + /* Named style — source of truth */ + --style-label-font-family: var(--font-family-primary); + --style-label-line-height: 1.25; - /* Semantic leading — config data anchored to the style */ - --leading-ui: var(--style-label-line-height, 1.25); + /* Semantic leading — config data anchored to the style */ + --leading-ui: var(--style-label-line-height, 1.25); } /* recipes/base.ts → generated/base.css */ :root { - --accordion-trigger-font-family: var(--style-label-font-family); - --field-label-line-height: var(--leading-ui); - --field-control-line-height: var(--leading-ui); - /* … dozens more recipe tokens */ + --accordion-trigger-font-family: var(--style-label-font-family); + --field-label-line-height: var(--leading-ui); + --field-control-line-height: var(--leading-ui); + /* … dozens more recipe tokens */ } /* components/field/field.css */ [data-field-label] { - line-height: var(--field-label-line-height); + line-height: var(--field-label-line-height); } ``` @@ -1007,15 +1006,15 @@ the morfo declares the component's whole BEHAVIORAL surface, not just its paintable one — but it isn't noise either. The criterion: - **Legitimate without a consumer** (the majority): - - *behavioral / a11y attrs* — state mirrors that exist for JS, tests, + - _behavioral / a11y attrs_ — state mirrors that exist for JS, tests, assistive tech or app-land selectors (`data-state` on parts the recipe styles via a parent, `aria-*` reflections); - - *composition artifacts* — a component whose visual lives in a SHARED + - _composition artifacts_ — a component whose visual lives in a SHARED layer or in its composed children shows its own contract as "unused" (css-field's 21 live in `spin-field.css`; collapsible is headless by design and its consumers style it; picker roots restyle the composed field/calendar contracts instead); - - *cross-component selectors* — entries like + - _cross-component selectors_ — entries like `[data-popover-content] [data-year-grid]` are consumed from the SIBLING's recipe, which the per-component report can't see. - **Debt** (the minority worth burning): an attr that was declared FOR a diff --git a/docs/architecture/packs.md b/docs/architecture/packs.md index 8d1ad172c..d8f0de00e 100644 --- a/docs/architecture/packs.md +++ b/docs/architecture/packs.md @@ -50,14 +50,14 @@ canon** through the full route — never to grow the pack's privileges. A pack is exempt from the acceptance matrix, NOT from discipline. Every pack ships with these six obligations, guarded mechanically (`packs-check`): -| P | Obligation | -| --- | --- | -| P-1 | Every animated piece declares a `reduce` policy (`'static-frame'` \| `'hide'`) — no silent default; reduced motion is honored via the injected DOM port, never a raw `matchMedia`. | -| P-2 | Full teardown: every mount returns a handle whose `dispose()` releases frames, observers, GL resources and listeners. | -| P-3 | DOM only through the injected port (`dom.requestFrame` / `observeResize` / `observeIntersection` / `listen`) — raw `requestAnimationFrame` / `ResizeObserver` / `IntersectionObserver` / `addEventListener` in `src/packs/` is an error. | +| P | Obligation | +| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| P-1 | Every animated piece declares a `reduce` policy (`'static-frame'` \| `'hide'`) — no silent default; reduced motion is honored via the injected DOM port, never a raw `matchMedia`. | +| P-2 | Full teardown: every mount returns a handle whose `dispose()` releases frames, observers, GL resources and listeners. | +| P-3 | DOM only through the injected port (`dom.requestFrame` / `observeResize` / `observeIntersection` / `listen`) — raw `requestAnimationFrame` / `ResizeObserver` / `IntersectionObserver` / `addEventListener` in `src/packs/` is an error. | | P-4 | Color parameters are token-aware: they accept an eidos custom-property name (resolved via `eidos.resolveToken`, re-resolved on mode change) or a raw CSS color as fallback; hardcoded palettes without a token path are an error for theme-facing params. | -| P-5 | Decorative DOM is `aria-hidden="true"`; pointer interactivity is opt-in per piece and never traps focus. | -| P-6 | Off-screen and hidden-tab work is paused; a scene budget guards against unbounded concurrent GL contexts (warn via logger). | +| P-5 | Decorative DOM is `aria-hidden="true"`; pointer interactivity is opt-in per piece and never traps focus. | +| P-6 | Off-screen and hidden-tab work is paused; a scene budget guards against unbounded concurrent GL contexts (warn via logger). | ## The promotion path (the agentic direction) diff --git a/docs/audit/components/_cierre.md b/docs/audit/components/_cierre.md index 4867cf236..90ac573ef 100644 --- a/docs/audit/components/_cierre.md +++ b/docs/audit/components/_cierre.md @@ -6,32 +6,32 @@ Ejecución de fixes: 2026-07-08 → 2026-07-11. Rama `alpha-0.1-sec-dom`. ## Resultado final (gates del 2026-07-11) -| Puerta | Estado | -| --- | --- | -| Matriz de aceptación (`component-audit.ts`) | **134 PASS · 0 NEEDS-WORK · 0 BROKEN** (de 81 PASS al arrancar la ejecución) | -| Guards de capa (`contracts.test.ts`) | **37/37 verde** (primera vez; partía con 13 fallos) | -| Vocabulario morfo (`morfo-vocabulary-check`) | exit 0 (1 WARN conocido: `cropper.commit-crop`, descubribilidad) | -| Baseline de tipos (fuera de `alpha\|words\|palabras\|chronos`) | **57** — invariante durante todo el programa (59→57 en P7) | -| eidos-lint (catálogo) | 0 selectores inválidos · 0 class-hooks en lo tocado | +| Puerta | Estado | +| -------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| Matriz de aceptación (`component-audit.ts`) | **134 PASS · 0 NEEDS-WORK · 0 BROKEN** (de 81 PASS al arrancar la ejecución) | +| Guards de capa (`contracts.test.ts`) | **37/37 verde** (primera vez; partía con 13 fallos) | +| Vocabulario morfo (`morfo-vocabulary-check`) | exit 0 (1 WARN conocido: `cropper.commit-crop`, descubribilidad) | +| Baseline de tipos (fuera de `alpha\|words\|palabras\|chronos`) | **57** — invariante durante todo el programa (59→57 en P7) | +| eidos-lint (catálogo) | 0 selectores inválidos · 0 class-hooks en lo tocado | ## Recorrido por paquetes (hashes) -| Paquete | Contenido | Commits | -| --- | --- | --- | -| P0–P3 | Doctrina · guardas · contratos morfo · 13 packs | `53b6f629` (lote inicial) | -| P4 naming N1–N10 | `selectionMode` · `onValueCommit` · `deselectable` · delays · razones tipadas… | `53b6f629` | -| P5 sistema | S1 codemod class-hooks · S5 outline · depth · touch · **S6 Field composition** (16 fields + poda 199 tokens + de-alias) · S8 calendar-surface | `5eb92132..d1670caa` · `83e13be6` | -| P6 | C6 `getWeekInfo` · C7 D13 textarea+censo · B.11 splitter bindable | `3b02fd11` · `444e05eb` · `0f0feb4f` | -| P7 baseline + S7 | 2 errores mandados (59→57) · papeleo pasivos/capas | `c0ae3de1` · `fe844c62` | -| **S4 dossiers** (5 lotes + drag-drop) | 30 READMEs eidos a forma dossier + README soma field-langs → **E-2.3 = 0** | `7b82aa79` · `20e2e7a1` | -| One-liners + §37 | delegated/apg/archetype + docblocks · **§37 resuelto (a)-con-nota** | `dd0637f6` · `bf573430` | -| compile.test | Test stale post dialog-001 adjudicado + guard nuevo | `e8f12aa1` | -| **S9 iniciativas** | gradient-\* (graduación ACTIVE_DEV_TRACK) · ntp integral (válvula `literal:` R-2.1 + README soma + suite 12/12) · metrics+suite (puente interno + helper + suites 7/7) | `45ae3691` · `dd249eac` · `8e3a67bd` | -| Censo contracts | 12→3: knob barrel+README · puentes internos · 6 barrels→`internals.ts` · track-filter · data-archetype + 4 morfos field-trigger | `cb554ae2` | -| Demos fuera del fix | **Decisión de producto (2026-07-10): frame NUEVO para las demos del sistema** → reglas D-\* verdict-neutral | `e53bf0e8` | -| Convergencia runtime | field `commit-submit` al Enter (doctrina de familia) · float-panel `writeProperty`/`uix.timers` + kb-gestos con handle-events · A-3.7 a acciones distintas · puerto media-provider exento → **contracts 37/37** | `33f346ed` · `bdd17f04` | -| Censo R-1.x | badge al velo §38 (fix real) + 8 exceptions con evidencia (pin-input ya pintaba en `[data-active]`) | `ee9786bc` | -| Lote 6 final | Los 5 últimos dossiers + apg timeline + literales anotados → **134/0/0** | `7f2bcc28` | +| Paquete | Contenido | Commits | +| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | +| P0–P3 | Doctrina · guardas · contratos morfo · 13 packs | `53b6f629` (lote inicial) | +| P4 naming N1–N10 | `selectionMode` · `onValueCommit` · `deselectable` · delays · razones tipadas… | `53b6f629` | +| P5 sistema | S1 codemod class-hooks · S5 outline · depth · touch · **S6 Field composition** (16 fields + poda 199 tokens + de-alias) · S8 calendar-surface | `5eb92132..d1670caa` · `83e13be6` | +| P6 | C6 `getWeekInfo` · C7 D13 textarea+censo · B.11 splitter bindable | `3b02fd11` · `444e05eb` · `0f0feb4f` | +| P7 baseline + S7 | 2 errores mandados (59→57) · papeleo pasivos/capas | `c0ae3de1` · `fe844c62` | +| **S4 dossiers** (5 lotes + drag-drop) | 30 READMEs eidos a forma dossier + README soma field-langs → **E-2.3 = 0** | `7b82aa79` · `20e2e7a1` | +| One-liners + §37 | delegated/apg/archetype + docblocks · **§37 resuelto (a)-con-nota** | `dd0637f6` · `bf573430` | +| compile.test | Test stale post dialog-001 adjudicado + guard nuevo | `e8f12aa1` | +| **S9 iniciativas** | gradient-\* (graduación ACTIVE_DEV_TRACK) · ntp integral (válvula `literal:` R-2.1 + README soma + suite 12/12) · metrics+suite (puente interno + helper + suites 7/7) | `45ae3691` · `dd249eac` · `8e3a67bd` | +| Censo contracts | 12→3: knob barrel+README · puentes internos · 6 barrels→`internals.ts` · track-filter · data-archetype + 4 morfos field-trigger | `cb554ae2` | +| Demos fuera del fix | **Decisión de producto (2026-07-10): frame NUEVO para las demos del sistema** → reglas D-\* verdict-neutral | `e53bf0e8` | +| Convergencia runtime | field `commit-submit` al Enter (doctrina de familia) · float-panel `writeProperty`/`uix.timers` + kb-gestos con handle-events · A-3.7 a acciones distintas · puerto media-provider exento → **contracts 37/37** | `33f346ed` · `bdd17f04` | +| Censo R-1.x | badge al velo §38 (fix real) + 8 exceptions con evidencia (pin-input ya pintaba en `[data-active]`) | `ee9786bc` | +| Lote 6 final | Los 5 últimos dossiers + apg timeline + literales anotados → **134/0/0** | `7f2bcc28` | ## Método canonizado (reutilizable) diff --git a/docs/audit/components/_naming.md b/docs/audit/components/_naming.md index b7dcc5696..3b9e7aab2 100644 --- a/docs/audit/components/_naming.md +++ b/docs/audit/components/_naming.md @@ -8,81 +8,92 @@ evidencia y pide veredicto. ## A. Convenciones CONFIRMADAS catálogo-completo (canon de facto — solo falta escribirlo) -| Concepto | Patrón canónico | Evidencia | -|---|---|---| -| Valor + cambio (valued controls) | `value` (bindable) + `onValueChange: OnChangeFn` | fields ×14, radio-group, toggle-group, rating-group, select, combobox, listbox, calendar, pickers — el par dominante | -| Estado binario semántico | nombre ARIA-alineado, NO value: `checked`/`onCheckedChange` (checkbox :15-19, switch :21-23) · `pressed`/`onPressedChange` (toggle :20-22) · + `indeterminate`/`onIndeterminateChange` (checkbox :16-21) | Convención Radix/bits seguida a rajatabla — REGLA: binarios hablan su ARIA | -| Apertura de overlay | `open` (bindable) + `onOpenChange` + **`onOpenChangeComplete`** (post-animación) | popover :11-15, dialog :12-51, drawer :46-73, tooltip :34-38, float-panel :20-24, dropdown-menu :18-22 — ×6 EXACTO, incluida la variante Complete | -| Cierre/outside | `onInteractOutside: (e: PointerEvent)` + `onFocusOutside` + `dismissible` + `restoreScrollDelay` | dialog :97-106, drawer :62-92, dropdown-menu :61-63 | -| Flotante | `side` + `align` + `forceMount` + `modal` (+ `updatePositionStrategy` en menús) | popover/dropdown-menu/float-panel | -| Flags de estado plano | `disabled` / `readonly` / `required` / `invalid` (booleans planos, OR-merge Field/Form) | catálogo entero; `is*` SOLO en snippetProps derivados (isFocused/isHovering/isPlaying — rating :21, carousel :18) — separación limpia | -| Form-bridge | `name` (+ hidden input; `value` enviado condicionado al estado) | checkbox/switch/toggle :67-69, radio-group, date-field (ISO), knob/radio-group como PARTE morfo | -| Navegación de lista | `loop` + `orientation` + `dir: Direction` | radio-group :39-41, toggle-group, select :46, combobox :45, listbox :55 | -| Localización | `locale?` (fallback reactivo soma.langs) + `dir` + `hourCycle` | familia date/time | -| Tipado de callbacks | `OnChangeFn` para todo callback con dato | transversal (las excepciones, abajo B.2) | +| Concepto | Patrón canónico | Evidencia | +| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| Valor + cambio (valued controls) | `value` (bindable) + `onValueChange: OnChangeFn` | fields ×14, radio-group, toggle-group, rating-group, select, combobox, listbox, calendar, pickers — el par dominante | +| Estado binario semántico | nombre ARIA-alineado, NO value: `checked`/`onCheckedChange` (checkbox :15-19, switch :21-23) · `pressed`/`onPressedChange` (toggle :20-22) · + `indeterminate`/`onIndeterminateChange` (checkbox :16-21) | Convención Radix/bits seguida a rajatabla — REGLA: binarios hablan su ARIA | +| Apertura de overlay | `open` (bindable) + `onOpenChange` + **`onOpenChangeComplete`** (post-animación) | popover :11-15, dialog :12-51, drawer :46-73, tooltip :34-38, float-panel :20-24, dropdown-menu :18-22 — ×6 EXACTO, incluida la variante Complete | +| Cierre/outside | `onInteractOutside: (e: PointerEvent)` + `onFocusOutside` + `dismissible` + `restoreScrollDelay` | dialog :97-106, drawer :62-92, dropdown-menu :61-63 | +| Flotante | `side` + `align` + `forceMount` + `modal` (+ `updatePositionStrategy` en menús) | popover/dropdown-menu/float-panel | +| Flags de estado plano | `disabled` / `readonly` / `required` / `invalid` (booleans planos, OR-merge Field/Form) | catálogo entero; `is*` SOLO en snippetProps derivados (isFocused/isHovering/isPlaying — rating :21, carousel :18) — separación limpia | +| Form-bridge | `name` (+ hidden input; `value` enviado condicionado al estado) | checkbox/switch/toggle :67-69, radio-group, date-field (ISO), knob/radio-group como PARTE morfo | +| Navegación de lista | `loop` + `orientation` + `dir: Direction` | radio-group :39-41, toggle-group, select :46, combobox :45, listbox :55 | +| Localización | `locale?` (fallback reactivo soma.langs) + `dir` + `hourCycle` | familia date/time | +| Tipado de callbacks | `OnChangeFn` para todo callback con dato | transversal (las excepciones, abajo B.2) | ## B. DIVERGENCIAS reales (veredicto requerido — evidencia por celda) ### B.1 Multiplicidad de selección — TRES nombres para el mismo eje -| Nombre | Quién | -|---|---| -| `type: 'single' \| 'multiple'` | select :17, combobox :17 (+ calendar `type` single/multiple) | -| `selectionMode: 'single' \| 'multiple'` | listbox :6-51, tree-view :4-14 | -| `multiple: boolean` | file-upload :53 | + +| Nombre | Quién | +| --------------------------------------- | ------------------------------------------------------------ | +| `type: 'single' \| 'multiple'` | select :17, combobox :17 (+ calendar `type` single/multiple) | +| `selectionMode: 'single' \| 'multiple'` | listbox :6-51, tree-view :4-14 | +| `multiple: boolean` | file-upload :53 | **Propuesta**: un solo nombre (candidato `selectionMode` — no colisiona con el `type` nativo de button/input que ya significa otra cosa en el catálogo; `type` de select/combobox además choca con `PinInputType`/`data-type` que son OTRO eje). Decidir si `multiple: boolean` sobrevive como azúcar donde no hay modo-single-con-array. ### B.2 Callback terminal — CUATRO patrones (de la familia fields, confirmado) + (a) `onValueCommit: OnChangeFn` (mask, editable) · (b) `onSubmit(value)` crudo (search, password) · (c) solo evento sema (date/time/color) · (d) `onComplete` (pin-input). **Propuesta**: (a) como norma; (d) azúcar de completitud si se distingue patrón-lleno de commit. ### B.3 Valor primario: `value` genérico vs palabra de dominio -| Patrón | Quién | -|---|---| -| `value` genérico | fields, grupos, select/combobox/listbox, **carousel** (:83 — ¡para el índice de slide!) | + +| Patrón | Quién | +| ------------------ | ---------------------------------------------------------------------------------------- | +| `value` genérico | fields, grupos, select/combobox/listbox, **carousel** (:83 — ¡para el índice de slide!) | | Palabra de dominio | pagination `page`/`onPageChange` (:23-39) · file-upload `files`/`onFilesChange` (:38-40) | **Cuestión**: ¿regla "value salvo que el dominio tenga palabra universal" (page, files)? Entonces carousel debería decir `index`/`onIndexChange` (su snippet YA habla de `index` :8-32) — hoy es el híbrido incoherente. ### B.4 Doble eje seleccionado/expandido + tree-view: `selectedValue` + `expandedValue` (+ `onExpandedChange`) :9-26 — sufijo `-Value` para desambiguar ejes (estilo Ark). Coherente internamente; PERO convive con el `value` a secas del resto. **Propuesta**: canonizar el sufijo SOLO para componentes multi-eje (tree, chronos) y documentarlo. ### B.5 Deseleccionabilidad — polaridad invertida + `deselectable?: boolean` (toggle-group :4) vs `preventDeselect?: boolean` (calendar :112). El MISMO concepto, nombres opuestos. **Propuesta**: uno de los dos catálogo-completo (candidato `deselectable` — positivo, sin negación mental). ### B.6 Delays de hover — dos convenciones + popover `openDelay`/`closeDelay` (:31-33) vs tooltip `delayDuration`/`skipDelayDuration` (:18-20, nombres Radix). **Propuesta**: unificar (candidato openDelay/closeDelay — simétrico y describe el efecto; skipDelayDuration se re-nombra a algo del grupo, p.ej. `groupSkipDelay`). ### B.7 Prefijos de capacidad + `allowsCustomValue` (combobox :53, estilo React-Aria "allows") vs adjetivos planos (`deselectable`, `dismissible`) vs `allowHalf` (rating :63 — "allow" singular). TRES micro-convenciones. **Propuesta**: adjetivo plano cuando exista (`deselectable`); `allowX` solo si no hay adjetivo natural; nunca `allowsX`. ### B.8 `defaultValue` — adopción parcial + editable :16-21 Y select :28 (¡no era único!). El resto del catálogo confía en `$bindable(inicial)`. **Veredicto**: canonizar en valued-controls (¿quién más lo necesita?) o podar ambos. ### B.9 Validación — tres firmas (de fields, confirmado) + `validate` + `onInvalid` razones TIPADAS (color-field) · sin tipar (date/time y ranges) · `validate` boolean + `onValueInvalid` (tags-input). **Propuesta**: nivelar a razones tipadas; un solo nombre de callback. ### B.10 Async-en-vuelo + `isPending`/`data-pending` (form) vs `loading` (button, field enum). Puede ser distinción legítima (form en vuelo vs control ocupado) — decidir si se unifica o se documenta. ### B.11 Callbacks de gesto sin par de valor + splitter `onResize(sizes)`/`onResizeEnd(sizes)` (:16-18) — datos por callback sin `sizes` bindable visible. Verificar si hay par bindable (por-Panel); si no, es el único flujo one-way del catálogo → veredicto. ## C. Filas heredadas del pass de fields (siguen vivas) -| Concepto | Norma emergente | Divergentes | -|---|---|---| -| Segundo valor temporal | `placeholder` + `onPlaceholderChange` | — (date/time + ranges coherentes) | -| Callbacks por sub-valor | `onStartValueChange`/`onEndValueChange` junto a `onValueChange` | — (ranges coherentes entre sí) | -| Valor multi-contexto | `values: Record` + `LangSpec` + `required: K[]` | field-langs único | -| Doble valor colección+borrador | `value` + `inputValue` ambos bindables | tags-input; combobox :29 TAMBIÉN usa `inputValue` ✓ coherente ×2 | -| Eje conmutable | `X` + `onXChange` + `allowedXs` | color-field (format) | -| Debounce | `debounceMs` (sufijo-unidad) | search-field | -| Completitud | `data-complete`/`isComplete` | mask-field, pin-input | -| Visibilidad de contenido | `visible`/`onVisibilityChange` + `purpose` | password-field (concepto DISTINTO del `open` de overlays — legítimo, documentar la distinción) | -| Cardinalidad | `enabledSelections` + `whenFull` | toggle-group (dueño único ✓ por decisión 2026-06-27) | -| Render condicional | `onlyWhen{Required·Optional·Invalid}` | field ×3 | -| Identidad de item | `index` requerido + `value` (tags-input) | listbox/select items usan `value` sin índice — censo cerrado: el index-required es exclusivo de tags (¿necesario?) | -| Tamaño/variante (eidos) | `size` (ResponsiveProp) + `variant` + `rounded`/`shape`/`block` | catálogo coherente | +| Concepto | Norma emergente | Divergentes | +| ------------------------------ | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| Segundo valor temporal | `placeholder` + `onPlaceholderChange` | — (date/time + ranges coherentes) | +| Callbacks por sub-valor | `onStartValueChange`/`onEndValueChange` junto a `onValueChange` | — (ranges coherentes entre sí) | +| Valor multi-contexto | `values: Record` + `LangSpec` + `required: K[]` | field-langs único | +| Doble valor colección+borrador | `value` + `inputValue` ambos bindables | tags-input; combobox :29 TAMBIÉN usa `inputValue` ✓ coherente ×2 | +| Eje conmutable | `X` + `onXChange` + `allowedXs` | color-field (format) | +| Debounce | `debounceMs` (sufijo-unidad) | search-field | +| Completitud | `data-complete`/`isComplete` | mask-field, pin-input | +| Visibilidad de contenido | `visible`/`onVisibilityChange` + `purpose` | password-field (concepto DISTINTO del `open` de overlays — legítimo, documentar la distinción) | +| Cardinalidad | `enabledSelections` + `whenFull` | toggle-group (dueño único ✓ por decisión 2026-06-27) | +| Render condicional | `onlyWhen{Required·Optional·Invalid}` | field ×3 | +| Identidad de item | `index` requerido + `value` (tags-input) | listbox/select items usan `value` sin índice — censo cerrado: el index-required es exclusivo de tags (¿necesario?) | +| Tamaño/variante (eidos) | `size` (ResponsiveProp) + `variant` + `rounded`/`shape`/`block` | catálogo coherente | ## D. Reglas de API que el veredicto debe ratificar (el "style guide" de props) @@ -95,4 +106,4 @@ splitter `onResize(sizes)`/`onResizeEnd(sizes)` (:16-18) — datos por callback 7. Multi-eje: sufijo `-Value` (`selectedValue`/`expandedValue`) solo donde hay ≥2 ejes. 8. Unidades en el nombre cuando el número las tiene: `debounceMs`, `restoreScrollDelay` (ms implícito documentado) — decidir sufijo-unidad como norma. -*(Censo cerrado con el barrido de types 2026-07-07 — media-player provider y service components quedan para verificación dirigida en fixes; sus superficies no mostraron pares de valor divergentes.)* +_(Censo cerrado con el barrido de types 2026-07-07 — media-player provider y service components quedan para verificación dirigida en fixes; sus superficies no mostraron pares de valor divergentes.)_ diff --git a/docs/audit/components/_system.md b/docs/audit/components/_system.md index f38a219c6..cfad04679 100644 --- a/docs/audit/components/_system.md +++ b/docs/audit/components/_system.md @@ -5,18 +5,18 @@ consolidan des-duplicados para el **checkpoint único post-barrido** (los fixes — locales y de sistema — se ejecutan después de auditar todo el catálogo). Formato: origen (fichas) · hallazgo · propuesta. -| # | Origen | Hallazgo de sistema | Propuesta | -|---|---|---|---| -| SYS-1 | button | **Hooks de selector como clases** (`.eidos-button-*`) conviviendo con data-attrs — las clases escapan a eidos-lint; segunda gramática de hooks | Norma de catálogo: hooks SIEMPRE `data-{c}-{kebab}`; + guard (lint de clases con estilos en CSS de componentes). El barrido censa qué componentes lo hacen | -| SYS-2 | button | **Política de tests**: cobertura heterogénea (button 0 tests; date-field 8KB ✓) | Decisión: suites por componente en la fase de fixes vs batch de testing posterior. Cada ficha registra su cobertura para dimensionar | -| SYS-3 | button | **Criterio de pack sema**: ¿cuándo un componente merece pack vs default de familia? (button sin pack = default contact; 38 packs sesgados a fields/pickers) | Canonizar el criterio por escrito (CANON/sema doctrine): default-de-familia legítimo para contacto genérico; pack donde el patrón añade significado | -| SYS-4 | button · date-field | **Nivel canónico del README**: la máquina exige eidos (E-2.3); button solo soma; date-field ambos | Ratificar: eidos = puerta del consumidor (API visual + tokens), soma = contrato headless; documentar en component-guide | -| SYS-5 | date-field | **Doctrina §32 vs segment-fields**: "los campos quedan en box-shadow" pero date-field usa outline flush con razón sólida (flicker en increment + forced-colors). Afecta a date/time/color-field + ranges | Decisión doctrinal: matizar §32 (text-fields = dos anillos box-shadow · segment-fields = outline flush, razón canonizada) — recomendada; el barrido censa la familia completa | -| SYS-6 | date-field (≡ mandato B.1) | **Composición Field**: soma participa (merge) pero el DOM no envuelve `[data-field][data-size]`; label propio; alturas `height-{k}` duplicadas por componente | Ya mandatado en `next-features.md` — el barrido acumula la lista de piezas por componente | +| # | Origen | Hallazgo de sistema | Propuesta | +| ----- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| SYS-1 | button | **Hooks de selector como clases** (`.eidos-button-*`) conviviendo con data-attrs — las clases escapan a eidos-lint; segunda gramática de hooks | Norma de catálogo: hooks SIEMPRE `data-{c}-{kebab}`; + guard (lint de clases con estilos en CSS de componentes). El barrido censa qué componentes lo hacen | +| SYS-2 | button | **Política de tests**: cobertura heterogénea (button 0 tests; date-field 8KB ✓) | Decisión: suites por componente en la fase de fixes vs batch de testing posterior. Cada ficha registra su cobertura para dimensionar | +| SYS-3 | button | **Criterio de pack sema**: ¿cuándo un componente merece pack vs default de familia? (button sin pack = default contact; 38 packs sesgados a fields/pickers) | Canonizar el criterio por escrito (CANON/sema doctrine): default-de-familia legítimo para contacto genérico; pack donde el patrón añade significado | +| SYS-4 | button · date-field | **Nivel canónico del README**: la máquina exige eidos (E-2.3); button solo soma; date-field ambos | Ratificar: eidos = puerta del consumidor (API visual + tokens), soma = contrato headless; documentar en component-guide | +| SYS-5 | date-field | **Doctrina §32 vs segment-fields**: "los campos quedan en box-shadow" pero date-field usa outline flush con razón sólida (flicker en increment + forced-colors). Afecta a date/time/color-field + ranges | Decisión doctrinal: matizar §32 (text-fields = dos anillos box-shadow · segment-fields = outline flush, razón canonizada) — recomendada; el barrido censa la familia completa | +| SYS-6 | date-field (≡ mandato B.1) | **Composición Field**: soma participa (merge) pero el DOM no envuelve `[data-field][data-size]`; label propio; alturas `height-{k}` duplicadas por componente | Ya mandatado en `next-features.md` — el barrido acumula la lista de piezas por componente | | SYS-7 | spin-field · number/css-field · color-swatch | **Clasificación "capa compartida"/átomo pasivo sin escribir**: spin-field/list-surface/field-segment-state (capas) y color-swatch (átomo eidos-native sin `## Passive justification`) — vs la doctrina "morfo-first even leaf". Sin criterio formal ni doc de capa | Definir formalmente CAPA y ÁTOMO PASIVO (markers, README de capa, justificación formal, cómo los clasifica la máquina); documentar en component-guide | | SYS-8 | calendar · range-calendar · month/year-grid · drp/dp (≡ mandato calendar-surface) | **La familia calendar vive de préstamo informal** (79+65+65+38+4 tokens cross-component); tras B.2, las marcas holiday/event solo pintan en calendar; month/year-grid ni declaran el privado de acento | Ya mandatado (next-features): capa `calendar-surface` y/o arquetipo de celda-día; las fichas de la familia listan las piezas por componente | -| SYS-9 | gradient-picker · gradient-builder (+ntp) | **Patrón "iniciativa a medias"**: componentes embarcados sin cerrar el paquete de estándares (expediente pre-flight F-1.x, APG, tests) — gradient-builder es el caso más agudo | Fase de fixes: cerrar por PAQUETES de iniciativa (gradient-* junto; natural-time-picker integral) — y valorar guard de embarque ("no se embarca sin expediente") | +| SYS-9 | gradient-picker · gradient-builder (+ntp) | **Patrón "iniciativa a medias"**: componentes embarcados sin cerrar el paquete de estándares (expediente pre-flight F-1.x, APG, tests) — gradient-builder es el caso más agudo | Fase de fixes: cerrar por PAQUETES de iniciativa (gradient-\* junto; natural-time-picker integral) — y valorar guard de embarque ("no se embarca sin expediente") | | SYS-10 | field (re-audit) | **Enums de estado muertos**: el morfo de field declara `data-state` con enum de 5 valores × 3 partes y CERO consumidores CSS en TODO el catálogo (grep exhaustivo); loading no pinta nada. Contrato emitido que nadie honra = peso muerto y promesa falsa del contrato | Veredicto por enum: consumirlo (materializar loading/invalid vía el enum) o podarlo del morfo. El barrido censa más enums muertos en las familias restantes | | SYS-11 | date-range-field · time-range-field · pickers (re-audit) | **El canon de delegación EXISTE y los compuestos no lo usan uniformemente**: `SemaExpressionMode = 'pack'\|'family-default'\|'delegated'\|'none'` está documentado en `morfo/types.ts` (decisión de autor 2026-05-26; `'delegated'` = "expression lives in children's packs"). date-picker/date-range-picker LO DECLARAN (`expression: 'delegated'` + evento propio solo para el gesto que el hijo no posee: commit-reset) — la regla practicada es exactamente la correcta. Pero date-range-field/time-range-field (compuestos equivalentes) NO declaran expression alguna, y la máquina (A-3.1) no usa `'delegated'` como marcador de exoneración | (a) Fix de una línea en los field-ranges: `expression: 'delegated'`; (b) afinar A-3.1: interactivo + 0 eventos + sin `'delegated'` = gap (caza pin-input y natural-time-picker, exonera a los delegados); (c) párrafo en CANON/sema citando la decisión 2026-05-26 y el patrón date-picker como ejemplo | @@ -34,7 +34,7 @@ catálogo). Formato: origen (fichas) · hallazgo · propuesta. - ~~Demo (D-7.4 chip drift · D-1.2 plantilla · D-1.5/D-4.3/D-3.1 cableado)~~ — **FUERA DEL ALCANCE de esta auditoría** (corrección de alcance 2026-07-07: solo las capas del componente). Los datos siguen en tmp/component-audit.md como backlog de demos aparte; no computan en fichas ni en el checkpoint. - **APG ausente (A-1.4)**: natural-time-picker, gradient-picker, gradient-builder, card (variante interactiva), drag-drop, media-player, virtual-list, metrics → y **regla nueva necesaria**: cómo satisfacer "sin patrón APG oficial" (media-player, drag-drop) — ¿`apg: 'none — rationale'`? **Actualización 2026-08-25**: la cara gemela —el `apg` PRESENTE pero MENTIROSO— tiene ya su primer miembro resuelto (`tags-input` F-1: se implementó el layout grid que citaba, en vez de corregir la cita) y dos miembros vivos medidos: `search-field` F-1 (`apg: combobox` sin popup) y **`tag-group`**, que declara el mismo `apg: grid` que `tags-input` y renderiza `role=row` SIN ninguna celda (`morfo/components/tag-group.ts:64` `:137` `:163` — cero `gridcell` declarados) más un `Label` colgando del `grid` que no es `row`. La regla que falta es UNA para las dos caras: qué se hace cuando no hay patrón, y qué se hace cuando el declarado no describe lo implementado. - **Gramática de eventos (A-3.6/A-3.7)**: media-player ×4 nombres fuera de `{familia}-{verbo}` · float-panel 28 mutaciones/6 eventos (el mayor infra-declarado). -- **Interactivo con 0 eventos (A-3.1) — censo FINAL tras chequeos de delegación**: **pin-input = el único caso desnudo** (0 eventos + comentario :7-9 que promete un "commit-fill" imposible: sema ni está en scope — contradicción in-file). EXONERADOS: date-range-field/time-range-field (delegación real a los singles embebidos; les falta solo declararla — `expression: 'delegated'`) · natural-time-picker (header :4-19 documenta la composición entera; su hueco transaccional es deuda del Picker genérico) · **Picker genérico = tercera categoría: deuda DECLARADA** (:18-19 "candidate commit-* events for a later sema pass" — Accept/Cancel/Clear sin contrato, reconocido; UN fix ahí repara ntp + los 5 pickers de la migración deferValue). Afinar A-3.1: leer expression/scope/header antes de flaguear; añadir el Picker genérico al universo del script. +- **Interactivo con 0 eventos (A-3.1) — censo FINAL tras chequeos de delegación**: **pin-input = el único caso desnudo** (0 eventos + comentario :7-9 que promete un "commit-fill" imposible: sema ni está en scope — contradicción in-file). EXONERADOS: date-range-field/time-range-field (delegación real a los singles embebidos; les falta solo declararla — `expression: 'delegated'`) · natural-time-picker (header :4-19 documenta la composición entera; su hueco transaccional es deuda del Picker genérico) · **Picker genérico = tercera categoría: deuda DECLARADA** (:18-19 "candidate commit-\* events for a later sema pass" — Accept/Cancel/Clear sin contrato, reconocido; UN fix ahí repara ntp + los 5 pickers de la migración deferValue). Afinar A-3.1: leer expression/scope/header antes de flaguear; añadir el Picker genérico al universo del script. - **Colores crudos (R-2.1)**: natural-time-picker ×9 (cielos physically-fixed SIN anotación) · tipografía literal (R-2.7): field-langs, timeline ×2. - **Focus ausente (R-1.5)**: announce, clipboard. - **Expediente pre-flight (F-1.x)**: gradient-picker/builder, color-swatch, radio-cards, metrics, timeline, onion-menu, scroll-frames, trans/format-date/format-number/relative-time, float-panel (parcial) — **13**. @@ -113,7 +113,7 @@ va SUSTITUYENDO fichas familia a familia. s-text-virtual-list · accordion · collapsible. Correcciones: s-text y s-text-virtual-list "morfo —" FALSOS (4ª y 5ª falsedad de la clase — el pass superficial no abrió los morfos de los pasivos) · table/grid-list/ - listbox/virtual-*/feed DECLARAN family-default (no son huecos) · + listbox/virtual-\*/feed DECLARAN family-default (no son huecos) · **tree-view expand=EMERGE no shift** (corregí mi propio borrador) · unselect=affirm sube a ×5 (grid-list :29, listbox :30) · accordion open/close vs collapsible expand/collapse = drift de verbos para el @@ -144,5 +144,5 @@ va SUSTITUYENDO fichas familia a familia. piloto + ~34 pasivos por barrido declarado + 4 WIP usuario (slider, tabs, number-field, css-field: re-visitar tras su pass) + 2 exclusiones. - **CHECKPOINT: RESUELTO 2026-07-07** — 29 veredictos (N1–N10 · S1–S11 · - C1–C8) ítem a ítem con el usuario → **[_veredictos.md](_veredictos.md)** + C1–C8) ítem a ítem con el usuario → **[\_veredictos.md](_veredictos.md)** es LA fuente. Los fixes se ejecutan por paquetes sobre esas decisiones. diff --git a/docs/audit/components/_veredictos.md b/docs/audit/components/_veredictos.md index 89867f0a8..04efa5efd 100644 --- a/docs/audit/components/_veredictos.md +++ b/docs/audit/components/_veredictos.md @@ -15,51 +15,51 @@ DESPUÉS, por paquetes, sobre estas decisiones. ## Bloque N — Naming / API -| # | Veredicto | -|---|---| -| N1 | **`selectionMode`** en los de selección (migran select, combobox, calendar, toggle-group; listbox/tree-view ya). file-upload conserva `multiple` como **espejo-nativo documentado** (clase accept/name) | -| N2 | **`onValueCommit: OnChangeFn` norma ÚNICA** de callback terminal. pin-input renombra `onComplete`; search/password migran su `onSubmit` crudo; el trío date/time/color AÑADE el callback junto al evento sema | -| N3 | Regla **"`value` salvo palabra de dominio universal"** — `page` (pagination) y `files` (file-upload) se quedan; carousel corrige `value`→`index`/`onIndexChange` | -| N4 | Sufijo **`{eje}Value` SOLO multi-eje** (tree-view queda: `selectedValue`/`expandedValue`); mono-eje = `value` a secas | -| N5 | **`deselectable`** catálogo-completo (calendar migra `preventDeselect`) | -| N6 | **`openDelay`/`closeDelay`** (tooltip migra `delayDuration`; `skipDelayDuration`→`groupSkipDelay`) | -| N7 | Prefijos de capacidad: **adjetivo plano > `allowX` > nunca `allowsX`** (combobox → `allowCustomValue`; `allowHalf` queda) | -| N8 | **`defaultValue` PODADO** de editable y select — `$bindable(inicial)` cubre el caso | -| N9 | Validación nivelada a color-field: **razón tipada + `onInvalid` único** (tags-input migra `validate`-boolean y `onValueInvalid`) | -| N10 | `pending` (derivado, form) vs `loading` (prop del consumidor) — **distinción documentada**; data-attrs quedan | +| # | Veredicto | +| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| N1 | **`selectionMode`** en los de selección (migran select, combobox, calendar, toggle-group; listbox/tree-view ya). file-upload conserva `multiple` como **espejo-nativo documentado** (clase accept/name) | +| N2 | **`onValueCommit: OnChangeFn` norma ÚNICA** de callback terminal. pin-input renombra `onComplete`; search/password migran su `onSubmit` crudo; el trío date/time/color AÑADE el callback junto al evento sema | +| N3 | Regla **"`value` salvo palabra de dominio universal"** — `page` (pagination) y `files` (file-upload) se quedan; carousel corrige `value`→`index`/`onIndexChange` | +| N4 | Sufijo **`{eje}Value` SOLO multi-eje** (tree-view queda: `selectedValue`/`expandedValue`); mono-eje = `value` a secas | +| N5 | **`deselectable`** catálogo-completo (calendar migra `preventDeselect`) | +| N6 | **`openDelay`/`closeDelay`** (tooltip migra `delayDuration`; `skipDelayDuration`→`groupSkipDelay`) | +| N7 | Prefijos de capacidad: **adjetivo plano > `allowX` > nunca `allowsX`** (combobox → `allowCustomValue`; `allowHalf` queda) | +| N8 | **`defaultValue` PODADO** de editable y select — `$bindable(inicial)` cubre el caso | +| N9 | Validación nivelada a color-field: **razón tipada + `onInvalid` único** (tags-input migra `validate`-boolean y `onValueInvalid`) | +| N10 | `pending` (derivado, form) vs `loading` (prop del consumidor) — **distinción documentada**; data-attrs quedan | -Las reglas D1–D8 de [_naming.md](_naming.md) quedan ratificadas en consecuencia. +Las reglas D1–D8 de [\_naming.md](_naming.md) quedan ratificadas en consecuencia. B.11 (splitter sin par bindable) = verificación dirigida, no veredicto. ## Bloque S — Sistema -| # | Veredicto | -|---|---| -| S1 | Hooks SIEMPRE `data-{c}-{kebab}` + **guard anti-`.eidos-*`** + codemod de los 22 (cropper 8, button 6, image-picker 6, toggle 1, image-adjustments 1) | -| S2 | Tests **DENTRO del paquete de fixes** de cada componente (goal-driven) | -| S3a | Criterio de pack **canonizado en la doctrina sema**: 3 categorías (pack / default-documentado / delegated) con citas + regla del "yet" (deuda declarada lleva plazo) | +| # | Veredicto | +| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| S1 | Hooks SIEMPRE `data-{c}-{kebab}` + **guard anti-`.eidos-*`** + codemod de los 22 (cropper 8, button 6, image-picker 6, toggle 1, image-adjustments 1) | +| S2 | Tests **DENTRO del paquete de fixes** de cada componente (goal-driven) | +| S3a | Criterio de pack **canonizado en la doctrina sema**: 3 categorías (pack / default-documentado / delegated) con citas + regla del "yet" (deuda declarada lleva plazo) | | S3b | **Ascensos a pack: LOS 10** (veredicto del usuario, más allá de la recomendación 1–7): time-field+color-field (1º), range-calendar, Picker genérico (eventos+carácter), clipboard, drag-drop, listbox, media-player (tras renombrar), onion-menu/menu-dial, month-grid/year-grid, table | -| S4 | README dos niveles ratificado: **eidos = puerta del consumidor · soma = contrato headless** (date-field plantilla) + component-guide + pass masivo (28 eidos + soma field-langs + doc capa spin-field) | -| S5 | **CANONIZAR OUTLINE** como el modelo de focus (tras comparativa: Radix Themes/MD3/Chakra v3/RAC = outline; box-shadow = generación Tailwind/Bootstrap). §32 se reescribe con la razón (forced-colors + flicker de segmentos) + nota fechada; el remanente box-shadow de field.css migra | -| S6 | Mandato **Field composition CONFIRMADO**; R-1.3/R-1.4 (readonly/invalid ×9+2) se resuelven DENTRO como tratamiento único; 6 fields matan alturas duplicadas; `--field-segment-height` aterriza | -| S7 | **CAPA y ÁTOMO PASIVO definidos formalmente** (component-guide + markers para la máquina); color-swatch y radio-cards citas canónicas | -| S8 | **calendar-surface CONFIRMADO + tokens de rango como API PÚBLICA** de la capa (drp deja de tunear `--_calendar-range-*` ajenos) | -| S9 | Cierre por **paquetes de iniciativa** (gradient-* junto; ntp integral; metrics+suite) + **guard de embarque** F-1.x bloqueante SOLO para componentes nuevos | -| S10 | **PODAR el `data-state` muerto** de field (los flags son la representación — anti-alias) + **declarar enums donde hay unión viva** (calendar `data-type` + los que salgan) | -| S11 | Delegación, LAS 4: (a) `expression: 'delegated'` en los 2 field-ranges · (b) A-3.1 afinada · (c) párrafo en CANON con la decisión 2026-05-26 + cita radio-cards · (d) **guard coherencia pack↔expression** (FAIL pack∧≠'pack'; WARN pack∧sin-expression) | +| S4 | README dos niveles ratificado: **eidos = puerta del consumidor · soma = contrato headless** (date-field plantilla) + component-guide + pass masivo (28 eidos + soma field-langs + doc capa spin-field) | +| S5 | **CANONIZAR OUTLINE** como el modelo de focus (tras comparativa: Radix Themes/MD3/Chakra v3/RAC = outline; box-shadow = generación Tailwind/Bootstrap). §32 se reescribe con la razón (forced-colors + flicker de segmentos) + nota fechada; el remanente box-shadow de field.css migra | +| S6 | Mandato **Field composition CONFIRMADO**; R-1.3/R-1.4 (readonly/invalid ×9+2) se resuelven DENTRO como tratamiento único; 6 fields matan alturas duplicadas; `--field-segment-height` aterriza | +| S7 | **CAPA y ÁTOMO PASIVO definidos formalmente** (component-guide + markers para la máquina); color-swatch y radio-cards citas canónicas | +| S8 | **calendar-surface CONFIRMADO + tokens de rango como API PÚBLICA** de la capa (drp deja de tunear `--_calendar-range-*` ajenos) | +| S9 | Cierre por **paquetes de iniciativa** (gradient-\* junto; ntp integral; metrics+suite) + **guard de embarque** F-1.x bloqueante SOLO para componentes nuevos | +| S10 | **PODAR el `data-state` muerto** de field (los flags son la representación — anti-alias) + **declarar enums donde hay unión viva** (calendar `data-type` + los que salgan) | +| S11 | Delegación, LAS 4: (a) `expression: 'delegated'` en los 2 field-ranges · (b) A-3.1 afinada · (c) párrafo en CANON con la decisión 2026-05-26 + cita radio-cards · (d) **guard coherencia pack↔expression** (FAIL pack∧≠'pack'; WARN pack∧sin-expression) | ## Bloque C — Censos doctrinales -| # | Veredicto | -|---|---| -| C1 | Matriz de intent del deshacer **CANONIZADA tal cual** (colección-unselect=affirm ×5 · binario-uncheck=neutral · reset/cancel=neutral) — solo doc | -| C2 | *(reformulado tras corrección del usuario: los 8 verbos emerge son conjunto CERRADO del cap. 26 — eventos DISTINTOS, no drift)* **Pass de verificación verbo-a-verbo contra el cap. 26** en fixes; cada overlay justifica su elección con cita; accordion (`open/close`) ↔ collapsible (`expand/collapse`) primer caso a adjudicar | -| C3 | Regla de asignación de intent en toggles binarios **ESCRITA**: marcar-hecho = direccional estático (checkbox ejemplar) · conmutar-modo = `fromProp` default neutral (switch ejemplar) | -| C4 | **QUITAR `role='application'`** de los 4 (calendar/range-calendar/month-grid/year-grid) — línea bits/Radix/M3; verificar con lector de pantalla tras el cambio | -| C5 | Campo `apg` admite **`'none — {rationale}'`** + A-1.4 exige URL o none-con-razón (ausente = flag SIEMPRE) + pass de declaración | -| C6 | **`getWeekInfo(locale)` en `$libs/days` AHORA** (fallback sáb/dom documentado); calendar deriva `weekend` + verificar `firstDay` en el mismo fix | -| C7 | D13: fix de textarea **vía el `measure` del ActiveDom DEL CONTEXTO** (precisión del usuario: nunca DOM directo ni import de `$libs/dom` — solo la instancia inyectada) + censo dirigido (scroll-area prioritario, cropper, splitter, float-panel, virtual-*, carousel) + sonda reflowDetector | -| C8 | Afinar las 4 reglas del script (A-3.1, A-1.4, A-3.6 nombre↔verbo, universo completo con Picker) + **los escapados como fixtures de regresión** del propio script | +| # | Veredicto | +| --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| C1 | Matriz de intent del deshacer **CANONIZADA tal cual** (colección-unselect=affirm ×5 · binario-uncheck=neutral · reset/cancel=neutral) — solo doc | +| C2 | _(reformulado tras corrección del usuario: los 8 verbos emerge son conjunto CERRADO del cap. 26 — eventos DISTINTOS, no drift)_ **Pass de verificación verbo-a-verbo contra el cap. 26** en fixes; cada overlay justifica su elección con cita; accordion (`open/close`) ↔ collapsible (`expand/collapse`) primer caso a adjudicar | +| C3 | Regla de asignación de intent en toggles binarios **ESCRITA**: marcar-hecho = direccional estático (checkbox ejemplar) · conmutar-modo = `fromProp` default neutral (switch ejemplar) | +| C4 | **QUITAR `role='application'`** de los 4 (calendar/range-calendar/month-grid/year-grid) — línea bits/Radix/M3; verificar con lector de pantalla tras el cambio | +| C5 | Campo `apg` admite **`'none — {rationale}'`** + A-1.4 exige URL o none-con-razón (ausente = flag SIEMPRE) + pass de declaración | +| C6 | **`getWeekInfo(locale)` en `$libs/days` AHORA** (fallback sáb/dom documentado); calendar deriva `weekend` + verificar `firstDay` en el mismo fix | +| C7 | D13: fix de textarea **vía el `measure` del ActiveDom DEL CONTEXTO** (precisión del usuario: nunca DOM directo ni import de `$libs/dom` — solo la instancia inyectada) + censo dirigido (scroll-area prioritario, cropper, splitter, float-panel, virtual-\*, carousel) + sonda reflowDetector | +| C8 | Afinar las 4 reglas del script (A-3.1, A-1.4, A-3.6 nombre↔verbo, universo completo con Picker) + **los escapados como fixtures de regresión** del propio script | ## Fixes obvios (aprobados sin veto durante el checkpoint) diff --git a/docs/audit/components/accordion.md b/docs/audit/components/accordion.md index 3c66b8596..feaa8e825 100644 --- a/docs/audit/components/accordion.md +++ b/docs/audit/components/accordion.md @@ -7,10 +7,10 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ + dato censo | emerge **open/close** mientras su gemelo collapsible dice emerge **expand/collapse** (y tree-view expand/collapse) — el drift de verbos DENTRO de la familia emerge para el MISMO gesto disclosure: dato central del censo de verbos | -| resto | | ✓ | pack ✓; tests ✓; README ×2 ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 3 | Coherencia semántica | ✓ + dato censo | emerge **open/close** mientras su gemelo collapsible dice emerge **expand/collapse** (y tree-view expand/collapse) — el drift de verbos DENTRO de la familia emerge para el MISMO gesto disclosure: dato central del censo de verbos | +| resto | | ✓ | pack ✓; tests ✓; README ×2 ✓ | ## Hallazgos y propuestas @@ -22,4 +22,4 @@ Censo verbos emerge (accordion open/close vs collapsible expand/collapse — mis ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/alert-dialog.md b/docs/audit/components/alert-dialog.md index 8ccc3144f..d0db4cce5 100644 --- a/docs/audit/components/alert-dialog.md +++ b/docs/audit/components/alert-dialog.md @@ -6,12 +6,12 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ delegación documentada | 0 eventos CON razón in-place (:7-10): "Sema picks up Action/Cancel button events via the Dialog morfo's shared `commit-*`/`close-*` events — alert-dialog's morfo only declares the Action + Cancel button parts (Provider is virtual)"; `scope: ['soma','eidos']` sin sema — coherente (como ntp). Cuarto ejemplo del patrón delegación bien escrito | -| 4 | API | ✓ | Cancel = `Extract` (narrowing §19 ✓); el sitio canónico del `signal.warn + threat` PRE-acción (doctrina button cap. 22) | -| 8 | Contrato morfo | ✓ | APG alertdialog (:12); bloque focus propio (:19+) | -| resto | | ✓ | tests ✓; README ×2 ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ delegación documentada | 0 eventos CON razón in-place (:7-10): "Sema picks up Action/Cancel button events via the Dialog morfo's shared `commit-*`/`close-*` events — alert-dialog's morfo only declares the Action + Cancel button parts (Provider is virtual)"; `scope: ['soma','eidos']` sin sema — coherente (como ntp). Cuarto ejemplo del patrón delegación bien escrito | +| 4 | API | ✓ | Cancel = `Extract` (narrowing §19 ✓); el sitio canónico del `signal.warn + threat` PRE-acción (doctrina button cap. 22) | +| 8 | Contrato morfo | ✓ | APG alertdialog (:12); bloque focus propio (:19+) | +| resto | | ✓ | tests ✓; README ×2 ✓ | ## Hallazgos y propuestas @@ -23,4 +23,4 @@ SYS-11 (cuarta cita del patrón). ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/announce.md b/docs/audit/components/announce.md index d2bab2a0b..301b61a4e 100644 --- a/docs/audit/components/announce.md +++ b/docs/audit/components/announce.md @@ -6,18 +6,18 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ declarado | 4 eventos + family-default escrito (el pass superficial lo daba "pack —" a secas); las firmas announce-* retuneadas en A.4/A.5 le pertenecen ✓ | -| 9 | A11y | hallazgo | **F-1**: R-1.5 — si su close/action es interactivo necesita focus visible; si es puro aviso, documentarlo | -| 14 | Docs | gap | Sin README eidos | +| # | Dimensión | Estado | Evidencia | +| --- | -------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ declarado | 4 eventos + family-default escrito (el pass superficial lo daba "pack —" a secas); las firmas announce-\* retuneadas en A.4/A.5 le pertenecen ✓ | +| 9 | A11y | hallazgo | **F-1**: R-1.5 — si su close/action es interactivo necesita focus visible; si es puro aviso, documentarlo | +| 14 | Docs | gap | Sin README eidos | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ------------------------------- | ----------------------------------- | | **F-1** | Focus del trigger/close (R-1.5) | Regla del arquetipo o justificación | -| **F-2** | README eidos | SYS-4 | +| **F-2** | README eidos | SYS-4 | ## Escalan al sistema @@ -25,4 +25,4 @@ SYS-4 · censo R-1.5. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/aspect-ratio.md b/docs/audit/components/aspect-ratio.md index c346e6335..1bd351ef0 100644 --- a/docs/audit/components/aspect-ratio.md +++ b/docs/audit/components/aspect-ratio.md @@ -3,4 +3,4 @@ - **Fecha**: 2026-07-07 · **Familia**: layout · **Método**: solo análisis - **Capas**: morfo ✓ (scope eidos) · README eidos ✓ · consume `--aspect-ratio-*` ✓ · **Máquina: PASS limpio**. -— limpio. **Escalan**: —. · **Veredictos**: *(pendiente checkpoint post-barrido)* +— limpio. **Escalan**: —. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/auto-grid.md b/docs/audit/components/auto-grid.md index 33fa38128..04afb5f1c 100644 --- a/docs/audit/components/auto-grid.md +++ b/docs/audit/components/auto-grid.md @@ -3,4 +3,4 @@ - **Fecha**: 2026-07-07 · **Familia**: layout (grid auto-fill por min-width) · **Método**: solo análisis - **Capas**: morfo ✓ (scope eidos) · README eidos ✓ · **Máquina: PASS limpio**. -— limpio. **Escalan**: —. · **Veredictos**: *(pendiente checkpoint post-barrido)* +— limpio. **Escalan**: —. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/avatar.md b/docs/audit/components/avatar.md index bad2226fc..c53ab5348 100644 --- a/docs/audit/components/avatar.md +++ b/docs/audit/components/avatar.md @@ -3,4 +3,4 @@ - **Fecha**: 2026-07-07 · **Familia**: display · **Método**: solo análisis - **Capas**: morfo ✓ · README eidos ✓ · **Máquina: PASS limpio** · excepción tipográfica canónica ✓ (inicial ∝ diámetro, documentada en §5). -— limpio (fallback de iniciales, estados de carga). **Escalan**: —. · **Veredictos**: *(pendiente checkpoint post-barrido)* +— limpio (fallback de iniciales, estados de carga). **Escalan**: —. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/badge.md b/docs/audit/components/badge.md index 18ee6da47..40d59a18b 100644 --- a/docs/audit/components/badge.md +++ b/docs/audit/components/badge.md @@ -2,10 +2,10 @@ - **Fecha**: 2026-07-07 · **Familia**: display · **Método**: solo análisis - **Capas**: morfo ✓ (scope eidos probable) · soma — (pasivo) · README eidos **✗** · pack — · soft translúcido §24.2 ✓ · 1:1 tipográfico ✓ -- **Máquina**: **NEEDS-WORK** — `E-2.3` (*falta README eidos*). +- **Máquina**: **NEEDS-WORK** — `E-2.3` (_falta README eidos_). -| Hallazgo | Propuesta | -|---|---| -| F-1: README eidos | SYS-4 | +| Hallazgo | Propuesta | +| ----------------- | --------- | +| F-1: README eidos | SYS-4 | -**Escalan**: SYS-4. · **Veredictos**: *(pendiente checkpoint post-barrido)* +**Escalan**: SYS-4. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/banner.md b/docs/audit/components/banner.md index 0f91776e4..da2045458 100644 --- a/docs/audit/components/banner.md +++ b/docs/audit/components/banner.md @@ -8,18 +8,18 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 8 | Contrato morfo | ~~✓ ejemplar~~ → corregido (A-109) | era `
` con la cita de la spec; hoy `
` sin rol propio. `data-intent` → canal color-role estándar | -| 10 | Theming | hallazgo | **F-1**: la variante `overlay` flota (banda z) sin estampar depth — al censo | -| 14 | Docs | gap menor | Clasificación formal → SYS-7 | +| # | Dimensión | Estado | Evidencia | +| --- | -------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 8 | Contrato morfo | ~~✓ ejemplar~~ → corregido (A-109) | era `
` con la cita de la spec; hoy `
` sin rol propio. `data-intent` → canal color-role estándar | +| 10 | Theming | hallazgo | **F-1**: la variante `overlay` flota (banda z) sin estampar depth — al censo | +| 14 | Docs | gap menor | Clasificación formal → SYS-7 | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ------------------------------- | ----------------------------------------------------------------- | | **F-1** | overlay-banner sin `data-depth` | Pass conjunto de depth (tooltip/toast/float-panel/banner-overlay) | -| **F-2** | Clasificación formal | SYS-7 | +| **F-2** | Clasificación formal | SYS-7 | ## Escalan al sistema @@ -27,4 +27,4 @@ SYS-7 · censo depth. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/box.md b/docs/audit/components/box.md index b8e9b8759..1c7f7b9ee 100644 --- a/docs/audit/components/box.md +++ b/docs/audit/components/box.md @@ -3,4 +3,4 @@ - **Fecha**: 2026-07-07 · **Familia**: layout (primitiva base) · **Método**: solo análisis - **Capas**: morfo ✓ (scope eidos) · README eidos ✓ · **Máquina: PASS limpio** · `--box-max-width` compone con container ✓. -— limpio. **Escalan**: —. · **Veredictos**: *(pendiente checkpoint post-barrido)* +— limpio. **Escalan**: —. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/breadcrumb.md b/docs/audit/components/breadcrumb.md index df02838ba..5d4cfdd57 100644 --- a/docs/audit/components/breadcrumb.md +++ b/docs/audit/components/breadcrumb.md @@ -6,16 +6,16 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ consistente, sin la línea | 0 eventos coherente (nav landmark; sus items son Links con semántica nativa) pero SIN razón escrita — misma clase que link-preview F-2 (los ejemplares fab/banner/radio-cards la escriben) | -| 8 | Contrato morfo | ✓ | Nav landmark, current-page aria, apg ✓ | -| resto | | ✓ | tests ✓; README ×2 ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 3 | Coherencia semántica | ✓ consistente, sin la línea | 0 eventos coherente (nav landmark; sus items son Links con semántica nativa) pero SIN razón escrita — misma clase que link-preview F-2 (los ejemplares fab/banner/radio-cards la escriben) | +| 8 | Contrato morfo | ✓ | Nav landmark, current-page aria, apg ✓ | +| resto | | ✓ | tests ✓; README ×2 ✓ | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | **F-1** | Silencio sin razón escrita | Una línea en el morfo ("nav display: los items delegan en la semántica nativa de Link/anchor") — pass conjunto con link-preview | ## Escalan al sistema @@ -24,4 +24,4 @@ Censo "0-eventos sin línea de razón" (breadcrumb, link-preview — vs 6 ejempl ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/button-group.md b/docs/audit/components/button-group.md index cb06881ff..e2620598b 100644 --- a/docs/audit/components/button-group.md +++ b/docs/audit/components/button-group.md @@ -3,8 +3,8 @@ - **Fecha**: 2026-07-07 · **Familia**: acciones (iniciativa 06-22) · **Método**: solo análisis - **Capas**: morfo — (composición eidos: contexto de defaults para Buttons) · soma — · README eidos ✓ · pack — · **Máquina: PASS limpio** · el contexto que button consume ✓ (prop explícita gana). -| Hallazgo | Propuesta | -|---|---| -| F-1: composición sin clasificar | SYS-7 | +| Hallazgo | Propuesta | +| ------------------------------- | --------- | +| F-1: composición sin clasificar | SYS-7 | -**Escalan**: SYS-7. · **Veredictos**: *(pendiente checkpoint post-barrido)* +**Escalan**: SYS-7. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/button.md b/docs/audit/components/button.md index 1e75a015d..877cd71b8 100644 --- a/docs/audit/components/button.md +++ b/docs/audit/components/button.md @@ -4,51 +4,51 @@ - **Método**: solo análisis — los fixes se ejecutan tras auditar TODO el catálogo; esta ficha registra hallazgos + **propuestas de solución** (locales y las que escalan al sistema). - **Tier**: control interactivo · **Morfo scope**: `['soma','sema','eidos']` · **Familia**: acciones - **Capas**: [morfo](../../../src/uix/morfo/components/button.ts) · [soma](../../../src/uix/soma/components/button/) · [eidos](../../../src/uix/eidos/components/button/) · sema: **sin pack** (cascada por defecto) -- **Máquina** (`component:audit --only button`): **NEEDS-WORK** — `E-2.3` (falta README a nivel eidos). R-rules (color/tipografía/recipe-contract): **limpias**. *(Reglas D-\* de demo excluidas — corrección de alcance 2026-07-07: la auditoría cubre solo las capas del componente.)* +- **Máquina** (`component:audit --only button`): **NEEDS-WORK** — `E-2.3` (falta README a nivel eidos). R-rules (color/tipografía/recipe-contract): **limpias**. _(Reglas D-\* de demo excluidas — corrección de alcance 2026-07-07: la auditoría cubre solo las capas del componente.)_ - **Vigencia re-auditoría**: ficha piloto — YA a profundidad de referencia (todas las capas leídas con evidencia); este retrofit solo extrae las demos del alcance. ## Tabla de dimensiones -| # | Dimensión | Estado | Evidencia / nota | -|---|---|---|---| -| 1 | Composición vs declaración | ✓ | API compositiva; `child` (asChild) ✓; Spinner morfo-declarado con razón documentada (derivado de estado, no composición) | -| 2 | Naming de props | ✓ con hallazgo | Props canónicas (`disabled/loading/type/color/intent`); ver **S-1** (hooks CSS mixtos clase/data) | -| 3 | Coherencia semántica ecosistema/theming | ✓ ejemplar | `intent` = SOLO visual (cap. 22 §11 citado); contact sin intent; cascada intent-gana-sobre-color en provider; anti-patrón `contact+fulfill` documentado (cap. 22 §8) | -| 4 | API normalizada | ✓ | `OptsFromProps`; defaults documentados; `color` tipado laxo para paleta per THEMING §25.5 con re-type en eidos; `onPress` = feedback diagnóstico (no side-effect) | -| 5 | Tokens usados | ✓ | Piloto de la cascada `--button-palette-*`; recipe R-limpio; focus por `--focus-ring-*`; 0 usos de state-layer = **correcto** (todas las variantes son valenced — swap de paleta; sin tier neutro) | -| 6 | Parte semántica | validar | **F-6**: sin pack sema — `contact.activate` suena por el default de familia → ver S-3 | -| 7 | Animaciones | ✓ | Sin `@keyframes` perceptuales locales; press vía sistema (`--press-*`); spinner = periodo funcional | -| 8 | Contrato morfo | ✓ con hallazgo | Ejemplar: APG enlazado, keyboard declarado, textos localizados, `data-state` eliminado con la razón del clobbering (audit 05-27) documentada. **F-1**: Spinner con `archetype: 'item'` siendo parte display | -| 9 | Accesibilidad | ✓ | `aria-busy` en loading, `aria-disabled`, iconos `aria-hidden`, `iconOnly` conserva label sr-only, `loadingText` mantiene children sr-only, Enter/Space nativos, touch coarse por arquetipo trigger | -| 10 | Adopción theming | ✓ | Bundle de size vía recipe; depth n/a (no flota); focus modelo superficies (outline) ✓; palette cascade ✓ | -| 11 | Composición interna | ✓ | Participa en `Field.Provider` (disabled OR-merge); consume contexto `ButtonGroup` para defaults (prop explícita gana); es el COMPUESTO por IconButton/triggers | -| 12 | Estados y ciclo | ✓ | disabled/loading completos (click gated en ambos); `type` default 'button' (anti-submit implícito); controlled n/a (stateless) | -| 13 | Disciplina runtime | ✓ | Runtime-direct; sin DOM manual; sin timers; `$derived` limpios | -| 14 | ~~Demo testbed~~ | fuera de alcance | Demos excluidas de la auditoría (corrección 2026-07-07) — sus reglas D-\* viven en tmp/component-audit.md, no aquí | -| 15 | Docs + tests | **gap doble** | **F-4**: falta README eidos (el de soma completo con `## Sema events` ✓) · **F-7**: **CERO tests** de provider/wrapper · **F-2**: doc de `aria-label` en soma types referencia `iconOnly` (prop de eidos — fuga de capa) · **F-3**: docblock del wrapper cita `data-state` que el morfo eliminó | -| 16 | i18n | ✓ | `texts` del morfo localizados (`#?components.button.*`); `loadingText` del consumidor; sin strings hardcoded | +| # | Dimensión | Estado | Evidencia / nota | +| --- | --------------------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Composición vs declaración | ✓ | API compositiva; `child` (asChild) ✓; Spinner morfo-declarado con razón documentada (derivado de estado, no composición) | +| 2 | Naming de props | ✓ con hallazgo | Props canónicas (`disabled/loading/type/color/intent`); ver **S-1** (hooks CSS mixtos clase/data) | +| 3 | Coherencia semántica ecosistema/theming | ✓ ejemplar | `intent` = SOLO visual (cap. 22 §11 citado); contact sin intent; cascada intent-gana-sobre-color en provider; anti-patrón `contact+fulfill` documentado (cap. 22 §8) | +| 4 | API normalizada | ✓ | `OptsFromProps`; defaults documentados; `color` tipado laxo para paleta per THEMING §25.5 con re-type en eidos; `onPress` = feedback diagnóstico (no side-effect) | +| 5 | Tokens usados | ✓ | Piloto de la cascada `--button-palette-*`; recipe R-limpio; focus por `--focus-ring-*`; 0 usos de state-layer = **correcto** (todas las variantes son valenced — swap de paleta; sin tier neutro) | +| 6 | Parte semántica | validar | **F-6**: sin pack sema — `contact.activate` suena por el default de familia → ver S-3 | +| 7 | Animaciones | ✓ | Sin `@keyframes` perceptuales locales; press vía sistema (`--press-*`); spinner = periodo funcional | +| 8 | Contrato morfo | ✓ con hallazgo | Ejemplar: APG enlazado, keyboard declarado, textos localizados, `data-state` eliminado con la razón del clobbering (audit 05-27) documentada. **F-1**: Spinner con `archetype: 'item'` siendo parte display | +| 9 | Accesibilidad | ✓ | `aria-busy` en loading, `aria-disabled`, iconos `aria-hidden`, `iconOnly` conserva label sr-only, `loadingText` mantiene children sr-only, Enter/Space nativos, touch coarse por arquetipo trigger | +| 10 | Adopción theming | ✓ | Bundle de size vía recipe; depth n/a (no flota); focus modelo superficies (outline) ✓; palette cascade ✓ | +| 11 | Composición interna | ✓ | Participa en `Field.Provider` (disabled OR-merge); consume contexto `ButtonGroup` para defaults (prop explícita gana); es el COMPUESTO por IconButton/triggers | +| 12 | Estados y ciclo | ✓ | disabled/loading completos (click gated en ambos); `type` default 'button' (anti-submit implícito); controlled n/a (stateless) | +| 13 | Disciplina runtime | ✓ | Runtime-direct; sin DOM manual; sin timers; `$derived` limpios | +| 14 | ~~Demo testbed~~ | fuera de alcance | Demos excluidas de la auditoría (corrección 2026-07-07) — sus reglas D-\* viven en tmp/component-audit.md, no aquí | +| 15 | Docs + tests | **gap doble** | **F-4**: falta README eidos (el de soma completo con `## Sema events` ✓) · **F-7**: **CERO tests** de provider/wrapper · **F-2**: doc de `aria-label` en soma types referencia `iconOnly` (prop de eidos — fuga de capa) · **F-3**: docblock del wrapper cita `data-state` que el morfo eliminó | +| 16 | i18n | ✓ | `texts` del morfo localizados (`#?components.button.*`); `loadingText` del consumidor; sin strings hardcoded | ## Hallazgos y propuestas de solución — del componente -| ID | Hallazgo | Propuesta de solución | -|---|---|---| -| **F-1** | Spinner declara `archetype: 'item'` siendo parte display (`aria-hidden`, no interactiva) — el gotcha conocido: item arrastra estilo interactivo | Quitar el archetype de la parte Spinner (omitirlo, como manda la doctrina para partes display); verificación visual antes/después del spinner en los 3 placements | -| **F-2** | En soma `types.ts`, el doc de `aria-label` dice "Required when `iconOnly` is true" — `iconOnly` es prop de EIDOS, no existe en soma (fuga de capa en docs) | Reescribir el JSDoc sin referencia cross-capa ("override del nombre accesible; obligatorio cuando el botón no tiene texto visible") | -| **F-3** | Docblock de eidos `button.svelte` dice "Soma owns … `data-state`" — button eliminó `data-state` (el propio morfo documenta por qué) | Actualizar el docblock (data-loading/data-disabled por presencia; sin data-state) | -| **F-4** | Falta `eidos/components/button/README.md` (convención que la máquina exige, `E-2.3`); el README de soma está completo | Crear el README eidos siguiendo el patrón de los existentes (css-field/number-field): API visual, variantes, tokens públicos del recipe, composición | -| ~~F-5~~ | *(retirado — hallazgo de demo, fuera del alcance de la auditoría)* | — | -| **F-6** | Sin pack sema propio: `contact.activate` materializa el default de la familia | Ver **S-3** (es cuestión doctrinal de catálogo, no de button) | -| **F-7** | **Cero tests** de provider/wrapper — el componente insignia sin suite | Suite browser del provider (click→contact-activate, gate disabled/loading, Field-merge, cascada intent/color, snippetProps) + wrapper (spinner placements, iconOnly sr-only, loadingText swap). Alcance/momento: ver **S-2** | +| ID | Hallazgo | Propuesta de solución | +| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **F-1** | Spinner declara `archetype: 'item'` siendo parte display (`aria-hidden`, no interactiva) — el gotcha conocido: item arrastra estilo interactivo | Quitar el archetype de la parte Spinner (omitirlo, como manda la doctrina para partes display); verificación visual antes/después del spinner en los 3 placements | +| **F-2** | En soma `types.ts`, el doc de `aria-label` dice "Required when `iconOnly` is true" — `iconOnly` es prop de EIDOS, no existe en soma (fuga de capa en docs) | Reescribir el JSDoc sin referencia cross-capa ("override del nombre accesible; obligatorio cuando el botón no tiene texto visible") | +| **F-3** | Docblock de eidos `button.svelte` dice "Soma owns … `data-state`" — button eliminó `data-state` (el propio morfo documenta por qué) | Actualizar el docblock (data-loading/data-disabled por presencia; sin data-state) | +| **F-4** | Falta `eidos/components/button/README.md` (convención que la máquina exige, `E-2.3`); el README de soma está completo | Crear el README eidos siguiendo el patrón de los existentes (css-field/number-field): API visual, variantes, tokens públicos del recipe, composición | +| ~~F-5~~ | _(retirado — hallazgo de demo, fuera del alcance de la auditoría)_ | — | +| **F-6** | Sin pack sema propio: `contact.activate` materializa el default de la familia | Ver **S-3** (es cuestión doctrinal de catálogo, no de button) | +| **F-7** | **Cero tests** de provider/wrapper — el componente insignia sin suite | Suite browser del provider (click→contact-activate, gate disabled/loading, Field-merge, cascada intent/color, snippetProps) + wrapper (spinner placements, iconOnly sr-only, loadingText swap). Alcance/momento: ver **S-2** | ## Hallazgos que escalan al sistema / theming -| ID | Hallazgo (visto en button, alcance = catálogo) | Propuesta | -|---|---|---| -| **S-1** | **Hooks estructurales del wrapper como clases** (`.eidos-button-{body,icon,sr-only,loading-text}`, 6 selectores) conviviendo con data-attrs (`[data-button-icon]`). Las clases **escapan a eidos-lint** (solo valida `[data-*]`) y crean segunda gramática de hooks | **Norma de catálogo**: hooks de selector SIEMPRE data-attrs (`data-{c}-{kebab}`), nunca clases; clase permitida solo como utility sin selector en CSS (p. ej. contenedor neutro). + Guard: lint que detecte `\.eidos-` / clases con estilos en CSS de componentes. Aplicar en el fix-phase a todos los que salgan en el barrido | -| **S-2** | Ausencia de tests no es exclusiva de button — política de alcance pendiente | **Decisión de catálogo** (checkpoint post-barrido): (a) suite por componente durante fase de fixes, o (b) batch de testing como fase propia posterior. La ficha de cada componente registra su cobertura actual para dimensionar | -| **S-3** | Componentes de contacto genérico sin pack sema: ¿el default de familia ES la firma, o el catálogo quiere carácter per-componente en los buques insignia? | **Decisión doctrinal** (checkpoint): criterio de cuándo un componente MERECE pack (hoy: 38 packs, sesgo a fields/pickers). Propuesta: default-de-familia = firma legítima para contact genérico (button, link); pack solo donde el patrón añade significado (ya es el statu quo — canonizarlo por escrito en CANON/sema doctrine) | -| **S-4** | El README vive en DOS niveles (soma completo aquí; la máquina exige el de eidos) — heterogeneidad de dónde vive la doc del componente | **Norma**: definir el nivel canónico (propuesta: eidos = puerta del consumidor, con API visual + tokens; soma README = contrato headless). La máquina ya empuja a eidos (`E-2.3`) — ratificar y documentar en component-guide | +| ID | Hallazgo (visto en button, alcance = catálogo) | Propuesta | +| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **S-1** | **Hooks estructurales del wrapper como clases** (`.eidos-button-{body,icon,sr-only,loading-text}`, 6 selectores) conviviendo con data-attrs (`[data-button-icon]`). Las clases **escapan a eidos-lint** (solo valida `[data-*]`) y crean segunda gramática de hooks | **Norma de catálogo**: hooks de selector SIEMPRE data-attrs (`data-{c}-{kebab}`), nunca clases; clase permitida solo como utility sin selector en CSS (p. ej. contenedor neutro). + Guard: lint que detecte `\.eidos-` / clases con estilos en CSS de componentes. Aplicar en el fix-phase a todos los que salgan en el barrido | +| **S-2** | Ausencia de tests no es exclusiva de button — política de alcance pendiente | **Decisión de catálogo** (checkpoint post-barrido): (a) suite por componente durante fase de fixes, o (b) batch de testing como fase propia posterior. La ficha de cada componente registra su cobertura actual para dimensionar | +| **S-3** | Componentes de contacto genérico sin pack sema: ¿el default de familia ES la firma, o el catálogo quiere carácter per-componente en los buques insignia? | **Decisión doctrinal** (checkpoint): criterio de cuándo un componente MERECE pack (hoy: 38 packs, sesgo a fields/pickers). Propuesta: default-de-familia = firma legítima para contact genérico (button, link); pack solo donde el patrón añade significado (ya es el statu quo — canonizarlo por escrito en CANON/sema doctrine) | +| **S-4** | El README vive en DOS niveles (soma completo aquí; la máquina exige el de eidos) — heterogeneidad de dónde vive la doc del componente | **Norma**: definir el nivel canónico (propuesta: eidos = puerta del consumidor, con API visual + tokens; soma README = contrato headless). La máquina ya empuja a eidos (`E-2.3`) — ratificar y documentar en component-guide | ## Veredictos -*(pendiente — checkpoint único post-barrido; las propuestas de arriba se ejecutan en la fase de fixes)* +_(pendiente — checkpoint único post-barrido; las propuestas de arriba se ejecutan en la fase de fixes)_ diff --git a/docs/audit/components/calendar.md b/docs/audit/components/calendar.md index 88b72c49d..6d27030d8 100644 --- a/docs/audit/components/calendar.md +++ b/docs/audit/components/calendar.md @@ -7,34 +7,34 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 1 | Composición | ✓ | 14 partes compositivas (Provider→Grid ``→GridRow ``→Cell `
`→Day); MonthSelect/YearSelect ``→GridRow ``→Cell `
`→Day); MonthSelect/YearSelect `` de formato — PrimitiveSelectAttributes, types :14) — composición, no prop-tree | -| 2 | Naming | ✓ conforme + patrón nuevo | `value/onValueChange`, `placeholder/…` ✓; **`format/onFormatChange/allowedFormats`** (types :64-75) — el patrón `X/onXChange/allowedXs` para ejes conmutables, candidato a canon en _naming.md; `enableAlpha` (:77-80) | -| 3 | Coherencia semántica | hallazgo de trío | Solo `commit-set` (verificado) — sin `commit-reset` como time-field → F-1; `onInvalid` con **razones tipadas** (`'custom' \| 'incomplete'`, types :34-38) ✓ mejor que sus hermanos (razón sin tipar) | -| 4 | API | ✓ | Validación estructurada; tipos de `$libs/color` (lib propia, doctrina cero-dep ✓) | -| 5 | Tokens | ✓ con dup | `height-{k}` duplicadas → SYS-6; canales 3ch fijos documentados en css (no reflow 99→100) | -| 6 | Sema | declarado, cuestionado | `family-default` explícito — mismo veredicto de trío que time-field (SYS-3) | -| 7 | Animaciones | ✓ | Patrón familia | -| 8 | Contrato morfo | verificar bloque ARIA | Espejo del trío; **incluir en F-3 de time-field**: ¿su Input lleva el bloque ARIA rico (readonly/invalid) o el pobre de date-field? — unificar los tres en el pass | -| 9 | A11y | con F-2 | Sin Home/End (especialmente útil aquí: canales 0-255/0-360) | -| 10 | Theming | censo | Focus outline → SYS-5 | -| 11 | Composición interna | mandato | Sin wrapper `[data-field]` (SYS-6/B.1) | -| 12 | Estados | ✓ | readonlySegments granular; hidden-input; `allowedFormats` de una entrada = select bloqueado (documentado) | -| 13 | Runtime | ✓ | targetOverride aplicado (antecedente) | -| 14 | Docs+tests | ✓ | README ×2, suite ✓ | -| 15 | i18n | ✓ | texts morfo; formatos por segmento localizados | +| # | Dimensión | Estado | Evidencia | +| --- | -------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Composición | ✓ | Partes compositivas + **FormatSelect** como parte propia (native `` real invisible + celdas fake renderizadas (`PinInputCellData`: char/isActive/isFilled/**hasFakeCaret** — types) — el input-único preserva autofill de SMS/gestores ✓ | -| 2 | Naming | **dato de censo** | `PinInputType: numeric\|alphanumeric\|alphabetic\|custom`; cells en snippet; `value` string; **`onComplete`** (test provider :115 "fires onComplete once") — CUARTO nombre de callback terminal del catálogo (vs onValueCommit/onSubmit/solo-evento) → _naming fila 22 | -| 3 | Coherencia semántica | **GAP GRAVE (elevado desde el pass superficial)** | **F-1**: `semantic:` = 0 confirmado (77 líneas) Y el header del morfo (:7-9) PROMETE lo contrario: "Sound for **commit-fill** (when the input reaches its length) comes from the `commit` family base — no per-component cascade needed" — pero no existe evento `commit-fill` alguno y `scope: ['soma','eidos']` (:10) NI SIQUIERA incluye sema: el sonido prometido es FÍSICAMENTE imposible. Comentario y contrato se contradicen en el mismo fichero. Tras el censo de delegaciones de la familia pickers (ntp exonerado, Picker con deuda declarada), pin-input queda como **el único 0-eventos desnudo del catálogo** | -| 4 | API | ✓ | Tipos de entrada, celdas derivadas, fake caret | -| 8 | Contrato morfo | con F-1 | APG textbox ✓ correcto para el input único | -| 9 | A11y | verificar en fixes | El anuncio del progreso (celda N de M) — revisar cuando F-1 defina los eventos | -| 10-15 | | ✓ | Focus outline; tests ✓; README ×2 ✓; sin señales de indisciplina | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Composición | ✓ | Patrón OTP estándar: UN `` real invisible + celdas fake renderizadas (`PinInputCellData`: char/isActive/isFilled/**hasFakeCaret** — types) — el input-único preserva autofill de SMS/gestores ✓ | +| 2 | Naming | **dato de censo** | `PinInputType: numeric\|alphanumeric\|alphabetic\|custom`; cells en snippet; `value` string; **`onComplete`** (test provider :115 "fires onComplete once") — CUARTO nombre de callback terminal del catálogo (vs onValueCommit/onSubmit/solo-evento) → \_naming fila 22 | +| 3 | Coherencia semántica | **GAP GRAVE (elevado desde el pass superficial)** | **F-1**: `semantic:` = 0 confirmado (77 líneas) Y el header del morfo (:7-9) PROMETE lo contrario: "Sound for **commit-fill** (when the input reaches its length) comes from the `commit` family base — no per-component cascade needed" — pero no existe evento `commit-fill` alguno y `scope: ['soma','eidos']` (:10) NI SIQUIERA incluye sema: el sonido prometido es FÍSICAMENTE imposible. Comentario y contrato se contradicen en el mismo fichero. Tras el censo de delegaciones de la familia pickers (ntp exonerado, Picker con deuda declarada), pin-input queda como **el único 0-eventos desnudo del catálogo** | +| 4 | API | ✓ | Tipos de entrada, celdas derivadas, fake caret | +| 8 | Contrato morfo | con F-1 | APG textbox ✓ correcto para el input único | +| 9 | A11y | verificar en fixes | El anuncio del progreso (celda N de M) — revisar cuando F-1 defina los eventos | +| 10-15 | | ✓ | Focus outline; tests ✓; README ×2 ✓; sin señales de indisciplina | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **F-1** | Interactivo sin contrato semántico (0 eventos; `onComplete` dispara JS crudo sin commit) | Declarar: `commit-set` (+fulfill) al completar todas las celdas (donde hoy dispara `onComplete`) · opcional `signal-warn-reject` untilFix si el consumidor marca el código inválido (el patrón de la familia: textarea/tags/form) · luego pack o family-default razonado. Es EL gap de contrato de la familia junto a natural-time-picker | -| **F-2** | Composición Field: soma OR-merge ✓ verificado (:99,:136 inputId al hidden) | Queda solo verificar el wrapper eidos (label/error del OTP) con el pass SYS-6 | +| **F-2** | Composición Field: soma OR-merge ✓ verificado (:99,:136 inputId al hidden) | Queda solo verificar el wrapper eidos (label/error del OTP) con el pass SYS-6 | ## Escalan al sistema @@ -30,4 +30,4 @@ Censo A-3.1-de-facto (interactivos sin eventos): natural-time-picker + **pin-inp ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/popover.md b/docs/audit/components/popover.md index aac57760f..1b6a629e0 100644 --- a/docs/audit/components/popover.md +++ b/docs/audit/components/popover.md @@ -6,11 +6,11 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ + dato de censo | `emerge-present`/`emerge-close` + pack ✓; **censo verbos emerge**: popover dice present/close, dialog open/close, tooltip open/close/dismiss, toast present/dismiss, drawer/float-panel present/close, menús open/close — ¿matriz deliberada (present=superficie sin robo de foco / open=toma foco... que tooltip rompe) o drift? → veredicto con la tabla del CANON | -| 8 | Contrato morfo | ✓ | Focus trap/return declarados; banda z overlay ✓ | -| resto | | ✓ | Es la referencia de la familia (de-dialoged + delegaciones de pickers apuntan aquí) | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ + dato de censo | `emerge-present`/`emerge-close` + pack ✓; **censo verbos emerge**: popover dice present/close, dialog open/close, tooltip open/close/dismiss, toast present/dismiss, drawer/float-panel present/close, menús open/close — ¿matriz deliberada (present=superficie sin robo de foco / open=toma foco... que tooltip rompe) o drift? → veredicto con la tabla del CANON | +| 8 | Contrato morfo | ✓ | Focus trap/return declarados; banda z overlay ✓ | +| resto | | ✓ | Es la referencia de la familia (de-dialoged + delegaciones de pickers apuntan aquí) | ## Hallazgos y propuestas @@ -22,4 +22,4 @@ Limpio a nivel de capas leídas. Aporta el dato present/close al censo de verbos ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/progress.md b/docs/audit/components/progress.md index a61da7ccd..7f873cb60 100644 --- a/docs/audit/components/progress.md +++ b/docs/audit/components/progress.md @@ -3,4 +3,4 @@ - **Fecha**: 2026-07-07 · **Familia**: feedback · **Método**: solo análisis - **Capas**: morfo ✓ · soma (tests: 1 ✓) · README soma ✓ / eidos ✓ · pack — · **Máquina: PASS limpio** · indeterminate = periodo funcional ✓. -— limpio. **Escalan**: —. · **Veredictos**: *(pendiente checkpoint post-barrido)* +— limpio. **Escalan**: —. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/qr-code.md b/docs/audit/components/qr-code.md index 5966e3c8c..e1acd6ef2 100644 --- a/docs/audit/components/qr-code.md +++ b/docs/audit/components/qr-code.md @@ -16,4 +16,4 @@ catálogo. Sin hallazgos de componente (todo lo que tenía era demo). ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/radio-cards.md b/docs/audit/components/radio-cards.md index f2db8f2a5..000dd311d 100644 --- a/docs/audit/components/radio-cards.md +++ b/docs/audit/components/radio-cards.md @@ -2,22 +2,22 @@ - **Familia**: controls (cards de selección única SOBRE RadioGroup) · **Método**: solo análisis · demos fuera - **Capas leídas**: [morfo](../../../src/uix/morfo/components/radio-cards.ts) (120 líneas; header :4-22, partes :37-111) -- **Máquina**: `F-1.1`–`F-1.4` (expediente pre-flight) · `F-1.5` (sin `## Passive justification`) *(su D-1.2 era de demo — fuera de alcance)* +- **Máquina**: `F-1.1`–`F-1.4` (expediente pre-flight) · `F-1.5` (sin `## Passive justification`) _(su D-1.2 era de demo — fuera de alcance)_ - **Corrección sobre el pass superficial**: decía "morfo —"; FALSO — el morfo EXISTE con `scope:['eidos']` y **la mejor razón de delegación del catálogo** (:4-22): "Reuses RadioGroup's soma wholesale… No new soma, no new events, no new sema — `commit-select` fires from the reused RadioGroup item runtime, handled by RadioGroup's sema pack… **declaring them here would duplicate the contract**. 0-event surface (like Badge)". Sin dir soma propio — coherente con el header. ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 1/3 | Composición y semántica | ✓ **cita para el canon** | Delegación wholesale documentada con el argumento anti-duplicación (:14-18) — EL texto a citar en SYS-11 (c) | -| 8 | Contrato morfo | ✓ | Partes de presentación card (Item/Indicator/Icon/Content/Title/Description :51-111) — exactamente el "thin contract for the card-presentation parts the recipe targets" | -| 10 | Theming | ✓ | R-rules limpias; ítem consume state-layer selected ✓ (rollout §38) | -| 14 | Docs | gap de papeleo | Expediente pre-flight + Passive justification sin escribir (el header YA contiene el argumento — solo hay que formalizarlo en la sección F-1.5) | +| # | Dimensión | Estado | Evidencia | +| --- | ----------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1/3 | Composición y semántica | ✓ **cita para el canon** | Delegación wholesale documentada con el argumento anti-duplicación (:14-18) — EL texto a citar en SYS-11 (c) | +| 8 | Contrato morfo | ✓ | Partes de presentación card (Item/Indicator/Icon/Content/Title/Description :51-111) — exactamente el "thin contract for the card-presentation parts the recipe targets" | +| 10 | Theming | ✓ | R-rules limpias; ítem consume state-layer selected ✓ (rollout §38) | +| 14 | Docs | gap de papeleo | Expediente pre-flight + Passive justification sin escribir (el header YA contiene el argumento — solo hay que formalizarlo en la sección F-1.5) | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ---------------------------- | ---------------------------------------------------------------------- | | **F-1** | Papeleo (pre-flight + F-1.5) | Escribir las secciones — el contenido ya existe en el header del morfo | ## Escalan al sistema @@ -26,4 +26,4 @@ SYS-7 (segundo átomo/composición BIEN construido — con color-swatch) · SYS- ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/radio-group.md b/docs/audit/components/radio-group.md index 2e0a036d5..8294b1f29 100644 --- a/docs/audit/components/radio-group.md +++ b/docs/audit/components/radio-group.md @@ -6,12 +6,12 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ | `commit-select` affirm (:19-24) — consistente con el censo colección-select=affirm; pack ✓; sin unselect (radio no des-selecciona ✓ coherente) | -| 8 | Contrato morfo | ✓ | APG radiogroup; **HiddenInput como PARTE declarada** (:141 — el form-bridge en el contrato, no improvisado); Indicator/Label partes | -| 10 | Theming | ✓ | Focus outline → SYS-5; touch-row YA hecho (la referencia del ◇ pendiente de checkbox/switch) | -| resto | | ✓ | Roving ✓; tests ✓; README ×2 ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ | `commit-select` affirm (:19-24) — consistente con el censo colección-select=affirm; pack ✓; sin unselect (radio no des-selecciona ✓ coherente) | +| 8 | Contrato morfo | ✓ | APG radiogroup; **HiddenInput como PARTE declarada** (:141 — el form-bridge en el contrato, no improvisado); Indicator/Label partes | +| 10 | Theming | ✓ | Focus outline → SYS-5; touch-row YA hecho (la referencia del ◇ pendiente de checkbox/switch) | +| resto | | ✓ | Roving ✓; tests ✓; README ×2 ✓ | ## Hallazgos y propuestas @@ -23,4 +23,4 @@ Limpio a nivel de capas leídas. (Su touch-row es el patrón a copiar en checkbo ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/range-calendar.md b/docs/audit/components/range-calendar.md index f1898d96f..e29b4f90d 100644 --- a/docs/audit/components/range-calendar.md +++ b/docs/audit/components/range-calendar.md @@ -6,28 +6,28 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 1 | Composición | ✓ | Estructura del donante (Provider/Header/Prev/Next/Grid/Cell/Day) + no-archetype de Day con razón propia (:284 bespoke `[data-range-calendar-day]`) | -| 3 | Coherencia semántica | ✓ **contrato de referencia** con gap de carácter | **La gradación de rango que faltaba en los field-ranges está AQUÍ**: `commit-select-start` (affirm :18-23) → `commit-select-range` (**fulfill** :28-33 — el sellado del par ES el momento fulfill) → `commit-reset` (neutral :38-43) + shift-navigate. Es EL precedente interno para date-range-field/time-range-field F-1 (citarlo en su veredicto). **F-1**: toda esa riqueza suena a `family-default` (:8) — sin pack, el fulfill del rango completo no se distingue del default genérico | -| 6 | Sema | gap | Sin pack (censo: CERO packs en la familia calendar salvo el donante) → SYS-3 con el mejor argumento: eventos ricos YA declarados sin carácter | -| 8 | Contrato morfo | ✓ con herencias | `data-range-start`/`data-range-end` en Cell (:253-254) y Day (:294-295) + data-selected para el tramo — vocabulario de rango coherente; **F-2**: `role='application'` (:64) heredado sin razón (censo familia — 4 morfos) · **F-3**: Day `role='button'` (:289) + `aria-selected` (:317) — el MISMO ARIA inválido del donante (calendar F-2); Cell (:268) la lleva legítimamente | -| 9 | A11y | con F-2/F-3 | Teclado del donante (grid APG) | -| 11 | Composición interna | **borrower** | 79 tokens `--calendar-*` consumidos informalmente + privados de acento re-declarados (8 ✓); marcas holiday/event muertas tras B.2 (el token vive en `[data-calendar]`) → SYS-8, caso TRIVIAL (privados ya declarados) | -| 12 | Estados | hallazgo máquina | R-1.3 readonly sin materializar (patrón familia) | -| 14 | Docs+tests | gap | Sin README eidos (E-2.3); tests ✓ | -| resto | | ✓ / hereda donante | Incluida la cuestión weekend-por-locale (calendar F-4) si comparte la derivación | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Composición | ✓ | Estructura del donante (Provider/Header/Prev/Next/Grid/Cell/Day) + no-archetype de Day con razón propia (:284 bespoke `[data-range-calendar-day]`) | +| 3 | Coherencia semántica | ✓ **contrato de referencia** con gap de carácter | **La gradación de rango que faltaba en los field-ranges está AQUÍ**: `commit-select-start` (affirm :18-23) → `commit-select-range` (**fulfill** :28-33 — el sellado del par ES el momento fulfill) → `commit-reset` (neutral :38-43) + shift-navigate. Es EL precedente interno para date-range-field/time-range-field F-1 (citarlo en su veredicto). **F-1**: toda esa riqueza suena a `family-default` (:8) — sin pack, el fulfill del rango completo no se distingue del default genérico | +| 6 | Sema | gap | Sin pack (censo: CERO packs en la familia calendar salvo el donante) → SYS-3 con el mejor argumento: eventos ricos YA declarados sin carácter | +| 8 | Contrato morfo | ✓ con herencias | `data-range-start`/`data-range-end` en Cell (:253-254) y Day (:294-295) + data-selected para el tramo — vocabulario de rango coherente; **F-2**: `role='application'` (:64) heredado sin razón (censo familia — 4 morfos) · **F-3**: Day `role='button'` (:289) + `aria-selected` (:317) — el MISMO ARIA inválido del donante (calendar F-2); Cell (:268) la lleva legítimamente | +| 9 | A11y | con F-2/F-3 | Teclado del donante (grid APG) | +| 11 | Composición interna | **borrower** | 79 tokens `--calendar-*` consumidos informalmente + privados de acento re-declarados (8 ✓); marcas holiday/event muertas tras B.2 (el token vive en `[data-calendar]`) → SYS-8, caso TRIVIAL (privados ya declarados) | +| 12 | Estados | hallazgo máquina | R-1.3 readonly sin materializar (patrón familia) | +| 14 | Docs+tests | gap | Sin README eidos (E-2.3); tests ✓ | +| resto | | ✓ / hereda donante | Incluida la cuestión weekend-por-locale (calendar F-4) si comparte la derivación | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| -| **F-1** | Semántica de rango ejemplar SIN pack (fulfill suena genérico) | SYS-3: si la familia gana packs, este es EL candidato (start affirm suave → range fulfill pleno → reset neutral) | -| **F-2** | `role='application'` heredado sin razón documentada | Resolver JUNTO con calendar F-1 (decisión única de familia) | -| **F-3** | `aria-selected` sobre Day `role='button'` (:317) — ARIA inválido, duplicado del Cell | Mismo fix que calendar F-2 (familia entera en un pase) | -| **F-4** | Marcas holiday/event muertas (borrow roto) | SYS-8 (capa calendar-surface) | -| **F-5** | readonly sin estilo (R-1.3) | Patrón familia (con date-picker) | -| **F-6** | README eidos ausente | SYS-4 | +| ID | Hallazgo | Propuesta | +| ------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | +| **F-1** | Semántica de rango ejemplar SIN pack (fulfill suena genérico) | SYS-3: si la familia gana packs, este es EL candidato (start affirm suave → range fulfill pleno → reset neutral) | +| **F-2** | `role='application'` heredado sin razón documentada | Resolver JUNTO con calendar F-1 (decisión única de familia) | +| **F-3** | `aria-selected` sobre Day `role='button'` (:317) — ARIA inválido, duplicado del Cell | Mismo fix que calendar F-2 (familia entera en un pase) | +| **F-4** | Marcas holiday/event muertas (borrow roto) | SYS-8 (capa calendar-surface) | +| **F-5** | readonly sin estilo (R-1.3) | Patrón familia (con date-picker) | +| **F-6** | README eidos ausente | SYS-4 | ## Escalan al sistema @@ -35,4 +35,4 @@ SYS-3 (mejor evidencia del catálogo) · SYS-4 · SYS-8 (prestatario ancla) · c ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/rating-group.md b/docs/audit/components/rating-group.md index 2af8635e0..b09a573ba 100644 --- a/docs/audit/components/rating-group.md +++ b/docs/audit/components/rating-group.md @@ -6,17 +6,17 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ | `commit-set` neutral (:15-20 — fijar valor = neutral, canon de fields aplicado a control ✓); pack ✓ | -| 9 | A11y | verificación dirigida | **F-1**: readonly (display de media) debe anunciar el valor y el hover-preview no debe disparar commits — verificar en fixes | -| 10 | Theming | censo | Focus outline → SYS-5 | -| resto | | ✓ | tests ✓; README ×2 ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ | `commit-set` neutral (:15-20 — fijar valor = neutral, canon de fields aplicado a control ✓); pack ✓ | +| 9 | A11y | verificación dirigida | **F-1**: readonly (display de media) debe anunciar el valor y el hover-preview no debe disparar commits — verificar en fixes | +| 10 | Theming | censo | Focus outline → SYS-5 | +| resto | | ✓ | tests ✓; README ×2 ✓ | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | --------------------------------------------------- | -------------------------------------- | | **F-1** | Anuncio de readonly + aislamiento del hover-preview | Verificación dirigida en fase de fixes | ## Escalan al sistema @@ -25,4 +25,4 @@ ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/relative-time.md b/docs/audit/components/relative-time.md index cea3daf36..6d92af86d 100644 --- a/docs/audit/components/relative-time.md +++ b/docs/audit/components/relative-time.md @@ -1,7 +1,7 @@ # relative-time — ficha de auditoría de componente - **Fecha**: 2026-07-07 · **Familia**: service components · **Método**: solo análisis -- **Máquina**: **NEEDS-WORK** — `D-3.1` (*sin eidosSnippet*) · `F-1.1`/`F-1.4`/`F-1.5` (*expediente + justificación pasivo*). +- **Máquina**: **NEEDS-WORK** — `D-3.1` (_sin eidosSnippet_) · `F-1.1`/`F-1.4`/`F-1.5` (_expediente + justificación pasivo_). - Ver **trans.md** — misma clase, pass conjunto. Nota: su ticker de refresco debe ir vía `uix.timers` (verificar en el pass). -**Escalan**: SYS-7. · **Veredictos**: *(pendiente checkpoint post-barrido)* +**Escalan**: SYS-7. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/s-text-virtual-list.md b/docs/audit/components/s-text-virtual-list.md index 0ffe56db4..af321d9f6 100644 --- a/docs/audit/components/s-text-virtual-list.md +++ b/docs/audit/components/s-text-virtual-list.md @@ -2,22 +2,22 @@ - **Familia**: data (lista virtual de s-text) · **Método**: solo análisis · demos fuera - **Capas leídas**: [morfo](../../../src/uix/morfo/components/s-text-virtual-list.ts) (67 líneas; 0 eventos — composición s-text × virtual-list) -- **Máquina**: NEEDS-WORK — `E-2.3` *(su D-3.1 era demo — fuera)* +- **Máquina**: NEEDS-WORK — `E-2.3` _(su D-3.1 era demo — fuera)_ - **Corrección**: "morfo —" FALSO también aquí (67 líneas existen) ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 1/3 | Composición | ✓ | Composición de dos piezas del sistema; 0 eventos coherente (los del scroll viven en virtual-list — delegación) | -| 14 | Docs | gap | Sin README eidos | +| # | Dimensión | Estado | Evidencia | +| --- | ----------- | ------ | -------------------------------------------------------------------------------------------------------------- | +| 1/3 | Composición | ✓ | Composición de dos piezas del sistema; 0 eventos coherente (los del scroll viven en virtual-list — delegación) | +| 14 | Docs | gap | Sin README eidos | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| -| **F-1** | README eidos | SYS-4 | -| **F-2** | Clasificación formal | SYS-7 | +| ID | Hallazgo | Propuesta | +| ------- | -------------------- | --------- | +| **F-1** | README eidos | SYS-4 | +| **F-2** | Clasificación formal | SYS-7 | ## Escalan al sistema @@ -25,4 +25,4 @@ SYS-4 · SYS-7. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/s-text.md b/docs/audit/components/s-text.md index 5ae93aa73..f691b1d06 100644 --- a/docs/audit/components/s-text.md +++ b/docs/audit/components/s-text.md @@ -7,17 +7,17 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 8 | Contrato morfo | ✓ existe | Morfo pasivo (0 eventos — display streaming); verificar en fixes si lleva línea de razón (censo "sin línea") | -| 14 | Docs | gap | Sin README eidos → SYS-4 | +| # | Dimensión | Estado | Evidencia | +| --- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------ | +| 8 | Contrato morfo | ✓ existe | Morfo pasivo (0 eventos — display streaming); verificar en fixes si lleva línea de razón (censo "sin línea") | +| 14 | Docs | gap | Sin README eidos → SYS-4 | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| -| **F-1** | README eidos | SYS-4 | -| **F-2** | Clasificación formal de pasivo | SYS-7 | +| ID | Hallazgo | Propuesta | +| ------- | ------------------------------ | --------- | +| **F-1** | README eidos | SYS-4 | +| **F-2** | Clasificación formal de pasivo | SYS-7 | ## Escalan al sistema @@ -25,4 +25,4 @@ SYS-4 · SYS-7 · el censo de falsedades "morfo —" (dato de proceso para el in ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/scroll-area.md b/docs/audit/components/scroll-area.md index b7d4e6256..d3000deed 100644 --- a/docs/audit/components/scroll-area.md +++ b/docs/audit/components/scroll-area.md @@ -10,4 +10,4 @@ censo D13**: las scrollbars custom miden viewport/track por definición — verificación dirigida del provider con textarea como precedente confirmado. -**Escalan**: censo D13 (candidato prioritario). · **Veredictos**: *(pendiente checkpoint post-barrido)* +**Escalan**: censo D13 (candidato prioritario). · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/scroll-frames.md b/docs/audit/components/scroll-frames.md index 061ae19a0..6fe2775b3 100644 --- a/docs/audit/components/scroll-frames.md +++ b/docs/audit/components/scroll-frames.md @@ -2,11 +2,11 @@ - **Fecha**: 2026-07-07 · **Familia**: media/scroll (secuencia por frames al scroll) · **Método**: solo análisis - **Capas**: morfo — · soma — · README eidos ✓ · pack — · opacidad cruda legítima documentada (§29) ✓ -- **Máquina**: **NEEDS-WORK** — `F-1.1`–`F-1.5` (*expediente completo + justificación pasivo ausentes*). +- **Máquina**: **NEEDS-WORK** — `F-1.1`–`F-1.5` (_expediente completo + justificación pasivo ausentes_). -| Hallazgo | Propuesta | -|---|---| -| F-1: expediente + justificación | Escribir (pass F-1.x conjunto) | +| Hallazgo | Propuesta | +| ---------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | +| F-1: expediente + justificación | Escribir (pass F-1.x conjunto) | | F-2: verificar disciplina runtime (scroll listener vía dom.listen, lecturas vía dom.measure — es EL candidato a violarlo por naturaleza) | Verificación dirigida en fase de fixes | -**Escalan**: SYS-7 · SYS-9. · **Veredictos**: *(pendiente checkpoint post-barrido)* +**Escalan**: SYS-7 · SYS-9. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/search-field.md b/docs/audit/components/search-field.md index 61f31fe47..eba2b552f 100644 --- a/docs/audit/components/search-field.md +++ b/docs/audit/components/search-field.md @@ -6,38 +6,38 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 1 | Composición | ✓ | Partes compositivas (Icon/Input/LoadingIndicator/ClearTrigger); Input bloquea passthrough de `value/onchange` vía `Without` (types :105-108) — el provider posee el valor | -| 2 | Naming | ✓ con heterogeneidad de tipado | `value/onValueChange` ✓; **`onSubmit: (value)=>void` y `onClear: ()=>void` en crudo** mientras onValueChange usa `OnChangeFn` (types :43-47) — tipado de callbacks heterogéneo → fila en _naming; `debounceMs` (sufijo-unidad, :74); `clearOnEscape` (:50) | -| 3 | Coherencia semántica | ✓ mejor que el trío | `commit-submit` + **`commit-reset`** (morfo :14-34) — buscar Y limpiar suenan; nota: target=`provider` mientras el trío targetea `input` (convención de target a unificar, menor) | -| 4 | API | ✓ ejemplar | Doble camino documentado con razón (live vs formal, types :29-32); debounce con semántica precisa (valor síncrono, callback diferido, flush en clear/submit, :68-74); interacción aria-label↔Field.Label documentada (:82-86) | -| 5 | Tokens | ✓ con dup | `height-{k}` duplicada (SYS-6); resto canónico | -| 6 | Sema | ✓ | `expression: 'pack'` + pack existente — submit/reset con carácter | -| 7 | Animaciones | ✓ | Sin keyframes; LoadingIndicator = spinner del sistema | -| 8 | Contrato morfo | **2 hallazgos** | **F-1**: `apg: combobox` (:9) pero NO hay popup/listbox (8 partes verificadas) — el patrón real es `role=searchbox` (:73); cita APG errónea. **F-2**: LoadingIndicator con `aria-hidden='true'` **Y** `aria-live='polite'` juntos (:113-116) — se anulan (un nodo hidden no anuncia); elegir uno. Flags focused/empty/loading en provider (:49-51) ✓ superficie de estado rica. Escape condicional DECLARADO en morfo (`when: prop-truthy clearOnEscape`, :95-99) ✓ elegante | -| 9 | A11y | ✓ con F-2 | aria-label vía `translationRef` con fallback (:82-84 — mecanismo i18n en el contrato ✓); aria-invalid condicional | -| 10 | Theming | hallazgo R-1.3 | `data-readonly` declarado (:46,:77) SIN estilo — contrato visual incompleto (máquina); focus outline puro → SYS-5 | -| 11 | Composición interna | ✓ | Participa de Field.Provider (OR-merge documentado, types :26-27, :52) | -| 12 | Estados | ✓ | disabled/readonly/required/invalid + focused/empty/loading proyectados | -| 13 | Runtime | ✓ **ejemplar** | Debounce vía `TimerHandle` de uix.timers con clave namespaced `soma:search-field:{id}:debounce` (provider :71,:115-116) + **cancel al desmontar** (regla A6 citada, :107-109) — el patrón de disciplina que la dimensión pide | -| 14 | Docs+tests | ✓ | README ×2, suite ✓ | -| 15 | i18n | ✓ | texts morfo (label + clear); translationRef en aria | +| # | Dimensión | Estado | Evidencia | +| --- | -------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Composición | ✓ | Partes compositivas (Icon/Input/LoadingIndicator/ClearTrigger); Input bloquea passthrough de `value/onchange` vía `Without` (types :105-108) — el provider posee el valor | +| 2 | Naming | ✓ con heterogeneidad de tipado | `value/onValueChange` ✓; **`onSubmit: (value)=>void` y `onClear: ()=>void` en crudo** mientras onValueChange usa `OnChangeFn` (types :43-47) — tipado de callbacks heterogéneo → fila en \_naming; `debounceMs` (sufijo-unidad, :74); `clearOnEscape` (:50) | +| 3 | Coherencia semántica | ✓ mejor que el trío | `commit-submit` + **`commit-reset`** (morfo :14-34) — buscar Y limpiar suenan; nota: target=`provider` mientras el trío targetea `input` (convención de target a unificar, menor) | +| 4 | API | ✓ ejemplar | Doble camino documentado con razón (live vs formal, types :29-32); debounce con semántica precisa (valor síncrono, callback diferido, flush en clear/submit, :68-74); interacción aria-label↔Field.Label documentada (:82-86) | +| 5 | Tokens | ✓ con dup | `height-{k}` duplicada (SYS-6); resto canónico | +| 6 | Sema | ✓ | `expression: 'pack'` + pack existente — submit/reset con carácter | +| 7 | Animaciones | ✓ | Sin keyframes; LoadingIndicator = spinner del sistema | +| 8 | Contrato morfo | **2 hallazgos** | **F-1**: `apg: combobox` (:9) pero NO hay popup/listbox (8 partes verificadas) — el patrón real es `role=searchbox` (:73); cita APG errónea. **F-2**: LoadingIndicator con `aria-hidden='true'` **Y** `aria-live='polite'` juntos (:113-116) — se anulan (un nodo hidden no anuncia); elegir uno. Flags focused/empty/loading en provider (:49-51) ✓ superficie de estado rica. Escape condicional DECLARADO en morfo (`when: prop-truthy clearOnEscape`, :95-99) ✓ elegante | +| 9 | A11y | ✓ con F-2 | aria-label vía `translationRef` con fallback (:82-84 — mecanismo i18n en el contrato ✓); aria-invalid condicional | +| 10 | Theming | hallazgo R-1.3 | `data-readonly` declarado (:46,:77) SIN estilo — contrato visual incompleto (máquina); focus outline puro → SYS-5 | +| 11 | Composición interna | ✓ | Participa de Field.Provider (OR-merge documentado, types :26-27, :52) | +| 12 | Estados | ✓ | disabled/readonly/required/invalid + focused/empty/loading proyectados | +| 13 | Runtime | ✓ **ejemplar** | Debounce vía `TimerHandle` de uix.timers con clave namespaced `soma:search-field:{id}:debounce` (provider :71,:115-116) + **cancel al desmontar** (regla A6 citada, :107-109) — el patrón de disciplina que la dimensión pide | +| 14 | Docs+tests | ✓ | README ×2, suite ✓ | +| 15 | i18n | ✓ | texts morfo (label + clear); translationRef en aria | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| -| **F-1** | Cita APG errónea (combobox sin popup) | Cambiar a la referencia correcta (searchbox no tiene patrón APG propio → usar la regla "sin patrón + razón" que saldrá de SYS para media-player/drag-drop) | -| **F-2** | `aria-hidden` + `aria-live` juntos en LoadingIndicator — se anulan | Decidir la intención: si debe ANUNCIAR "buscando…", quitar aria-hidden y dar texto sr-only; si es decorativo, quitar aria-live | -| **F-3** | readonly sin estilo (R-1.3) | Tratamiento único de familia (composición Field, SYS-6) | -| **F-4** | Tipado de callbacks heterogéneo (OnChangeFn vs crudo) | Normalizar en el veredicto de _naming (propuesta: OnChangeFn para todo callback de dato; crudo solo para señales sin payload) | -| **F-5** | `height-{k}` duplicada | SYS-6 | +| ID | Hallazgo | Propuesta | +| ------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **F-1** | Cita APG errónea (combobox sin popup) | Cambiar a la referencia correcta (searchbox no tiene patrón APG propio → usar la regla "sin patrón + razón" que saldrá de SYS para media-player/drag-drop) | +| **F-2** | `aria-hidden` + `aria-live` juntos en LoadingIndicator — se anulan | Decidir la intención: si debe ANUNCIAR "buscando…", quitar aria-hidden y dar texto sr-only; si es decorativo, quitar aria-live | +| **F-3** | readonly sin estilo (R-1.3) | Tratamiento único de familia (composición Field, SYS-6) | +| **F-4** | Tipado de callbacks heterogéneo (OnChangeFn vs crudo) | Normalizar en el veredicto de \_naming (propuesta: OnChangeFn para todo callback de dato; crudo solo para señales sin payload) | +| **F-5** | `height-{k}` duplicada | SYS-6 | ## Escalan al sistema -_naming (tipado de callbacks + debounceMs + target-de-evento provider-vs-input) · SYS-5 · SYS-6 · regla APG-sin-patrón (con media-player/drag-drop). +\_naming (tipado de callbacks + debounceMs + target-de-evento provider-vs-input) · SYS-5 · SYS-6 · regla APG-sin-patrón (con media-player/drag-drop). ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/section.md b/docs/audit/components/section.md index d74b20563..852e71e1c 100644 --- a/docs/audit/components/section.md +++ b/docs/audit/components/section.md @@ -1,10 +1,10 @@ # section — ficha de auditoría de componente - **Fecha**: 2026-07-07 · **Familia**: layout (ritmo vertical) · **Método**: solo análisis -- **Capas**: morfo ✓ (scope eidos) · README eidos ✓ · **Máquina: PASS** con `D-7.4` (*chip drift*). +- **Capas**: morfo ✓ (scope eidos) · README eidos ✓ · **Máquina: PASS** con `D-7.4` (_chip drift_). -| Hallazgo | Propuesta | -|---|---| +| Hallazgo | Propuesta | +| ----------------------- | ------------------- | | F-1: chips desalineados | Pass D-7.4 conjunto | -**Escalan**: censo D-7.4. · **Veredictos**: *(pendiente checkpoint post-barrido)* +**Escalan**: censo D-7.4. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/select.md b/docs/audit/components/select.md index b3499c199..09f41e79a 100644 --- a/docs/audit/components/select.md +++ b/docs/audit/components/select.md @@ -6,20 +6,20 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ completa | 4 eventos: `commit-select`/`commit-unselect` (affirm :21,:31) + `emerge-open`/`emerge-dismiss` (:38-64 — posee su popup, correcto: no delega a Popover porque su Content NO es dialog) + `expression: 'pack'` + pack ✓. **unselect=affirm** consistente con calendar/combobox (censo ×3: la doctrina de-facto "des-seleccionar es acto afirmativo; reset/cancel neutral" — solo falta escribirla) | -| 8 | Contrato morfo | ✓ ejemplar | Trigger `role='combobox'` (:91) + `aria-activedescendant` (:110) con la razón del managed-focus documentada (:62 "items are never DOM-focused"); listbox/option/group/separator (:144-243) — APG select-only combobox completo | -| 9 | A11y | ✓ con verificación | Teclado :119-125 (Enter/Space open, flechas, Home/End, Escape) ✓; **F-2**: typeahead (saltar a opción por carácter — APG lo recomienda; el nativo lo tiene) no declarable en la gramática keyboard del morfo → verificar en provider durante fixes | -| 10 | Theming | ✓ | list-surface adoptado (era pendiente del registro 06-21); focus outline (o5) → SYS-5; depth ×2 ✓ | -| resto | | ✓ | tests ✓; README ×2 ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ completa | 4 eventos: `commit-select`/`commit-unselect` (affirm :21,:31) + `emerge-open`/`emerge-dismiss` (:38-64 — posee su popup, correcto: no delega a Popover porque su Content NO es dialog) + `expression: 'pack'` + pack ✓. **unselect=affirm** consistente con calendar/combobox (censo ×3: la doctrina de-facto "des-seleccionar es acto afirmativo; reset/cancel neutral" — solo falta escribirla) | +| 8 | Contrato morfo | ✓ ejemplar | Trigger `role='combobox'` (:91) + `aria-activedescendant` (:110) con la razón del managed-focus documentada (:62 "items are never DOM-focused"); listbox/option/group/separator (:144-243) — APG select-only combobox completo | +| 9 | A11y | ✓ con verificación | Teclado :119-125 (Enter/Space open, flechas, Home/End, Escape) ✓; **F-2**: typeahead (saltar a opción por carácter — APG lo recomienda; el nativo lo tiene) no declarable en la gramática keyboard del morfo → verificar en provider durante fixes | +| 10 | Theming | ✓ | list-surface adoptado (era pendiente del registro 06-21); focus outline (o5) → SYS-5; depth ×2 ✓ | +| resto | | ✓ | tests ✓; README ×2 ✓ | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | | **F-1** | Adopción list-surface a confirmar fila a fila (item/option/group-label por `--list-item-*` sin re-declarar) | Verificación dirigida (grep + visual) en fase de fixes | -| **F-2** | Typeahead sin evidencia en morfo (puede vivir en provider) | Verificación dirigida; si falta, añadirlo (APG) | +| **F-2** | Typeahead sin evidencia en morfo (puede vivir en provider) | Verificación dirigida; si falta, añadirlo (APG) | ## Escalan al sistema @@ -27,4 +27,4 @@ Censo "unselect=affirm" ×3 (con calendar F-5: la respuesta es CONSISTENCIA — ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/separator.md b/docs/audit/components/separator.md index f0e88b5fc..4ff2e69e0 100644 --- a/docs/audit/components/separator.md +++ b/docs/audit/components/separator.md @@ -3,4 +3,4 @@ - **Fecha**: 2026-07-07 · **Familia**: layout (divisor) · **Método**: solo análisis - **Capas**: morfo ✓ (scope eidos) · README eidos ✓ · **Máquina: PASS limpio** · role=separator/none según decorativo ✓. -— limpio. **Escalan**: —. · **Veredictos**: *(pendiente checkpoint post-barrido)* +— limpio. **Escalan**: —. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/skeleton.md b/docs/audit/components/skeleton.md index 5da11b242..d82337dfb 100644 --- a/docs/audit/components/skeleton.md +++ b/docs/audit/components/skeleton.md @@ -2,10 +2,10 @@ - **Fecha**: 2026-07-07 · **Familia**: feedback de carga · **Método**: solo análisis - **Capas**: morfo ✓ · soma — (pasivo) · README eidos **✗** · pack — · shimmer = gradiente funcional (no tematizable POR DISEÑO §29) ✓ -- **Máquina**: **NEEDS-WORK** — `E-2.3` (*falta README eidos*). +- **Máquina**: **NEEDS-WORK** — `E-2.3` (_falta README eidos_). -| Hallazgo | Propuesta | -|---|---| -| F-1: README eidos | SYS-4 | +| Hallazgo | Propuesta | +| ----------------- | --------- | +| F-1: README eidos | SYS-4 | -**Escalan**: SYS-4. · **Veredictos**: *(pendiente checkpoint post-barrido)* +**Escalan**: SYS-4. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/slider.md b/docs/audit/components/slider.md index ef861f267..1df1dc0c1 100644 --- a/docs/audit/components/slider.md +++ b/docs/audit/components/slider.md @@ -5,9 +5,9 @@ - **⚠ WIP del usuario en curso** (slider.svelte / slider-provider / types modificados en working tree) — auditado read-only. - **Vigencia re-auditoría 2026-07-07**: mantenido read-only por el WIP (misma política que number/css-field) — re-visitar a profundidad piloto cuando el usuario cierre su pass; anotar entonces en el censo handle si declara la trilogía pick/drag/drop (knob la tiene completa). -| Hallazgo | Propuesta | -|---|---| +| Hallazgo | Propuesta | +| ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | F-1: **buffered-range = gap REGISTRADO** (iniciativa media-player 2026-06-29: el slider no soporta rango secundario/buffer que MediaPlayer necesita) | Diseñarlo como parte del slider (segunda pista `data-slider-buffer` + token de color), no ad-hoc en media-player — es el gap flagged de la iniciativa; ejecutar en fase de fixes con el veredicto de API | -| Nota: multi-thumb/range ✓; steps/marks ✓ | Re-pasar la ficha tras el WIP del usuario | +| Nota: multi-thumb/range ✓; steps/marks ✓ | Re-pasar la ficha tras el WIP del usuario | -**Escalan**: el gap buffered alimenta la fase de fixes de media-player. · **Veredictos**: *(pendiente checkpoint post-barrido; re-visitar tras WIP)* +**Escalan**: el gap buffered alimenta la fase de fixes de media-player. · **Veredictos**: _(pendiente checkpoint post-barrido; re-visitar tras WIP)_ diff --git a/docs/audit/components/spin-field.md b/docs/audit/components/spin-field.md index d52322798..a7318e3bb 100644 --- a/docs/audit/components/spin-field.md +++ b/docs/audit/components/spin-field.md @@ -6,18 +6,18 @@ ## Dimensiones — las que aplican a una capa -| Dim | Estado | Nota | -|---|---|---| -| 5/10 tokens | ✓ | `--spin-field-*` vía recipe; `data-spin-field*` markers; state-hover ✓ (1 uso, tier neutro correcto); glifos ▲/▼ +/− por `:empty::before` tematizables (§33) | -| 8 contrato | nota doctrinal | Sin morfo — la doctrina "morfo-first incluso leaf (scope eidos)" vs la naturaleza de CAPA (list-surface tampoco tiene). Coherente si se clasifica capa, no componente → alimentar el criterio en SYS (¿qué ES capa vs componente? calendar-surface llegará con la misma pregunta) | -| 15 docs | **gap** | Ni README ni doc de capa — la capa que dos componentes consumen no está documentada en sitio propio (solo §34 del changelog) | -| altura | ✓ | `--spin-field-height-md` = size-bundle ✓ (no duplica: ES la fuente para sus dos consumidores) | +| Dim | Estado | Nota | +| ----------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 5/10 tokens | ✓ | `--spin-field-*` vía recipe; `data-spin-field*` markers; state-hover ✓ (1 uso, tier neutro correcto); glifos ▲/▼ +/− por `:empty::before` tematizables (§33) | +| 8 contrato | nota doctrinal | Sin morfo — la doctrina "morfo-first incluso leaf (scope eidos)" vs la naturaleza de CAPA (list-surface tampoco tiene). Coherente si se clasifica capa, no componente → alimentar el criterio en SYS (¿qué ES capa vs componente? calendar-surface llegará con la misma pregunta) | +| 15 docs | **gap** | Ni README ni doc de capa — la capa que dos componentes consumen no está documentada en sitio propio (solo §34 del changelog) | +| altura | ✓ | `--spin-field-height-md` = size-bundle ✓ (no duplica: ES la fuente para sus dos consumidores) | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| -| F-1 | Capa sin doc propia | Doc de capa en `eidos/components/spin-field/README.md` (qué markers expone, qué tokens, quién la consume) — mismo patrón que tendrá calendar-surface | +| ID | Hallazgo | Propuesta | +| --- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| F-1 | Capa sin doc propia | Doc de capa en `eidos/components/spin-field/README.md` (qué markers expone, qué tokens, quién la consume) — mismo patrón que tendrá calendar-surface | | F-2 | Criterio capa-vs-componente sin escribir | → **SYS-7** (nuevo): definir formalmente "capa compartida" (spin-field, list-surface, field-segment-state, futura calendar-surface): sin morfo/soma, markers `data-{layer}*`, README de capa, y cómo la máquina las clasifica | ## Escalan al sistema @@ -26,4 +26,4 @@ ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/spinner.md b/docs/audit/components/spinner.md index bb744f145..64f3b5777 100644 --- a/docs/audit/components/spinner.md +++ b/docs/audit/components/spinner.md @@ -2,10 +2,10 @@ - **Fecha**: 2026-07-07 · **Familia**: feedback de carga (variantes propias bars·dots·ring §19 ✓) · **Método**: solo análisis - **Capas**: morfo ✓ · soma — (pasivo) · README eidos **✗** · pack — · keyframe spin = funcional anotado ✓; opacidad cruda legítima documentada (§29) ✓ -- **Máquina**: **NEEDS-WORK** — `E-2.3` · `D-7.4` (*chip drift en demo*). +- **Máquina**: **NEEDS-WORK** — `E-2.3` · `D-7.4` (_chip drift en demo_). -| Hallazgo | Propuesta | -|---|---| +| Hallazgo | Propuesta | +| --------------------------------------------------- | ------------------------------------------------------------------------------ | | F-1: README eidos · F-2: chips de demo desalineados | SYS-4 · realinear chips a unions (con combobox — demos computadas-desde-tipos) | -**Escalan**: SYS-4 · censo D-7.4. · **Veredictos**: *(pendiente checkpoint post-barrido)* +**Escalan**: SYS-4 · censo D-7.4. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/split-button.md b/docs/audit/components/split-button.md index e246abffa..82748f3eb 100644 --- a/docs/audit/components/split-button.md +++ b/docs/audit/components/split-button.md @@ -7,16 +7,16 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 1/3 | Composición | ✓ | Button + DropdownMenu — delegación por composición (verificar línea de razón en fixes, censo "sin línea") | -| 14 | Docs | gap menor | Clasificación formal → SYS-7 | +| # | Dimensión | Estado | Evidencia | +| --- | ----------- | --------- | --------------------------------------------------------------------------------------------------------- | +| 1/3 | Composición | ✓ | Button + DropdownMenu — delegación por composición (verificar línea de razón en fixes, censo "sin línea") | +| 14 | Docs | gap menor | Clasificación formal → SYS-7 | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| -| **F-1** | Clasificación formal | SYS-7 | +| ID | Hallazgo | Propuesta | +| ------- | -------------------- | --------- | +| **F-1** | Clasificación formal | SYS-7 | ## Escalan al sistema @@ -24,4 +24,4 @@ SYS-7 · censo falsedades "morfo —". ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/splitter.md b/docs/audit/components/splitter.md index 044c0ded9..87e6f4ce0 100644 --- a/docs/audit/components/splitter.md +++ b/docs/audit/components/splitter.md @@ -6,16 +6,16 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ con infra-declaración | 3 eventos + pack ✓ funcionando; **F-1**: `expression` sin declarar teniendo pack — misma clase que gradient-builder (SYS-11 d: debería decir `'pack'`; hoy el guard lo tolera por la regla OK-si-pack) | -| 12 | Runtime | candidato D13 | El resize de paneles mide — verificar dom.measure en provider (censo D13) | -| resto | | ✓ | APG ✓; tests ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 3 | Coherencia semántica | ✓ con infra-declaración | 3 eventos + pack ✓ funcionando; **F-1**: `expression` sin declarar teniendo pack — misma clase que gradient-builder (SYS-11 d: debería decir `'pack'`; hoy el guard lo tolera por la regla OK-si-pack) | +| 12 | Runtime | candidato D13 | El resize de paneles mide — verificar dom.measure en provider (censo D13) | +| resto | | ✓ | APG ✓; tests ✓ | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | -------------------------------- | --------------------------------- | | **F-1** | expression ausente (pack existe) | `expression: 'pack'` — SYS-11 (d) | ## Escalan al sistema @@ -24,4 +24,4 @@ SYS-11 (d) (+1: gradient-builder, splitter) · censo D13. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/stack.md b/docs/audit/components/stack.md index 10864bd7b..100117da8 100644 --- a/docs/audit/components/stack.md +++ b/docs/audit/components/stack.md @@ -3,4 +3,4 @@ - **Fecha**: 2026-07-07 · **Familia**: layout · **Método**: solo análisis - **Capas**: morfo ✓ (scope eidos) · README eidos ✓ · **Máquina: PASS limpio**. -— limpio. **Escalan**: —. · **Veredictos**: *(pendiente checkpoint post-barrido)* +— limpio. **Escalan**: —. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/stepper.md b/docs/audit/components/stepper.md index 104912c64..70ddc6abf 100644 --- a/docs/audit/components/stepper.md +++ b/docs/audit/components/stepper.md @@ -6,18 +6,18 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ gradación completa | `shift-step` (:15-18, navegación entre pasos sin intent ✓) + **`commit-complete` FULFILL** (:24-29 — completar el proceso = el fulfill terminal, coherente con range-calendar/gradient-save) ✓; pack ✓ | -| 8 | Contrato morfo | **hallazgo** | **F-1**: sin cita `apg:` (censo = 0) con máquina PASS — TERCER agujero A-1.4 (con image-picker); stepper no tiene patrón APG oficial → el caso exacto para la regla nueva `apg: 'none — rationale'` | -| 10 | Theming | ✓ | Focus outline (o6, triggers de paso) → SYS-5 | -| 12 | Estados | ✓ | done/active/pending; orientación; conectores | -| resto | | ✓ | tests ✓; README ×2 ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 3 | Coherencia semántica | ✓ gradación completa | `shift-step` (:15-18, navegación entre pasos sin intent ✓) + **`commit-complete` FULFILL** (:24-29 — completar el proceso = el fulfill terminal, coherente con range-calendar/gradient-save) ✓; pack ✓ | +| 8 | Contrato morfo | **hallazgo** | **F-1**: sin cita `apg:` (censo = 0) con máquina PASS — TERCER agujero A-1.4 (con image-picker); stepper no tiene patrón APG oficial → el caso exacto para la regla nueva `apg: 'none — rationale'` | +| 10 | Theming | ✓ | Focus outline (o6, triggers de paso) → SYS-5 | +| 12 | Estados | ✓ | done/active/pending; orientación; conectores | +| resto | | ✓ | tests ✓; README ×2 ✓ | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | **F-1** | Sin cita APG y la regla no lo cazó | Con la regla nueva "sin patrón oficial" (media-player/drag-drop/image-picker/stepper) + arreglar A-1.4 del script | ## Escalan al sistema @@ -26,4 +26,4 @@ Censo A-1.4 (+1, agujero ×3) · fulfill de proceso completo (cita CANON). ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/svg.md b/docs/audit/components/svg.md index 586b8a5b7..44a2eae67 100644 --- a/docs/audit/components/svg.md +++ b/docs/audit/components/svg.md @@ -3,4 +3,4 @@ - **Fecha**: 2026-07-07 · **Familia**: display (wrapper svg genérico) · **Método**: solo análisis - **Capas**: morfo ✓ (scope eidos) · README eidos ✓ · **Máquina: PASS limpio**. -— limpio. **Escalan**: —. · **Veredictos**: *(pendiente checkpoint post-barrido)* +— limpio. **Escalan**: —. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/switch.md b/docs/audit/components/switch.md index e92dd654f..bcb4c43f5 100644 --- a/docs/audit/components/switch.md +++ b/docs/audit/components/switch.md @@ -6,17 +6,17 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ **modelo fromProp** | UN `commit-toggle` con `intent: { fromProp: 'intent', default: 'neutral', supported: [neutral,affirm,risk,threat] }` (:40-44) — el consumidor gradúa el peso (el switch "borrar mi cuenta" = threat); satisface el intent-obligatorio de commit por la vía dinámica documentada en memoria (data-color ≠ perceptual-intent). Censo: contraste con el modelo direccional de checkbox | -| 8 | Contrato morfo | ✓ | APG switch (:31); Thumb como parte | -| 10 | Theming | censo | Focus outline → SYS-5; **F-1**: touch-row §37 ◇ (el marker de fila 44px coarse existe para radio, no para switch/checkbox) | -| resto | | ✓ | pack ✓; tests ✓; README ×2 ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ **modelo fromProp** | UN `commit-toggle` con `intent: { fromProp: 'intent', default: 'neutral', supported: [neutral,affirm,risk,threat] }` (:40-44) — el consumidor gradúa el peso (el switch "borrar mi cuenta" = threat); satisface el intent-obligatorio de commit por la vía dinámica documentada en memoria (data-color ≠ perceptual-intent). Censo: contraste con el modelo direccional de checkbox | +| 8 | Contrato morfo | ✓ | APG switch (:31); Thumb como parte | +| 10 | Theming | censo | Focus outline → SYS-5; **F-1**: touch-row §37 ◇ (el marker de fila 44px coarse existe para radio, no para switch/checkbox) | +| resto | | ✓ | pack ✓; tests ✓; README ×2 ✓ | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ---------------------------------- | ---------------------------------------------- | | **F-1** | Touch-row coarse pendiente (§37 ◇) | Patrón label-row con checkbox en fase de fixes | ## Escalan al sistema @@ -25,4 +25,4 @@ Censo touch-row · censo modelos de intent en toggles binarios. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/table.md b/docs/audit/components/table.md index 65c1f1411..a3ee9a32e 100644 --- a/docs/audit/components/table.md +++ b/docs/audit/components/table.md @@ -6,17 +6,17 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ declarado | 3 eventos (commit-set sort presumible + select + expand) con `family-default` ESCRITO — no es hueco (corrección al pass superficial que lo listaba como "sin pack" a secas); si el sort-toggle merece señal sutil propia es la pregunta SYS-3 | -| 14 | Docs | gap | Sin README eidos → SYS-4 | -| resto | | ✓ | tests ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ declarado | 3 eventos (commit-set sort presumible + select + expand) con `family-default` ESCRITO — no es hueco (corrección al pass superficial que lo listaba como "sin pack" a secas); si el sort-toggle merece señal sutil propia es la pregunta SYS-3 | +| 14 | Docs | gap | Sin README eidos → SYS-4 | +| resto | | ✓ | tests ✓ | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| -| **F-1** | README eidos ausente | SYS-4 | +| ID | Hallazgo | Propuesta | +| ------- | --------------------------------- | ----------------------------------------------------- | +| **F-1** | README eidos ausente | SYS-4 | | **F-2** | ¿Sort/select con carácter propio? | SYS-3 (default declarado; decidir si asciende a pack) | ## Escalan al sistema @@ -25,4 +25,4 @@ SYS-3 · SYS-4. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/tabs.md b/docs/audit/components/tabs.md index 895579656..7560b9c86 100644 --- a/docs/audit/components/tabs.md +++ b/docs/audit/components/tabs.md @@ -6,8 +6,8 @@ - **Capas**: morfo ✓ · soma (tests: 1 ✓) · README soma ✓ / eidos ✓ · pack sema ✓ · **Máquina: PASS limpio** · 4 variantes (line/surface/pills/segmented, vocabularies ✓; reference §19 corregido en C) · data-motion × orientación = patrón materials R-4.5 ✓. - Pendiente conocido de §38: tabs quedó fuera del rollout state-layer con nota (bespoke) — evaluar su adopción en el veredicto de fixes. -| Hallazgo | Propuesta | -|---|---| +| Hallazgo | Propuesta | +| -------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | F-1: hover bespoke (◇ documentado §38) | Adoptar state-layer para el tier neutro al ejecutar fixes (o razón anotada) — re-pasar tras el WIP del usuario | -**Escalan**: censo §38 pendientes. · **Veredictos**: *(pendiente checkpoint post-barrido; re-visitar tras WIP)* +**Escalan**: censo §38 pendientes. · **Veredictos**: _(pendiente checkpoint post-barrido; re-visitar tras WIP)_ diff --git a/docs/audit/components/tag-group.md b/docs/audit/components/tag-group.md index 7c20578be..3462ac4e3 100644 --- a/docs/audit/components/tag-group.md +++ b/docs/audit/components/tag-group.md @@ -6,10 +6,10 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ | 4 eventos + pack ✓ (selección/remove de tags con carácter) | -| resto | | ✓ | tests ✓; README ×2 ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | ------ | ---------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ | 4 eventos + pack ✓ (selección/remove de tags con carácter) | +| resto | | ✓ | tests ✓; README ×2 ✓ | ## Hallazgos y propuestas @@ -21,4 +21,4 @@ SYS-5 (censo focus). ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/tags-input.md b/docs/audit/components/tags-input.md index 65edca4fb..c2389899a 100644 --- a/docs/audit/components/tags-input.md +++ b/docs/audit/components/tags-input.md @@ -6,32 +6,32 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 1 | Composición | ✓ | Control/Input/Item(+Text/DeleteTrigger)/ClearTrigger compositivos; Item requiere `index`+`value` (types :60-66 — identidad por índice, estilo Ark) → nota de censo: convención de identidad de items del catálogo | -| 2 | Naming | **3 datos de heterogeneidad** | `value: string[]` + `onValueChange` ✓ + **`inputValue` bindable** (doble valor — types :14-16); **`onValueInvalid`** (:43-44) vs `onInvalid` de color/date-field — dos nombres para el rechazo; **`validate: (v)=>boolean`** (:39-40) vs el validador que DEVUELVE MENSAJE de date/color (`string[]\|string\|void`) — tercera firma de validador → filas en _naming | -| 3 | Coherencia semántica | ✓ con nota de vocabulario | 4 eventos: `commit-set-add` (affirm) · **`signal-warn-reject`** untilFix + persistent-trace + clearTarget (libro §6.2 — TERCER contrato de referencia de la familia, :26-44) · `commit-remove` · `commit-reset` ✓ tiene reset. Nota: añadir usa verbo `set` (+qualifier `-add`) mientras quitar usa verbo propio `remove` — asimetría de verbos a revisar contra el CANON | -| 4 | API | ✓ | `max`, `delimiter`, `addOnPaste`, `blurBehavior: 'add'\|'clear'`, `allowDuplicates` — completa y documentada | -| 5 | Tokens | ✓ | Sin duplicación de alturas; chips vía recipe | -| 6 | Sema | ✓ | Pack ✓ + eventos ricos | -| 7 | Animaciones | ✓ | Sin keyframes | -| 8 | Contrato morfo | **F-1 CERRADO 2026-08-25** | **F-1**: `apg: grid` (:9) pero Provider `role='listbox'` (:73) **Y** Control `role='listbox'` (:88) **Y** Input `role='combobox'` + `aria-autocomplete='list'` + `activedescendant` (:104-113) — tres patrones mezclados, y **dos listbox anidados** (Provider⊃Control con el mismo rol) es ARIA inválida; además combobox exige popup que no existe (misma clase que search-field F-1). **Cerrado** por la firma «el árbol ARIA de `tags-input` habla UN patrón» — ver la nota fechada bajo «Hallazgos y propuestas» | -| 9 | A11y | condicionada a F-1 | Textos remove/clear localizados ✓; activedescendant hacia item ✓ — pero el árbol de roles necesita el veredicto de F-1 antes de validar el conjunto | -| 10 | Theming | ✓ | Focus o7 (input+chips) → censo SYS-5; state-hover 1 tier neutro ✓ | -| 11 | Composición interna | verificar | Types NO mencionan OR-merge con Field (a diferencia de sus hermanos) — ¿participa de Field.Provider? verificar provider en fase de fixes; si no, gap de familia | -| 12 | Estados | ✓ | empty/disabled/readonly/invalid + focus en Control | -| 13 | Runtime | ✓ a nivel leído | Sin señales de timers/DOM crudo en lo leído | -| 14 | Docs+tests | ✓ | README ×2, suite ✓ | -| 15 | i18n | ✓ | texts ×3 | +| # | Dimensión | Estado | Evidencia | +| --- | -------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Composición | ✓ | Control/Input/Item(+Text/DeleteTrigger)/ClearTrigger compositivos; Item requiere `index`+`value` (types :60-66 — identidad por índice, estilo Ark) → nota de censo: convención de identidad de items del catálogo | +| 2 | Naming | **3 datos de heterogeneidad** | `value: string[]` + `onValueChange` ✓ + **`inputValue` bindable** (doble valor — types :14-16); **`onValueInvalid`** (:43-44) vs `onInvalid` de color/date-field — dos nombres para el rechazo; **`validate: (v)=>boolean`** (:39-40) vs el validador que DEVUELVE MENSAJE de date/color (`string[]\|string\|void`) — tercera firma de validador → filas en \_naming | +| 3 | Coherencia semántica | ✓ con nota de vocabulario | 4 eventos: `commit-set-add` (affirm) · **`signal-warn-reject`** untilFix + persistent-trace + clearTarget (libro §6.2 — TERCER contrato de referencia de la familia, :26-44) · `commit-remove` · `commit-reset` ✓ tiene reset. Nota: añadir usa verbo `set` (+qualifier `-add`) mientras quitar usa verbo propio `remove` — asimetría de verbos a revisar contra el CANON | +| 4 | API | ✓ | `max`, `delimiter`, `addOnPaste`, `blurBehavior: 'add'\|'clear'`, `allowDuplicates` — completa y documentada | +| 5 | Tokens | ✓ | Sin duplicación de alturas; chips vía recipe | +| 6 | Sema | ✓ | Pack ✓ + eventos ricos | +| 7 | Animaciones | ✓ | Sin keyframes | +| 8 | Contrato morfo | **F-1 CERRADO 2026-08-25** | **F-1**: `apg: grid` (:9) pero Provider `role='listbox'` (:73) **Y** Control `role='listbox'` (:88) **Y** Input `role='combobox'` + `aria-autocomplete='list'` + `activedescendant` (:104-113) — tres patrones mezclados, y **dos listbox anidados** (Provider⊃Control con el mismo rol) es ARIA inválida; además combobox exige popup que no existe (misma clase que search-field F-1). **Cerrado** por la firma «el árbol ARIA de `tags-input` habla UN patrón» — ver la nota fechada bajo «Hallazgos y propuestas» | +| 9 | A11y | condicionada a F-1 | Textos remove/clear localizados ✓; activedescendant hacia item ✓ — pero el árbol de roles necesita el veredicto de F-1 antes de validar el conjunto | +| 10 | Theming | ✓ | Focus o7 (input+chips) → censo SYS-5; state-hover 1 tier neutro ✓ | +| 11 | Composición interna | verificar | Types NO mencionan OR-merge con Field (a diferencia de sus hermanos) — ¿participa de Field.Provider? verificar provider en fase de fixes; si no, gap de familia | +| 12 | Estados | ✓ | empty/disabled/readonly/invalid + focus en Control | +| 13 | Runtime | ✓ a nivel leído | Sin señales de timers/DOM crudo en lo leído | +| 14 | Docs+tests | ✓ | README ×2, suite ✓ | +| 15 | i18n | ✓ | texts ×3 | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| -| ~~**F-1**~~ | ~~Árbol ARIA mezclado: grid citado, listbox ×2 anidados, combobox sin popup~~ | **CERRADO 2026-08-25** — se eligió el layout grid que el `apg` ya citaba (ver nota abajo) | -| **F-2** | `validate` booleano + `onValueInvalid` divergen de la familia (validador-mensaje + `onInvalid`) | Veredicto _naming: una firma de validador (propuesta: la que devuelve mensaje, con razones tipadas como color-field) y un nombre de callback | -| **F-3** | Verbo `set-add` vs `remove` asimétrico | Revisar contra verbos del CANON en el veredicto de vocabulario | -| **F-4** | Participación en Field sin documentar (¿existe?) | Verificación dirigida; si falta, sumarlo al pass SYS-6 | +| ID | Hallazgo | Propuesta | +| ----------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| ~~**F-1**~~ | ~~Árbol ARIA mezclado: grid citado, listbox ×2 anidados, combobox sin popup~~ | **CERRADO 2026-08-25** — se eligió el layout grid que el `apg` ya citaba (ver nota abajo) | +| **F-2** | `validate` booleano + `onValueInvalid` divergen de la familia (validador-mensaje + `onInvalid`) | Veredicto \_naming: una firma de validador (propuesta: la que devuelve mensaje, con razones tipadas como color-field) y un nombre de callback | +| **F-3** | Verbo `set-add` vs `remove` asimétrico | Revisar contra verbos del CANON en el veredicto de vocabulario | +| **F-4** | Participación en Field sin documentar (¿existe?) | Verificación dirigida; si falta, sumarlo al pass SYS-6 | ### F-1 — CERRADO el 2026-08-25 @@ -39,8 +39,8 @@ Firma «el árbol ARIA de `tags-input` habla UN patrón», estructura **B**. Se eligió UNO: el **layout grid** que el `apg` ya citaba — porque es el patrón que existe justamente para esto («a layout grid can be used to group a set of interactive elements», APG), y el botón de borrar por etiqueta es un -interactivo que un `listbox` no puede alojar (`option` es *Children -Presentational: true*, y el nombre computado lo probaba: `"svelte Eliminar +interactivo que un `listbox` no puede alojar (`option` es _Children +Presentational: true_, y el nombre computado lo probaba: `"svelte Eliminar etiqueta"` — el botón se había fundido con el nombre de la opción). Las tres caras, cerradas juntas: @@ -49,8 +49,8 @@ Las tres caras, cerradas juntas: contenía ninguna opción propia; las dos son hijas DIRECTAS del Control) y el Control pasa a `role='grid'`, de UNA fila. `aria-orientation` se va con el listbox: no es propiedad soportada de `grid`, y Chrome la ignora (medido). -2. **La etiqueta** pasa a `role='gridcell'` — que es *Children Presentational: - false*, así que el botón de borrar deja de fundirse, y que **admite +2. **La etiqueta** pasa a `role='gridcell'` — que es _Children Presentational: + false_, así que el botón de borrar deja de fundirse, y que **admite `aria-selected`**, así que el `stateRef('active')` de la firma del 2026-08-24 sobrevive sin tocarse. No necesita celda propia para el botón: APG contempla el widget dentro de la celda. @@ -69,8 +69,8 @@ píxel y **0 diffs sobre 2496 valores computados**. ## Escalan al sistema -_naming (validadores ×3 firmas · onInvalid vs onValueInvalid · identidad de items por índice) · **F-1 CERRADO 2026-08-25** — el censo de citas APG erróneas sigue vivo con `search-field` (mismo «combobox sin popup») y ahora con `tag-group`, que declara el mismo `apg` y tiene filas SIN celdas · SYS-5. +\_naming (validadores ×3 firmas · onInvalid vs onValueInvalid · identidad de items por índice) · **F-1 CERRADO 2026-08-25** — el censo de citas APG erróneas sigue vivo con `search-field` (mismo «combobox sin popup») y ahora con `tag-group`, que declara el mismo `apg` y tiene filas SIN celdas · SYS-5. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/text.md b/docs/audit/components/text.md index e0056d61b..f0207c501 100644 --- a/docs/audit/components/text.md +++ b/docs/audit/components/text.md @@ -1,10 +1,10 @@ # text — ficha de auditoría de componente - **Fecha**: 2026-07-07 · **Familia**: tipografía (EL modelo de primitiva: size/weight/color expuestos — la doctrina de demos tipográficas nació aquí) · **Método**: solo análisis -- **Capas**: morfo ✓ (scope eidos) · README eidos ✓ · consume named styles ✓ · **Máquina: PASS** con `D-7.4` (*chip drift en demo*). +- **Capas**: morfo ✓ (scope eidos) · README eidos ✓ · consume named styles ✓ · **Máquina: PASS** con `D-7.4` (_chip drift en demo_). -| Hallazgo | Propuesta | -|---|---| +| Hallazgo | Propuesta | +| ------------------------------- | --------------------------------------------------- | | F-1: chips desalineados (D-7.4) | Pass conjunto D-7.4 (chips computados desde unions) | -**Escalan**: censo D-7.4. · **Veredictos**: *(pendiente checkpoint post-barrido)* +**Escalan**: censo D-7.4. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/textarea.md b/docs/audit/components/textarea.md index 84a79cc4d..16f5174d2 100644 --- a/docs/audit/components/textarea.md +++ b/docs/audit/components/textarea.md @@ -6,37 +6,37 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 1 | Composición | ✓ | Input + Count compositivos (morfo :9-13) | -| 2 | Naming | ✓ conforme | `value/onValueChange` ✓; `onSubmit` crudo (3ª ocurrencia del patrón (b) → _naming); `submitOn: 'enter'\|'shift+enter'\|'mod+enter'\|false` (types :5-6, :77-84 — shortcut flexible con semántica de newline documentada); `autosize/minRows/maxRows/rows/resize` | -| 3 | Coherencia semántica | ✓ **referencia** | Doctrina teclear≠commit citada VERBATIM con el incidente (morfo :25-30 — 3ª ocurrencia: canonizada de facto en la familia); **`signal-warn-count-overflow`** = signal.warn + risk con `persistence: 'untilFix'` (libro §6.2 citado), clear vía `clearTarget` al bajar del límite, y `a11ySemantic.requiresPersistentTrace` (:42-66) — segundo contrato de evento de referencia del catálogo (con el caps de password) | -| 4 | API | ✓ | Overflow: el `value` bindable NUNCA excede maxLength (types :86-91); clamp en el elemento para no saltar el cursor (provider :307-310); snippet expone count/max/isOverflow/clear (:11-26) | -| 5 | Tokens | ✓ | Sin duplicación de alturas (multiline n/a); recipe canónico | -| 6 | Sema | ✓ | Pack ✓ + los dos eventos con carácter | -| 7 | Animaciones | ✓ | Sin keyframes | -| 8 | Contrato morfo | ✓ con perla | `data-resize` ENUM con la nota del compilador documentada in-place (sin `values` sería presence-flag y el valor no llegaría a eidos — :84-92); flags autosize/focused/empty ✓; APG textbox correcto | -| 9 | A11y | ✓ | Count con `aria-live: polite` + `data-overflow` (verificado); el overflow anuncia con traza persistente por contrato | -| 10 | Theming | ✓ | Focus outline (censo SYS-5) | -| 11 | Composición interna | ✓ | OR-merge Field (types :33-35); describedBy vía field | -| 12 | Estados | ✓ | resize suprimido bajo autosize (documentado :71-74); overflow como estado proyectado | -| 13 | Runtime | **VIOLACIÓN** | **F-1**: `measureAutosize()` = write-then-read (height→auto :159, scrollHeight :173, getComputedStyle :177) llamado **SÍNCRONO en `oninput`** (:313, tras clamp+commit) y en clear (:238) y mount (:290) — **cero `dom.measure`/raf en el fichero**: reflow forzado POR TECLA con autosize activo — la clase exacta que la doctrina "[Violation] Forced reflow" prohíbe y que `uix.perf` detecta. Además reescribe el `style` attr completo (comentado :349) | -| 14 | Docs+tests | **gap doble** | Sin README eidos; **cero tests** — el contrato de overflow untilFix+clearTarget sin suite que lo proteja | -| 15 | i18n | ✓ | `count: '{{count}} of {{max}}'` interpolado (morfo :21) | +| # | Dimensión | Estado | Evidencia | +| --- | -------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 1 | Composición | ✓ | Input + Count compositivos (morfo :9-13) | +| 2 | Naming | ✓ conforme | `value/onValueChange` ✓; `onSubmit` crudo (3ª ocurrencia del patrón (b) → \_naming); `submitOn: 'enter'\|'shift+enter'\|'mod+enter'\|false` (types :5-6, :77-84 — shortcut flexible con semántica de newline documentada); `autosize/minRows/maxRows/rows/resize` | +| 3 | Coherencia semántica | ✓ **referencia** | Doctrina teclear≠commit citada VERBATIM con el incidente (morfo :25-30 — 3ª ocurrencia: canonizada de facto en la familia); **`signal-warn-count-overflow`** = signal.warn + risk con `persistence: 'untilFix'` (libro §6.2 citado), clear vía `clearTarget` al bajar del límite, y `a11ySemantic.requiresPersistentTrace` (:42-66) — segundo contrato de evento de referencia del catálogo (con el caps de password) | +| 4 | API | ✓ | Overflow: el `value` bindable NUNCA excede maxLength (types :86-91); clamp en el elemento para no saltar el cursor (provider :307-310); snippet expone count/max/isOverflow/clear (:11-26) | +| 5 | Tokens | ✓ | Sin duplicación de alturas (multiline n/a); recipe canónico | +| 6 | Sema | ✓ | Pack ✓ + los dos eventos con carácter | +| 7 | Animaciones | ✓ | Sin keyframes | +| 8 | Contrato morfo | ✓ con perla | `data-resize` ENUM con la nota del compilador documentada in-place (sin `values` sería presence-flag y el valor no llegaría a eidos — :84-92); flags autosize/focused/empty ✓; APG textbox correcto | +| 9 | A11y | ✓ | Count con `aria-live: polite` + `data-overflow` (verificado); el overflow anuncia con traza persistente por contrato | +| 10 | Theming | ✓ | Focus outline (censo SYS-5) | +| 11 | Composición interna | ✓ | OR-merge Field (types :33-35); describedBy vía field | +| 12 | Estados | ✓ | resize suprimido bajo autosize (documentado :71-74); overflow como estado proyectado | +| 13 | Runtime | **VIOLACIÓN** | **F-1**: `measureAutosize()` = write-then-read (height→auto :159, scrollHeight :173, getComputedStyle :177) llamado **SÍNCRONO en `oninput`** (:313, tras clamp+commit) y en clear (:238) y mount (:290) — **cero `dom.measure`/raf en el fichero**: reflow forzado POR TECLA con autosize activo — la clase exacta que la doctrina "[Violation] Forced reflow" prohíbe y que `uix.perf` detecta. Además reescribe el `style` attr completo (comentado :349) | +| 14 | Docs+tests | **gap doble** | Sin README eidos; **cero tests** — el contrato de overflow untilFix+clearTarget sin suite que lo proteja | +| 15 | i18n | ✓ | `count: '{{count}} of {{max}}'` interpolado (morfo :21) | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **F-1** | Autosize mide síncrono en oninput (reflow por tecla) | Diferir a frame coalescido: `dom.measure(() => …, el)` (el vehículo sancionado) — una medición por frame, no por tecla; el write (height:auto) y el read quedan dentro del mismo callback de frame. Verificable con el reflowDetector de uix.perf antes/después | -| **F-2** | Cero tests | Suite: overflow (clamp + signal untilFix + clear al bajar), submitOn ×3 modos, autosize min/max — SYS-2 | -| **F-3** | Sin README eidos | SYS-4 | -| **F-4** | `onSubmit` crudo | Veredicto _naming | +| **F-2** | Cero tests | Suite: overflow (clamp + signal untilFix + clear al bajar), submitOn ×3 modos, autosize min/max — SYS-2 | +| **F-3** | Sin README eidos | SYS-4 | +| **F-4** | `onSubmit` crudo | Veredicto \_naming | ## Escalan al sistema -**Dimensión 13 gana su primer censo real**: buscar la clase write-then-read-síncrono en el resto (scroll-area, cropper, splitter, float-panel, virtual-\* son los candidatos naturales) · SYS-2 · SYS-4 · _naming. +**Dimensión 13 gana su primer censo real**: buscar la clase write-then-read-síncrono en el resto (scroll-area, cropper, splitter, float-panel, virtual-\* son los candidatos naturales) · SYS-2 · SYS-4 · \_naming. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/time-field.md b/docs/audit/components/time-field.md index 80efa61a5..153908fd9 100644 --- a/docs/audit/components/time-field.md +++ b/docs/audit/components/time-field.md @@ -6,32 +6,32 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 1 | Composición | ✓ | Partes compositivas (Provider/Input/Segment/Label/HiddenInput); snippet `segments` ordenado (types :20-22) | -| 2 | Naming | ✓ conforme | `value/onValueChange` + `placeholder/onPlaceholderChange` (types :30-36); `validate/onInvalid`, `minValue/maxValue`, `readonlySegments`, `granularity`, `hideTimeZone`, `errorMessageId`, `locale/dir/hourCycle` (:38-67) — fila conforme en [_naming.md](./_naming.md) | -| 3 | Coherencia semántica | **hallazgo de trío** | Solo `commit-set` (morfo :14-25) — **date-field además emite `commit-reset`** al limpiar: vaciar un time-field no produce señal semántica → F-1. `intent: neutral`, `sequence: post` ✓ canon | -| 4 | API | ✓ | Espejo exacto de date-field (sin `kind`, correcto: no aplica a horas); bindables declarados; fallbacks reactivos documentados (types :59-67) | -| 5 | Tokens | ✓ con dup | Recipe canónico; `height-{k}` re-declara el bundle (SYS-6, evidencia matriz familia) | -| 6 | Sema | declarado, cuestionado | `family-default` EXPLÍCITO en morfo — el trío roto es elección declarada, no olvido; la pregunta SYS-3 pasa a ser: ¿debe el trío compartir UNA expression (los tres pack o los tres default)? El provider usa `targetOverride` en el trigger (:436 ✓ el fix de partes doble-registradas) | -| 7 | Animaciones | ✓ | Sin keyframes; transición solo-background heredada del patrón de familia | -| 8 | Contrato morfo | ✓ más rico que su hermano | APG spinbutton ✓; teclado 5 teclas (verificado — sin Home/End, F-2); **su bloque ARIA del Input es MÁS completo que el de date-field**: añade `aria-readonly` ariaBoolean + `aria-invalid` condicional (morfo :76-90) que date-field no tiene → F-3 (unificar el trío al bloque rico) | -| 9 | A11y | ✓ con F-2 | aria-value\* por segmento, labelledby condicional; falta Home/End | -| 10 | Theming | censo | Focus outline (o2/b1) → SYS-5; segment marker compartido ✓ (state-layer llega) | -| 11 | Composición interna | mandato | Sin wrapper `[data-field]` (SYS-6/B.1 — fue LA sonda en vivo de esta familia); wrapper eidos espejo exacto de date-field (leído) | -| 12 | Estados | ✓ | invalid/disabled/readonly/required + readonlySegments granular; hidden-input forms ✓ | -| 13 | Runtime | ✓ | Sin timers/listeners crudos en lo dirigido; targetOverride ✓ | -| 14 | Docs+tests | ✓ | README ×2, suite ✓ | -| 15 | i18n | ✓ | texts morfo; formatter por locale reactivo; `$libs/days` ✓ | +| # | Dimensión | Estado | Evidencia | +| --- | -------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Composición | ✓ | Partes compositivas (Provider/Input/Segment/Label/HiddenInput); snippet `segments` ordenado (types :20-22) | +| 2 | Naming | ✓ conforme | `value/onValueChange` + `placeholder/onPlaceholderChange` (types :30-36); `validate/onInvalid`, `minValue/maxValue`, `readonlySegments`, `granularity`, `hideTimeZone`, `errorMessageId`, `locale/dir/hourCycle` (:38-67) — fila conforme en [\_naming.md](./_naming.md) | +| 3 | Coherencia semántica | **hallazgo de trío** | Solo `commit-set` (morfo :14-25) — **date-field además emite `commit-reset`** al limpiar: vaciar un time-field no produce señal semántica → F-1. `intent: neutral`, `sequence: post` ✓ canon | +| 4 | API | ✓ | Espejo exacto de date-field (sin `kind`, correcto: no aplica a horas); bindables declarados; fallbacks reactivos documentados (types :59-67) | +| 5 | Tokens | ✓ con dup | Recipe canónico; `height-{k}` re-declara el bundle (SYS-6, evidencia matriz familia) | +| 6 | Sema | declarado, cuestionado | `family-default` EXPLÍCITO en morfo — el trío roto es elección declarada, no olvido; la pregunta SYS-3 pasa a ser: ¿debe el trío compartir UNA expression (los tres pack o los tres default)? El provider usa `targetOverride` en el trigger (:436 ✓ el fix de partes doble-registradas) | +| 7 | Animaciones | ✓ | Sin keyframes; transición solo-background heredada del patrón de familia | +| 8 | Contrato morfo | ✓ más rico que su hermano | APG spinbutton ✓; teclado 5 teclas (verificado — sin Home/End, F-2); **su bloque ARIA del Input es MÁS completo que el de date-field**: añade `aria-readonly` ariaBoolean + `aria-invalid` condicional (morfo :76-90) que date-field no tiene → F-3 (unificar el trío al bloque rico) | +| 9 | A11y | ✓ con F-2 | aria-value\* por segmento, labelledby condicional; falta Home/End | +| 10 | Theming | censo | Focus outline (o2/b1) → SYS-5; segment marker compartido ✓ (state-layer llega) | +| 11 | Composición interna | mandato | Sin wrapper `[data-field]` (SYS-6/B.1 — fue LA sonda en vivo de esta familia); wrapper eidos espejo exacto de date-field (leído) | +| 12 | Estados | ✓ | invalid/disabled/readonly/required + readonlySegments granular; hidden-input forms ✓ | +| 13 | Runtime | ✓ | Sin timers/listeners crudos en lo dirigido; targetOverride ✓ | +| 14 | Docs+tests | ✓ | README ×2, suite ✓ | +| 15 | i18n | ✓ | texts morfo; formatter por locale reactivo; `$libs/days` ✓ | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | | **F-1** | Falta `commit-reset` (date-field lo tiene; limpiar no suena) | Añadir el evento al morfo + wiring en provider (espejo de date-field) — como parte del veredicto de trío SYS-3 | -| **F-2** | Sin Home/End (APG spinbutton) | Pass de familia de segmentos (con date/color) | -| **F-3** | ARIA del Input asimétrico en el trío (este es el RICO) | Unificar: date-field adopta aria-readonly/aria-invalid de time-field | -| **F-4** | `height-{k}` duplicadas | SYS-6 | +| **F-2** | Sin Home/End (APG spinbutton) | Pass de familia de segmentos (con date/color) | +| **F-3** | ARIA del Input asimétrico en el trío (este es el RICO) | Unificar: date-field adopta aria-readonly/aria-invalid de time-field | +| **F-4** | `height-{k}` duplicadas | SYS-6 | ## Escalan al sistema @@ -39,4 +39,4 @@ SYS-3 **matizado con evidencia nueva**: el trío declara expressions distintas a ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/time-picker.md b/docs/audit/components/time-picker.md index 784ec6a08..1f54fac22 100644 --- a/docs/audit/components/time-picker.md +++ b/docs/audit/components/time-picker.md @@ -7,20 +7,20 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 1 | Composición | ✓ | Time-field + Popover + reloj de sliders (HourSlider/MinuteSlider/SecondSlider :160-192 — el patrón Knob.ValueField documentado en memoria de construcción); Clock + HourScale como partes de layout | -| 3 | Coherencia semántica | ✓ patrón canónico + gap heredado | `expression: "delegated"` (:15) + `commit-reset` propio neutral (:33-39) — idéntico a date-picker ✓ SYS-11 conforme; **F-1**: la cadena delegada aterriza en time-field SIN pack → el picker entero suena a default (vs date-picker tuneado) — SYS-3 en su forma más audible | -| 8 | Contrato morfo | ✓ | scope completo `['soma','sema','eidos']` (:13) + apg citado | -| 11 | Composición interna | ✓ con duplicación | Matemática del trigger re-derivada localmente (`--_time-field-height − space-2`, censada en Fase 1) → SYS-6 | -| resto | | ✓ | tests ✓; README ×2 ✓; patrón picker correcto | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Composición | ✓ | Time-field + Popover + reloj de sliders (HourSlider/MinuteSlider/SecondSlider :160-192 — el patrón Knob.ValueField documentado en memoria de construcción); Clock + HourScale como partes de layout | +| 3 | Coherencia semántica | ✓ patrón canónico + gap heredado | `expression: "delegated"` (:15) + `commit-reset` propio neutral (:33-39) — idéntico a date-picker ✓ SYS-11 conforme; **F-1**: la cadena delegada aterriza en time-field SIN pack → el picker entero suena a default (vs date-picker tuneado) — SYS-3 en su forma más audible | +| 8 | Contrato morfo | ✓ | scope completo `['soma','sema','eidos']` (:13) + apg citado | +| 11 | Composición interna | ✓ con duplicación | Matemática del trigger re-derivada localmente (`--_time-field-height − space-2`, censada en Fase 1) → SYS-6 | +| resto | | ✓ | tests ✓; README ×2 ✓; patrón picker correcto | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ----------------------------------------------------------------- | ---------------------------------------------------------------- | | **F-1** | Asimetría sonora vs date-picker (herencia de time-field sin pack) | SYS-3 — el veredicto del trío debe cubrir pickers explícitamente | -| **F-2** | Matemática de trigger duplicada | SYS-6 (token de familia elimina la re-derivación) | +| **F-2** | Matemática de trigger duplicada | SYS-6 (token de familia elimina la re-derivación) | ## Escalan al sistema @@ -28,4 +28,4 @@ SYS-3 · SYS-6 · SYS-11 (tercer ejemplo positivo de `delegated`). ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/time-range-field.md b/docs/audit/components/time-range-field.md index eee917c09..892d36979 100644 --- a/docs/audit/components/time-range-field.md +++ b/docs/audit/components/time-range-field.md @@ -6,26 +6,26 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 1 | Composición | ✓ | Mismo patrón del gemelo: cada Input = TimeField completo por endpoint; 0 eventos propios = delegación by-design | -| 2 | Naming | ✓ | `value: TimeRange` + `onValueChange` + `onStartValueChange`/`onEndValueChange` — coherente con date-range ✓ | -| 3 | Coherencia semántica | condicionada + cuestión de diseño | Delegada a time-field (que SÍ tiene commit-set ✓, 1 evento)… **pero time-field no tiene pack** → este range entero suena a default crudo mientras date-range suena tuneado: la asimetría SYS-3 en su forma más visible. Comparte con el gemelo la falta de evento rango-completo (F-1 conjunta) | -| 8 | Contrato morfo | **gap menor** | **F-2**: SIN cita `apg:` — su gemelo cita spinbutton y time-field single también; una línea de asimetría documental | -| 12 | Estados | **hallazgo doble (máquina)** | R-1.3 readonly Y **R-1.4 invalid** sin materializar — el único de la familia donde INVALID tampoco pinta: un rango inválido (end < start) valida en soma pero NO SE VE → F-3 prioritario | -| 14 | Docs | gap | Sin README eidos (E-2.3) | -| 4-7, 9-11, 13, 15 | | heredan del single | Wrapper de composición | +| # | Dimensión | Estado | Evidencia | +| ----------------- | -------------------- | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Composición | ✓ | Mismo patrón del gemelo: cada Input = TimeField completo por endpoint; 0 eventos propios = delegación by-design | +| 2 | Naming | ✓ | `value: TimeRange` + `onValueChange` + `onStartValueChange`/`onEndValueChange` — coherente con date-range ✓ | +| 3 | Coherencia semántica | condicionada + cuestión de diseño | Delegada a time-field (que SÍ tiene commit-set ✓, 1 evento)… **pero time-field no tiene pack** → este range entero suena a default crudo mientras date-range suena tuneado: la asimetría SYS-3 en su forma más visible. Comparte con el gemelo la falta de evento rango-completo (F-1 conjunta) | +| 8 | Contrato morfo | **gap menor** | **F-2**: SIN cita `apg:` — su gemelo cita spinbutton y time-field single también; una línea de asimetría documental | +| 12 | Estados | **hallazgo doble (máquina)** | R-1.3 readonly Y **R-1.4 invalid** sin materializar — el único de la familia donde INVALID tampoco pinta: un rango inválido (end < start) valida en soma pero NO SE VE → F-3 prioritario | +| 14 | Docs | gap | Sin README eidos (E-2.3) | +| 4-7, 9-11, 13, 15 | | heredan del single | Wrapper de composición | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| -| **F-1** | Sin evento de rango-completo (compartida con date-range F-1) | Misma decisión, mismo fix para ambos ranges | -| **F-2** | Sin cita APG (gemelo y single la tienen) | Añadir `apg: spinbutton` — una línea; verificar por qué el guard F-1.x no lo cazó | -| **F-3** | `data-invalid` sin estilo (R-1.4) — la validación de rango existe y no pinta | Materializar invalid (border `--{c}-border-invalid` como el gemelo) — y a largo plazo vía composición Field (SYS-6) | -| **F-4** | readonly sin estilo (R-1.3) | Censo de familia ×4 | -| **F-5** | README eidos ausente | SYS-4 | -| **F-6** | Compuesto SIN `expression` declarada (canon `'delegated'` existe; primos pickers lo usan) | Añadir `expression: 'delegated'` — SYS-11, mismo pase que el gemelo | +| ID | Hallazgo | Propuesta | +| ------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| **F-1** | Sin evento de rango-completo (compartida con date-range F-1) | Misma decisión, mismo fix para ambos ranges | +| **F-2** | Sin cita APG (gemelo y single la tienen) | Añadir `apg: spinbutton` — una línea; verificar por qué el guard F-1.x no lo cazó | +| **F-3** | `data-invalid` sin estilo (R-1.4) — la validación de rango existe y no pinta | Materializar invalid (border `--{c}-border-invalid` como el gemelo) — y a largo plazo vía composición Field (SYS-6) | +| **F-4** | readonly sin estilo (R-1.3) | Censo de familia ×4 | +| **F-5** | README eidos ausente | SYS-4 | +| **F-6** | Compuesto SIN `expression` declarada (canon `'delegated'` existe; primos pickers lo usan) | Añadir `expression: 'delegated'` — SYS-11, mismo pase que el gemelo | ## Escalan al sistema @@ -33,4 +33,4 @@ SYS-3 (el range hace VISIBLE la asimetría del trío: gemelos que suenan distint ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/time-range-picker.md b/docs/audit/components/time-range-picker.md index fe331ed12..b6777aa39 100644 --- a/docs/audit/components/time-range-picker.md +++ b/docs/audit/components/time-range-picker.md @@ -7,19 +7,19 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 1 | Composición | ✓ | Espejo de time-picker con time-range-field; apg ×2 (compuesto citado — lo que time-range-FIELD no hace, su F-2) | -| 3 | Coherencia semántica | ✓ patrón + gap heredado ×2 | `expression: "delegated"` (:18) + `commit-reset` neutral propio (:39-45) ✓; **F-1**: hereda de time-field sin pack Y de time-range-field sin expression — la cadena sonora más cruda del censo SYS-3, y además su field delegante ni declara la delegación (time-range-field F-6) | -| 11 | Composición interna | ✓ con duplicación | Trigger re-derivado (patrón picker) → SYS-6 | -| resto | | ✓ | tests ✓; README ×2 ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Composición | ✓ | Espejo de time-picker con time-range-field; apg ×2 (compuesto citado — lo que time-range-FIELD no hace, su F-2) | +| 3 | Coherencia semántica | ✓ patrón + gap heredado ×2 | `expression: "delegated"` (:18) + `commit-reset` neutral propio (:39-45) ✓; **F-1**: hereda de time-field sin pack Y de time-range-field sin expression — la cadena sonora más cruda del censo SYS-3, y además su field delegante ni declara la delegación (time-range-field F-6) | +| 11 | Composición interna | ✓ con duplicación | Trigger re-derivado (patrón picker) → SYS-6 | +| resto | | ✓ | tests ✓; README ×2 ✓ | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------- | | **F-1** | Cadena sonora cruda completa (time-field sin pack → range-field sin expression → picker delegado) | SYS-3 + el fix SYS-11 de los field-ranges | -| **F-2** | Trigger re-derivado | SYS-6 | +| **F-2** | Trigger re-derivado | SYS-6 | ## Escalan al sistema @@ -27,4 +27,4 @@ SYS-3 · SYS-6 · SYS-11. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/timeline.md b/docs/audit/components/timeline.md index 9fe17d7c7..8c8c0a1a4 100644 --- a/docs/audit/components/timeline.md +++ b/docs/audit/components/timeline.md @@ -2,12 +2,12 @@ - **Fecha**: 2026-07-07 · **Familia**: data · **Método**: solo análisis - **Capas**: morfo ✓ · soma (tests: 1 ✓) · README soma ✓ / eidos ✓ · pack sema ✓ · patrón materials (data-live gating) documentado en R-4.5 ✓ -- **Máquina**: **NEEDS-WORK** — `F-1.1`–`F-1.4` (*expediente pre-flight ausente*) · `R-2.7` (*tipografía literal ×2: `font-size: 1em` sin token ni anotación*). +- **Máquina**: **NEEDS-WORK** — `F-1.1`–`F-1.4` (_expediente pre-flight ausente_) · `R-2.7` (_tipografía literal ×2: `font-size: 1em` sin token ni anotación_). - **⚠ WIP del usuario** (morfo timeline.ts modificado en working tree) — read-only. -| Hallazgo | Propuesta | -|---|---| +| Hallazgo | Propuesta | +| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | | F-1: `font-size: 1em` ×2 sin anotar (R-2.7) | `1em` = herencia proporcional deliberada casi seguro → anotar `/* literal: inherit-proportional */` (o token si no) | -| F-2: expediente ausente | Escribir las 4 secciones | +| F-2: expediente ausente | Escribir las 4 secciones | -**Escalan**: SYS-9 (expediente). · **Veredictos**: *(pendiente checkpoint post-barrido; re-visitar tras WIP)* +**Escalan**: SYS-9 (expediente). · **Veredictos**: _(pendiente checkpoint post-barrido; re-visitar tras WIP)_ diff --git a/docs/audit/components/toast.md b/docs/audit/components/toast.md index 81f1af099..620828917 100644 --- a/docs/audit/components/toast.md +++ b/docs/audit/components/toast.md @@ -6,17 +6,17 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ **la familia signal bien usada** | `signal-announce` con `fromProp: 'intent'` (:35-40) — la notificación ES un signal (no un commit) y el intent viene del consumidor (toast de error=risk, de éxito=fulfill) ✓ ejemplar; + present/dismiss; pack ✓ | -| 9 | A11y | ✓ | aria-live regions ✓; auto-dismiss vía uix.timers ✓ (disciplina runtime) | -| 10 | Theming | hallazgo | **F-1**: depth sin estampar (0) — flota en `--z-index-overlay-toast` sin plano: bajo cristal no se esmerila | -| resto | | ✓ | Banda z propia sobre FloatPanel ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ **la familia signal bien usada** | `signal-announce` con `fromProp: 'intent'` (:35-40) — la notificación ES un signal (no un commit) y el intent viene del consumidor (toast de error=risk, de éxito=fulfill) ✓ ejemplar; + present/dismiss; pack ✓ | +| 9 | A11y | ✓ | aria-live regions ✓; auto-dismiss vía uix.timers ✓ (disciplina runtime) | +| 10 | Theming | hallazgo | **F-1**: depth sin estampar (0) — flota en `--z-index-overlay-toast` sin plano: bajo cristal no se esmerila | +| resto | | ✓ | Banda z propia sobre FloatPanel ✓ | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ---------------- | --------------------------------------------------------------- | | **F-1** | Sin `data-depth` | Pass conjunto de depth (con tooltip/float-panel/banner-overlay) | ## Escalan al sistema @@ -25,4 +25,4 @@ Censo depth · cita signal-announce+fromProp para el CANON (la notificación par ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/toggle-group.md b/docs/audit/components/toggle-group.md index 9dff7b46c..8bc1c1902 100644 --- a/docs/audit/components/toggle-group.md +++ b/docs/audit/components/toggle-group.md @@ -7,11 +7,11 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ **la cardinalidad EN el contrato** | `commit-toggle` fromProp (:24-30, modelo switch/toggle ✓ coherente) + **`commit-block` con intent `risk`** (:42-47) — el rechazo por grupo lleno ES un evento semántico declarado (la decisión whenFull 2026-06-27 materializada en morfo, no solo en soma) ✓ ejemplar | -| 8 | Contrato morfo | ✓ | APG; Item :72 | -| resto | | ✓ | pack ✓; tests ✓; README ×2 ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ **la cardinalidad EN el contrato** | `commit-toggle` fromProp (:24-30, modelo switch/toggle ✓ coherente) + **`commit-block` con intent `risk`** (:42-47) — el rechazo por grupo lleno ES un evento semántico declarado (la decisión whenFull 2026-06-27 materializada en morfo, no solo en soma) ✓ ejemplar | +| 8 | Contrato morfo | ✓ | APG; Item :72 | +| resto | | ✓ | pack ✓; tests ✓; README ×2 ✓ | ## Hallazgos y propuestas @@ -23,4 +23,4 @@ Limpio. `commit-block` risk = cita para el CANON (cómo un límite estructural s ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/toggle.md b/docs/audit/components/toggle.md index ab6bd8e4e..37ceb9b70 100644 --- a/docs/audit/components/toggle.md +++ b/docs/audit/components/toggle.md @@ -7,18 +7,18 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ razonada in-place | `commit-toggle` fromProp como switch (:44-48) + la razón del sequence ESCRITA (:40-42): "the perceptual closure belongs AFTER the structural state flip — **celebrate the new state, not the gesture**" — doctrina citable | -| 8 | Contrato morfo | ✓ | APG button (:32 — toggle button, correcto: pressed, no switch) | -| 2 | Naming | hallazgo SYS-1 | **F-1**: 1 hook-clase en su CSS → censo SYS-1 | -| 10 | Theming | ✓ | `--radius-default` ancla el radio en reposo ✓ (consumidor del knob A.10) | -| resto | | ✓ | pack ✓; tests ✓; README ×2 ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ razonada in-place | `commit-toggle` fromProp como switch (:44-48) + la razón del sequence ESCRITA (:40-42): "the perceptual closure belongs AFTER the structural state flip — **celebrate the new state, not the gesture**" — doctrina citable | +| 8 | Contrato morfo | ✓ | APG button (:32 — toggle button, correcto: pressed, no switch) | +| 2 | Naming | hallazgo SYS-1 | **F-1**: 1 hook-clase en su CSS → censo SYS-1 | +| 10 | Theming | ✓ | `--radius-default` ancla el radio en reposo ✓ (consumidor del knob A.10) | +| resto | | ✓ | pack ✓; tests ✓; README ×2 ✓ | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ------------ | ----------------------- | | **F-1** | 1 hook-clase | Codemod del censo SYS-1 | ## Escalan al sistema @@ -27,4 +27,4 @@ SYS-1 (censo) · la cita ":40-42" para el capítulo de sequence del CANON. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/toolbar.md b/docs/audit/components/toolbar.md index fcf2b3aa8..8d1929f78 100644 --- a/docs/audit/components/toolbar.md +++ b/docs/audit/components/toolbar.md @@ -14,4 +14,4 @@ Contrato sano: 1 evento + pack + APG toolbar con roving. Sin hallazgos en las ca ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/tooltip.md b/docs/audit/components/tooltip.md index 8e8bae3e9..45b8238d6 100644 --- a/docs/audit/components/tooltip.md +++ b/docs/audit/components/tooltip.md @@ -6,17 +6,17 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ + dato censo | TRES verbos emerge: open/close/**dismiss** (:29-60 — el único con la tripleta; presumible open=hover-in, close=hover-out, dismiss=Escape) — dato clave para el censo de verbos: si la distinción close/dismiss es deliberada aquí, la matriz existe y los demás overlays la infra-usan | -| 7 | Animaciones | ✓ | Preset `delayed-open` (alias del canal motion) ✓ | -| 10 | Theming | **hallazgo** | **F-1**: depth SIN estampar (0) — el único superviviente del censo Tier-A: flota en `--z-index-overlay-tooltip` sin declarar plano → un tema cristal no lo esmerila | -| resto | | ✓ | pack ✓; tests ✓ | +| # | Dimensión | Estado | Evidencia | +| ----- | -------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ + dato censo | TRES verbos emerge: open/close/**dismiss** (:29-60 — el único con la tripleta; presumible open=hover-in, close=hover-out, dismiss=Escape) — dato clave para el censo de verbos: si la distinción close/dismiss es deliberada aquí, la matriz existe y los demás overlays la infra-usan | +| 7 | Animaciones | ✓ | Preset `delayed-open` (alias del canal motion) ✓ | +| 10 | Theming | **hallazgo** | **F-1**: depth SIN estampar (0) — el único superviviente del censo Tier-A: flota en `--z-index-overlay-tooltip` sin declarar plano → un tema cristal no lo esmerila | +| resto | | ✓ | pack ✓; tests ✓ | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ---------------- | -------------------------------------------------------------------------------------------------------------------- | | **F-1** | Sin `data-depth` | Estampar plano (overlay) en su content — cierra el censo Tier-A (pass conjunto con toast/float-panel/banner-overlay) | ## Escalan al sistema @@ -25,4 +25,4 @@ Censo depth (Tier-A) · censo verbos emerge (el dato de la tripleta). ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/trans.md b/docs/audit/components/trans.md index b17d8f859..0dc31c0b6 100644 --- a/docs/audit/components/trans.md +++ b/docs/audit/components/trans.md @@ -2,11 +2,11 @@ - **Fecha**: 2026-07-07 · **Familia**: service components (i18n; sprint 2026-05-25) · **Método**: solo análisis - **Capas**: morfo — (servicio render-less) · soma — · README eidos ✓ · pack — -- **Máquina**: **NEEDS-WORK** — `F-1.1`/`F-1.4`/`F-1.5` (*expediente pre-flight + justificación de pasivo ausentes*). +- **Máquina**: **NEEDS-WORK** — `F-1.1`/`F-1.4`/`F-1.5` (_expediente pre-flight + justificación de pasivo ausentes_). -| Hallazgo | Propuesta | -|---|---| -| F-1: expediente + `## Passive justification` | Escribir en un pass conjunto de los 4 service components (trans, format-date, format-number, relative-time): son la MISMA clase (render-less, sin morfo por naturaleza) — la justificación puede ser compartida y citada | -| Nota: la doctrina de demos con locale propio (chip picker) les aplica ✓ | Verificar en el pass | +| Hallazgo | Propuesta | +| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| F-1: expediente + `## Passive justification` | Escribir en un pass conjunto de los 4 service components (trans, format-date, format-number, relative-time): son la MISMA clase (render-less, sin morfo por naturaleza) — la justificación puede ser compartida y citada | +| Nota: la doctrina de demos con locale propio (chip picker) les aplica ✓ | Verificar en el pass | -**Escalan**: SYS-7 (clase "service component" formal). · **Veredictos**: *(pendiente checkpoint post-barrido)* +**Escalan**: SYS-7 (clase "service component" formal). · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/tree-grid.md b/docs/audit/components/tree-grid.md index cb824a1e8..d64029744 100644 --- a/docs/audit/components/tree-grid.md +++ b/docs/audit/components/tree-grid.md @@ -6,17 +6,17 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ | Gemelo de tree-view CON pack ✓ (el único par tree con carácter) | -| 12 | Estados | hallazgo máquina | readonly sin materializar (R-1.3) | -| 14 | Docs | gap | Sin README eidos | +| # | Dimensión | Estado | Evidencia | +| --- | -------------------- | ---------------- | --------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ | Gemelo de tree-view CON pack ✓ (el único par tree con carácter) | +| 12 | Estados | hallazgo máquina | readonly sin materializar (R-1.3) | +| 14 | Docs | gap | Sin README eidos | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| -| **F-1** | README eidos | SYS-4 | +| ID | Hallazgo | Propuesta | +| ------- | ---------------- | ------------------------- | +| **F-1** | README eidos | SYS-4 | | **F-2** | readonly (R-1.3) | Censo — tratamiento único | ## Escalan al sistema @@ -25,4 +25,4 @@ SYS-4 · censo R-1.3. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/tree-view.md b/docs/audit/components/tree-view.md index 65412650b..8e3421c0a 100644 --- a/docs/audit/components/tree-view.md +++ b/docs/audit/components/tree-view.md @@ -6,17 +6,17 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ | commit-select + **emerge** expand/collapse (verificado: la rama que se abre es emerge, como collapsible — NO shift; corrijo mi propio borrador que lo asumió) + pack ✓ — dato para el censo emerge: la familia cubre disclosure además de flotantes, con TRES pares de verbos (open/close · present/dismiss · expand/collapse) | -| 8 | Contrato morfo | ✓ | APG treeview citado | -| 14 | Docs | gap | Sin README eidos → SYS-4 | +| # | Dimensión | Estado | Evidencia | +| --- | -------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 3 | Coherencia semántica | ✓ | commit-select + **emerge** expand/collapse (verificado: la rama que se abre es emerge, como collapsible — NO shift; corrijo mi propio borrador que lo asumió) + pack ✓ — dato para el censo emerge: la familia cubre disclosure además de flotantes, con TRES pares de verbos (open/close · present/dismiss · expand/collapse) | +| 8 | Contrato morfo | ✓ | APG treeview citado | +| 14 | Docs | gap | Sin README eidos → SYS-4 | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| -| **F-1** | README eidos ausente | SYS-4 | +| ID | Hallazgo | Propuesta | +| ------- | -------------------- | --------- | +| **F-1** | README eidos ausente | SYS-4 | ## Escalan al sistema @@ -24,4 +24,4 @@ SYS-4. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/virtual-grid.md b/docs/audit/components/virtual-grid.md index 766a8dc83..c71ee4cf3 100644 --- a/docs/audit/components/virtual-grid.md +++ b/docs/audit/components/virtual-grid.md @@ -6,16 +6,16 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ declarado | 4 eventos (scroll ejes ×2 presumible + navigate + set) family-default; apg ✓ | -| 12 | Runtime | candidato D13 | Con virtual-list | -| 14 | Docs | gap | Sin README eidos | +| # | Dimensión | Estado | Evidencia | +| --- | -------------------- | ------------- | ---------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ declarado | 4 eventos (scroll ejes ×2 presumible + navigate + set) family-default; apg ✓ | +| 12 | Runtime | candidato D13 | Con virtual-list | +| 14 | Docs | gap | Sin README eidos | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ------------ | -------------------------------------- | | **F-1** | README eidos | SYS-4 (pass conjunto con virtual-list) | ## Escalan al sistema @@ -24,4 +24,4 @@ SYS-4 · censo D13. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/virtual-list.md b/docs/audit/components/virtual-list.md index b258f330a..cb8638eca 100644 --- a/docs/audit/components/virtual-list.md +++ b/docs/audit/components/virtual-list.md @@ -7,19 +7,19 @@ ## Dimensiones (evidencia leída) -| # | Dimensión | Estado | Evidencia | -|---|---|---|---| -| 3 | Coherencia semántica | ✓ declarado | shift scroll/navigate + commit-set con family-default ESCRITO; su gemelo virtual-grid declara apg y este no — la asimetría de gemelos otra vez (como date-range/time-range) | -| 8 | Contrato morfo | hallazgo | **F-1**: sin APG (A-1.4 — cazado por la máquina esta vez); virtualización no tiene patrón oficial → caso "apg: none — rationale" | -| 12 | Runtime | candidato D13 | Virtualización mide viewport/scroll — verificar `dom.measure` en provider (censo D13) | -| 14 | Docs | gap | Sin README eidos | +| # | Dimensión | Estado | Evidencia | +| --- | -------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 3 | Coherencia semántica | ✓ declarado | shift scroll/navigate + commit-set con family-default ESCRITO; su gemelo virtual-grid declara apg y este no — la asimetría de gemelos otra vez (como date-range/time-range) | +| 8 | Contrato morfo | hallazgo | **F-1**: sin APG (A-1.4 — cazado por la máquina esta vez); virtualización no tiene patrón oficial → caso "apg: none — rationale" | +| 12 | Runtime | candidato D13 | Virtualización mide viewport/scroll — verificar `dom.measure` en provider (censo D13) | +| 14 | Docs | gap | Sin README eidos | ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| -| **F-1** | Sin APG | Regla "none — rationale" (pass con virtual-grid: los gemelos igualados) | -| **F-2** | README eidos | SYS-4 | +| ID | Hallazgo | Propuesta | +| ------- | ------------ | ----------------------------------------------------------------------- | +| **F-1** | Sin APG | Regla "none — rationale" (pass con virtual-grid: los gemelos igualados) | +| **F-2** | README eidos | SYS-4 | ## Escalan al sistema @@ -27,4 +27,4 @@ SYS-4 · censo A-1.4 · censo D13 (candidato) · asimetría de gemelos. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/components/wrap.md b/docs/audit/components/wrap.md index 2b880ebfa..71cbb6e25 100644 --- a/docs/audit/components/wrap.md +++ b/docs/audit/components/wrap.md @@ -3,4 +3,4 @@ - **Fecha**: 2026-07-07 · **Familia**: layout · **Método**: solo análisis - **Capas**: morfo ✓ (scope eidos) · README eidos ✓ · **Máquina: PASS limpio**. -— limpio. **Escalan**: —. · **Veredictos**: *(pendiente checkpoint post-barrido)* +— limpio. **Escalan**: —. · **Veredictos**: _(pendiente checkpoint post-barrido)_ diff --git a/docs/audit/components/year-grid.md b/docs/audit/components/year-grid.md index b52883dda..f66355c50 100644 --- a/docs/audit/components/year-grid.md +++ b/docs/audit/components/year-grid.md @@ -15,11 +15,11 @@ cero tests. ## Hallazgos y propuestas -| ID | Hallazgo | Propuesta | -|---|---|---| +| ID | Hallazgo | Propuesta | +| ------- | ------------------------------------------------- | ------------------------- | | **F-1** | = month-grid F-1 (`role='application'` sin razón) | Decisión única de familia | -| **F-2** | = month-grid F-2 (marcas muertas) | SYS-8 | -| **F-3** | = month-grid F-3 (cero tests) | SYS-2 | +| **F-2** | = month-grid F-2 (marcas muertas) | SYS-8 | +| **F-3** | = month-grid F-3 (cero tests) | SYS-2 | ## Escalan al sistema @@ -27,4 +27,4 @@ SYS-2 · SYS-3 · SYS-8 · censo `role='application'`. ## Veredictos -*(pendiente — checkpoint post-barrido)* +_(pendiente — checkpoint post-barrido)_ diff --git a/docs/audit/theming-audit.md b/docs/audit/theming-audit.md index ddce2ae06..96226f414 100644 --- a/docs/audit/theming-audit.md +++ b/docs/audit/theming-audit.md @@ -2,7 +2,7 @@ - **Estado**: CERRADA (2026-07-05 → 2026-07-07). Todos los ítems del checkpoint ejecutados bajo veredicto de usuario, con guard y verificación por ítem. - **Reemplaza**: el informe de Fase 1 anterior, invalidado por contaminación metodológica (ancló en auditorías previas, invirtió la jerarquía doc↔código, decidió valores unilateralmente). Este informe se derivó de cero. -- **Método**: clean-room — entradas prohibidas `docs/old-deprecated/**`, informes previos de `docs/audit/**`, `tmp/component-audit.md`; sin agentes; evidencia primaria (lectura directa + greps reproducibles + sondas de navegador). **La norma es la decisión de diseño documentada y fechada** (changelog/RFC/canon); el código discrepante es *deriva*, y las fechas arbitran. Ningún valor se decidió sin veredicto explícito del usuario. +- **Método**: clean-room — entradas prohibidas `docs/old-deprecated/**`, informes previos de `docs/audit/**`, `tmp/component-audit.md`; sin agentes; evidencia primaria (lectura directa + greps reproducibles + sondas de navegador). **La norma es la decisión de diseño documentada y fechada** (changelog/RFC/canon); el código discrepante es _deriva_, y las fechas arbitran. Ningún valor se decidió sin veredicto explícito del usuario. - **Desviación sancionada del plan**: el plan original era solo-informe. En el checkpoint (F) el usuario redirigió a **resolver ítem a ítem** — análisis (implicaciones + referencias de frameworks + coherencia del ecosistema) → veredicto → ejecución con nota fechada + guard + verificación. Este informe documenta hallazgos **y** su resolución. - **Detalle canónico**: cada ejecución dejó su crónica en [`docs/theming/changelog.md`](../theming/changelog.md) §39–§41 y notas fechadas en secciones/RFCs previos. Este informe **enlaza, no copia**. @@ -13,10 +13,10 @@ El sistema quedó, tras la ejecución, en el estado que su propia doctrina promete: **una fuente por concepto, todo eje como dato del config, contrato ≡ emisión, y cada arreglo sistémico con su guard**. Los cinco hallazgos de mayor calado, todos ejecutados: 1. **El contrato CSS era un segundo censo a mano y había derivado 211 tokens** (familias enteras — depth, named styles, scaling, breakpoints, banda overlay-z — invisibles para `setCssVariables` estricto y para el esqueleto de temas CSS-only). Hoy el contrato **se deriva de la propia emisión** (census-as-data) con muro bidireccional en suite. §2-A.9. -2. **Dieciséis alias token-level** (puentes de la migración air→eidos, 2026-05-14) mantenían una segunda gramática — incluida una cadena de TRES nombres para la tipografía de UI y un par duplicado de focus con ambos nombres adoptados (36 vs 13 refs). Doctrina de usuario: *"no quiero alias"* → migración value-preserving (~207 refs) y muerte de todos. §2-A.9. +2. **Dieciséis alias token-level** (puentes de la migración air→eidos, 2026-05-14) mantenían una segunda gramática — incluida una cadena de TRES nombres para la tipografía de UI y un par duplicado de focus con ambos nombres adoptados (36 vs 13 refs). Doctrina de usuario: _"no quiero alias"_ → migración value-preserving (~207 refs) y muerte de todos. §2-A.9. 3. **El pick de contraste on-solid vivía dos veces**: computado en el bucle de roles y como **lista curada a mano** en la cascada per-instance — y la lista había derivado: `color="orange"` embarcaba tinta blanca a **2.97:1 (sub-AA)**. Hoy hay **un criterio único computado** (`pickOnSolid`, blanco-preferente salvo fallo de AMBOS suelos APCA≥60 y WCAG≥3) consumido por ambos caminos + test de paridad. §2-A.7. -4. **Knobs canónicos cocidos fuera del sistema de datos** (state-layer en `archetypes.css`; radius-factor/default, inset-ring, floating-gaps como constantes de emisor). Doctrina de usuario: *"todo tiene que ser tematizable"* → promovidos a `primitives.{state,floating,radiusFactor,radiusDefault}` + `border.insetRingWidth`, valores verbatim. §2-A.10. -5. **La clase eager-freeze**: tokens de `:root` referenciando privados `--_*` de CSS de componente se congelaban *guaranteed-invalid* — las marcas de festivo/evento del calendario **nunca pintaron**, las alturas de segmento computaron `auto` desde su nacimiento, y un slot inexistente (`--color-content-tertiary`) heredó color un mes. Resueltos con veredictos + **dos guards** (G1 scope-para-privados, G2 referencias-fantasma contra el contrato derivado). §2-B. +4. **Knobs canónicos cocidos fuera del sistema de datos** (state-layer en `archetypes.css`; radius-factor/default, inset-ring, floating-gaps como constantes de emisor). Doctrina de usuario: _"todo tiene que ser tematizable"_ → promovidos a `primitives.{state,floating,radiusFactor,radiusDefault}` + `border.insetRingWidth`, valores verbatim. §2-A.10. +5. **La clase eager-freeze**: tokens de `:root` referenciando privados `--_*` de CSS de componente se congelaban _guaranteed-invalid_ — las marcas de festivo/evento del calendario **nunca pintaron**, las alturas de segmento computaron `auto` desde su nacimiento, y un slot inexistente (`--color-content-tertiary`) heredó color un mes. Resueltos con veredictos + **dos guards** (G1 scope-para-privados, G2 referencias-fantasma contra el contrato derivado). §2-B. Métricas finales: contrato **4.540 nombres** (3.383 static + 1.157 theme), **emisión ≡ contrato en ambas direcciones** (por construcción + guard), solo **31 paths `(derived)`** (el resto con procedencia real de config). Suites eidos **299/299** · `check` 59 (baseline intocada) · `component:audit` 86/47/1 (baseline) · `docs:check` 0 errores. @@ -47,14 +47,14 @@ Formato: clasificación · severidad · evidencia · norma · veredicto → ejec ### A.4/A.5 — Canon temporal de sema: cita falsa + doble tabla de holds (DR·SE) -- **Evidencia**: `holds.ts` citaba "cap. 24 §6.2 *verbatim*" — esa sección es otra cosa; **el libro da regiones cualitativas, no ms** (c4 §13 · c12 §4-9 · TABLA 32.1/32.2). Dos tablas de holds (holds.ts y sema-map.ts) podían discrepar. El hold actuaba de tijera sobre la expresión visual. -- **Veredicto**: **el libro es el canon**; los ms son materialización del framework; peldaño intermedio `settled: 400`; *"el hold es suelo, no tijera"*. +- **Evidencia**: `holds.ts` citaba "cap. 24 §6.2 _verbatim_" — esa sección es otra cosa; **el libro da regiones cualitativas, no ms** (c4 §13 · c12 §4-9 · TABLA 32.1/32.2). Dos tablas de holds (holds.ts y sema-map.ts) podían discrepar. El hold actuaba de tijera sobre la expresión visual. +- **Veredicto**: **el libro es el canon**; los ms son materialización del framework; peldaño intermedio `settled: 400`; _"el hold es suelo, no tijera"_. - **Ejecución**: citas reales; `SEMA_HOLDS_BY_INTENT` = única tabla (resolver + canal visual la consumen; la columna de sema-map murió); `commit.fulfill → settled`, `signal.loss → brief`; `awaitExpression` espera el fin de la animación tras el hold con techo absoluto `MAX_EXPRESSION_WAIT_MS = 1500` (nunca derivado del hold — el "2×hold" propuesto se rechazó por re-acoplar presupuestos); firmas announce retuneadas a regiones del libro. - **Guard**: triple (granularidad fulfill 400 · suelo-no-tijera · cap con timers por fases) + **design-lint**: toda firma ≤ cap. Sema 178/178. `book-deviations.md` D.12 (candidata editorial ApD). ### A.6 — Depth Decisión 8 (ratificación, docs-only) -El plano pinta el **bundle de APARIENCIA** (surface·border·shadow+halo·tipografía on-surface); **z JAMÁS se pinta** — *"el plano pinta, el posicionador posiciona"*. rfc-depth §5 + changelog §29 Adopción-v2. (La cobertura Tier-A de estampado `data-depth` → auditoría de componentes.) +El plano pinta el **bundle de APARIENCIA** (surface·border·shadow+halo·tipografía on-surface); **z JAMÁS se pinta** — _"el plano pinta, el posicionador posiciona"_. rfc-depth §5 + changelog §29 Adopción-v2. (La cobertura Tier-A de estampado `data-depth` → auditoría de componentes.) ### A.7 — On-solid: lista curada vs cómputo (SE — accesibilidad) @@ -65,31 +65,31 @@ El plano pinta el **bundle de APARIENCIA** (surface·border·shadow+halo·tipogr ### A.8 — Dos idiomas dimensionales: px/py vs logical properties (RC) - **Evidencia**: 198 claves `p[xy]` (+3 `my`) evadían R-4.4; 20 shorthands físicos. -- **Veredicto** (literal): *"normalizar el ecosistema de una puta vez… aunque haya que reescribir todo"*; exclusión words/palabras/chronos LEVANTADA para el codemod. +- **Veredicto** (literal): _"normalizar el ecosistema de una puta vez… aunque haya que reescribir todo"_; exclusión words/palabras/chronos LEVANTADA para el codemod. - **Ejecución + guard**: codemod atómico 57 ficheros (~570 nombres) + longhands lógicos; R-4.4 ampliado a segmentos abreviados (error, sin allowlist); **muro de tipos** `defineRecipes` (clave física no compila). Before/after idéntico en navegador. recipe-contract §1 nota. ### A.9 — Alias token-level + contrato-censo derivado (RC·DR) — el mayor - **Evidencia**: (a) 211 tokens emitidos fuera de `getCssContract()` (sonda reproducible: parse de declaraciones `--x:` de la emisión vs el census a mano) — `setCssVariables` estricto LANZABA sobre vocabulario legítimo; (b) 16 alias (función literal `appendTransitionAliasDeclarations`, nacidos `6e8ced2e` 2026-05-14): `--font-ui` (cadena de 3 nombres, 94 refs), `--font-mono` (64), `--color-focus-ring` vs `--focus-ring-color` (13 vs 36 — ambos vivos), `--text-{1..6}-*` (que además **evadía la regla §5 del bundle**, como px/py evadía R-4.4), `--radius-xs` (step inexistente), etc. -- **Veredictos**: P1 directa (contrato derivado); *"no quiero alias"*; font-ui muere → `--style-label-font-family` + **validación**: `styles.label.family` es requisito; los knobs `(derived)` se promueven en lote con A.10. +- **Veredictos**: P1 directa (contrato derivado); _"no quiero alias"_; font-ui muere → `--style-label-font-family` + **validación**: `styles.label.family` es requisito; los knobs `(derived)` se promueven en lote con A.10. - **Ejecución**: codemod ~207 refs (con reversión selectiva del **vocabulario local** del dev-site — `layout.css` usaba `--font-sans/--font-mono` como hook propio: renombrarlos habría re-tipografiado los componentes del site); [`lib/contract.ts`](../../src/uix/eidos/lib/contract.ts) reescrito — **parsea la emisión real** (static + temas `scales:'all'`; tema sintético para configs CSS-only), metadatos por tabla de reglas (una familia sin regla entra igual: degrada metadatos, nunca cobertura), WeakMap cache; 4 tokens `field-*` compartidos: par adoptado → recipe `field`; par hover **superseded por el state-layer** con 0 consumidores → eliminado con veredicto explícito. - **Guards**: muro bidireccional (emisión-pública ≡ contrato, ambas direcciones) + pins de familias nuevas + pins de los 16 alias muertos. Changelog **§39** (crónica completa). ### A.10 — Knobs cocidos en emisor → config (RC) - **Evidencia**: `--state-{hover,press,selected}` hardcoded en `archetypes.css :root` (8%/12%/12% — los números de M3, sobre `currentColor`); `--radius-factor: '1'`, `--radius-default: md`, `--ring-inset-width: medium`, `--floating-gap-{menu,panel}` como constantes de render-css. Ninguno expresable en config, todos fuera de validación/serialización. -- **Veredicto** (literal): *"todo tiene que ser tematizable"* — a la capa primitives, defaults verbatim. +- **Veredicto** (literal): _"todo tiene que ser tematizable"_ — a la capa primitives, defaults verbatim. - **Ejecución + guard**: `primitives.state` + `radiusFactor` + `radiusDefault` + `border.insetRingWidth` + `primitives.floating`; el bloque de archetypes.css **murió** (las reglas quedan); validación (porcentajes, membresías); test de **retune** (hover 4%, factor 1.25…); +5 pins al muro. Cero cambio visual (verificado). Changelog **§40**. ### B — Fantasmas value-changing: la clase eager-freeze (SE ×3) -Mecánica común: custom property de `:root` referenciando `--_*` de CSS de componente → congela *guaranteed-invalid* → herencia congelada (punto ciego del TSC: su inferencia solo ve deps públicas). +Mecánica común: custom property de `:root` referenciando `--_*` de CSS de componente → congela _guaranteed-invalid_ → herencia congelada (punto ciego del TSC: su inferencia solo ve deps públicas). -| Fantasma | Realidad shipped | Veredicto → ejecución | -| --- | --- | --- | -| `--calendar-{day-holiday,event}-shadow` | **nunca pintaron** | *"las features deben existir"* → `scope:'host'` (dato TSC): **pintan por primera vez** (verificado + captura). Préstamos cross-component (range-calendar 79 tokens de calendar, month/year-grid 65, drp 38) → veredicto de diseño: **la familia calendar se formaliza como capa compartida/arquetipo** (chronos beberá de ella) — registrado. | -| `--{date,time,color}-field-segment-height` | `auto` desde su nacimiento | Veredicto de diseño: la altura sale del **eje size a nivel familia field** (`--field-control-height-{k}`, hoy re-duplicada por cada x-field) — **mandatado**: los componentes deben incorporar el wrapper Field (sondado: los segmentos NO viven bajo `[data-field]`). Mientras: 3 tokens rotos + 4 consumos eliminados (cero cambio visual, verificado 20px→20px). | -| `image-adjustments` `value-color: var(--color-content-tertiary)` | slot **inexistente** — heredó color desde `7da7285d` (2026-06-11) | Corrección de usuario: *"tertiary era un color de ACENTO"* (mezcló namespace content con nombre de ROL) → `var(--color-tertiary-text)`; el read-out pinta el acento (verificado). | +| Fantasma | Realidad shipped | Veredicto → ejecución | +| ---------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--calendar-{day-holiday,event}-shadow` | **nunca pintaron** | _"las features deben existir"_ → `scope:'host'` (dato TSC): **pintan por primera vez** (verificado + captura). Préstamos cross-component (range-calendar 79 tokens de calendar, month/year-grid 65, drp 38) → veredicto de diseño: **la familia calendar se formaliza como capa compartida/arquetipo** (chronos beberá de ella) — registrado. | +| `--{date,time,color}-field-segment-height` | `auto` desde su nacimiento | Veredicto de diseño: la altura sale del **eje size a nivel familia field** (`--field-control-height-{k}`, hoy re-duplicada por cada x-field) — **mandatado**: los componentes deben incorporar el wrapper Field (sondado: los segmentos NO viven bajo `[data-field]`). Mientras: 3 tokens rotos + 4 consumos eliminados (cero cambio visual, verificado 20px→20px). | +| `image-adjustments` `value-color: var(--color-content-tertiary)` | slot **inexistente** — heredó color desde `7da7285d` (2026-06-11) | Corrección de usuario: _"tertiary era un color de ACENTO"_ (mezcló namespace content con nombre de ROL) → `var(--color-tertiary-text)`; el read-out pinta el acento (verificado). | - **Guards**: **G1** — referencia a `--_*` en valor de recipe exige scope que la cubra (nunca `:root`); **G2** — toda referencia pública sin fallback debe existir en el contrato derivado (`--color-content-tertiary` habría roto el build el día que se escribió). Changelog **§41**. @@ -101,12 +101,12 @@ Arbitradas por fechas/código y corregidas al doc con nota fechada. Las de más ## 3. Censo de tokens — estado final -| Clase | Antes | Después | -| --- | --- | --- | -| **Duplicados** (dos nombres, un concepto) | 16 alias + par focus duplicado + doble censo (contrato) + doble tabla de holds | **0** — un nombre por concepto; contrato y holds con fuente única | +| Clase | Antes | Después | +| ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| **Duplicados** (dos nombres, un concepto) | 16 alias + par focus duplicado + doble censo (contrato) + doble tabla de holds | **0** — un nombre por concepto; contrato y holds con fuente única | | **Repetitivos** (patrón que la gramática debería factorizar) | `height-{xs..xl}` re-declarada por cada x-field; alturas de segmento ×3; vocabulario calendar prestado ×5 componentes (~250 refs) | Registrados como **diseño de familia** para la auditoría de componentes (field-family + calendar-surface/arquetipo) | -| **Contradictorios** | on-solid lista vs cómputo; holds ×2; §36 gap 0 vs space-1; 13 vs 12 slots; … | **1 vivo** → decisión D-1 (0.96 vs 0.97) | -| **Fuera de gramática** | `--state-*`/`--floating-gap*` sin casa en config; `p[xy]` ×198; `text-N` numérica paralela | **0 sistémicos** | +| **Contradictorios** | on-solid lista vs cómputo; holds ×2; §36 gap 0 vs space-1; 13 vs 12 slots; … | **1 vivo** → decisión D-1 (0.96 vs 0.97) | +| **Fuera de gramática** | `--state-*`/`--floating-gap*` sin casa en config; `p[xy]` ×198; `text-N` numérica paralela | **0 sistémicos** | ## 4. Matriz de ejes ortogonales — post-ejecución @@ -136,8 +136,8 @@ Todo §2, con crónica en changelog §23–§41 (notas fechadas), RFCs corregido ### 5.4 Guardas añadidas (para que nada reaparezca) -Participación de scaling (flip test) · container↔breakpoint refs · styles-never-redeclare óptica · triple guard temporal sema + design-lint firmas≤cap · paridad on-solid rol↔instancia + pin del set computado · R-4.4 ampliado (segmentos físicos, error) + muro de tipos `defineRecipes` · **muro bidireccional emisión≡contrato** + pins (familias nuevas, alias muertos, state/floating) · validación `styles.label.family` + porcentajes state + membresías radiusDefault/insetRingWidth · test de retune de knobs · **G1** (privados exigen scope) · **G2** (referencias fantasma vs contrato). Doctrina cumplida: *cada fix sistémico se empareja con un guard* — y tres de ellos pararon errores míos durante esta misma auditoría. +Participación de scaling (flip test) · container↔breakpoint refs · styles-never-redeclare óptica · triple guard temporal sema + design-lint firmas≤cap · paridad on-solid rol↔instancia + pin del set computado · R-4.4 ampliado (segmentos físicos, error) + muro de tipos `defineRecipes` · **muro bidireccional emisión≡contrato** + pins (familias nuevas, alias muertos, state/floating) · validación `styles.label.family` + porcentajes state + membresías radiusDefault/insetRingWidth · test de retune de knobs · **G1** (privados exigen scope) · **G2** (referencias fantasma vs contrato). Doctrina cumplida: _cada fix sistémico se empareja con un guard_ — y tres de ellos pararon errores míos durante esta misma auditoría. --- -*Auditoría Fase 1 cerrada 2026-07-07. Siguiente fase natural: la auditoría de componentes (§5.3-3), que cobra la adopción de los ejes que este pass dejó canónicos, contratados y guardados.* +_Auditoría Fase 1 cerrada 2026-07-07. Siguiente fase natural: la auditoría de componentes (§5.3-3), que cobra la adopción de los ejes que este pass dejó canónicos, contratados y guardados._ diff --git a/docs/authoring.md b/docs/authoring.md index 3390ae147..c4c4340cd 100644 --- a/docs/authoring.md +++ b/docs/authoring.md @@ -11,7 +11,7 @@ status: current How to create and edit documentation in this repo. These rules exist because the corpus is meant to read as a coherent, drift-free reference (and a future book) — not as an accreting pile of notes. Read [`docs/README.md`](./README.md) first for -the map; this file is the *how-to-write* layer. +the map; this file is the _how-to-write_ layer. The rules are not aesthetic. Each one fixes a failure that actually happened during the corpus migration. @@ -28,7 +28,7 @@ copy. three docs for weeks after the canon moved to 8, because each doc had re-transcribed the list instead of linking it. -The doctrine fixes the *doctrine*; the code fixes the *numbers*. When a value +The doctrine fixes the _doctrine_; the code fixes the _numbers_. When a value lives in code (per-family holds, channel signatures, full verb lists), **link the `file:symbol`**, don't snapshot it into prose. @@ -37,15 +37,15 @@ the `file:symbol`**, don't snapshot it into prose. Decide the stratum before you write; it decides where the file goes and how timeless it must read. -| Stratum | Kind of doc | Lives in | -| --- | --- | --- | -| **E0 — orientation** | entry point, glossary | `docs/README.md`, `docs/glossary.md` | -| **E1 — architecture** | how the layers fit | `docs/architecture/` (the book chapters) + in-place stubs | -| **E2 — canon** | fixed vocabulary / contracts | `docs/CANON.md`, [`docs/canon/tsc.md`](./canon/tsc.md) | -| **E3 — decisions / RFC** | why a thing is built this way | `docs/decisions.md` + the RFC / design files | -| **E4 — guides** | how to do a thing | `guides/component-guide.md`, `theming/guide.md` | -| **E5 — module reference** | per-artifact docs | `src/arts/{name}/README.md`, `src/packs/{name}/README.md` (in-place) | -| **process** | hand-offs, snapshots, audits | `docs/process/` — ephemeral, never a source of truth | +| Stratum | Kind of doc | Lives in | +| ------------------------- | ----------------------------- | -------------------------------------------------------------------- | +| **E0 — orientation** | entry point, glossary | `docs/README.md`, `docs/glossary.md` | +| **E1 — architecture** | how the layers fit | `docs/architecture/` (the book chapters) + in-place stubs | +| **E2 — canon** | fixed vocabulary / contracts | `docs/CANON.md`, [`docs/canon/tsc.md`](./canon/tsc.md) | +| **E3 — decisions / RFC** | why a thing is built this way | `docs/decisions.md` + the RFC / design files | +| **E4 — guides** | how to do a thing | `guides/component-guide.md`, `theming/guide.md` | +| **E5 — module reference** | per-artifact docs | `src/arts/{name}/README.md`, `src/packs/{name}/README.md` (in-place) | +| **process** | hand-offs, snapshots, audits | `docs/process/` — ephemeral, never a source of truth | Layer and module reference stay **in-place** next to the code. Cross-cutting orientation, canon, decisions and process live under `docs/`. diff --git a/docs/book-map.md b/docs/book-map.md index 54f9301c8..ef1f509fe 100644 --- a/docs/book-map.md +++ b/docs/book-map.md @@ -7,70 +7,70 @@ la TABLA D.1 del propio libro (subconjunto citado por el código). ## Anclas -| Ancla | Destino | Fija | -|---|---|---| -| BK-INTERFAZ | Capítulo 1 | la interfaz como algo que ocurre | -| BK-QWERTY | Capítulo 2 | carga histórica y dependencia del camino | -| BK-PERCEPCION | Capítulo 3 | lo que la percepción ya sabía | -| BK-EVENTO | Capítulo 4 | qué cuenta como evento | -| BK-GRAMATICA | Capítulo 5 | por qué una gramática | -| BK-INT-SEMANTICA | Capítulo 6 | la interacción semántica como unidad | -| BK-CRITERIOS | Capítulo 7 | criterios para que una diferencia sea lenguaje | -| BK-FAMILIAS | Capítulo 8 | las ocho familias | -| BK-EVALUABILIDAD | Capítulo 9 | evaluable frente a estructural | -| BK-A1-EXCEPCION | Capítulo 9, §9 | la aparición-que-advierte y el test sustractivo | -| BK-INTENTS | Capítulo 10 | los seis intents | -| BK-THREAT-LOSS | Capítulo 10 | amenaza frente a pérdida | -| BK-SEMAFORO | Capítulo 10 | crítica del semáforo heredado | -| BK-CANALES | Capítulo 11 | los canales de expresión | -| BK-TIEMPO | Capítulo 12 | gramática temporal | -| BK-DOS-SUPERFICIES | Capítulo 12 | ocurrencia y estado | -| BK-MOTION | Capítulo 13 | la física percibida | -| BK-PRESENCIA | Capítulo 14 | presencia y profundidad | -| BK-FORMA | Capítulo 15 | la forma como canal | -| BK-COLOR | Capítulo 16 | color: refuerzo y valencia | -| BK-SONIDO | Capítulo 17 | el canal sonoro | -| BK-SONIDO-MATERIA | Capítulo 17 | nombrar el sonido por su materia | -| BK-HAPTICA | Capítulo 18 | el canal háptico | -| BK-HAPTICA-MATERIA | Capítulo 18 | primitivas hápticas por sensación | -| BK-MIGRACION | Capítulo 19 | migración semántica | -| BK-COORDINACION | Capítulo 20 | varios canales, un solo evento | -| BK-LEER-FAMILIAS | Capítulo 21 | cómo leer los capítulos de familia | -| BK-CONTACT | Capítulo 22 | contact | -| BK-COMMIT | Capítulo 23 | commit | -| BK-SIGNAL | Capítulo 24 | signal | -| BK-HANDLE | Capítulo 25 | handle | -| BK-MUESTREO | Capítulo 25, §5 | muestrear al ritmo de la percepción | -| BK-EMERGE | Capítulo 26 | emerge | -| BK-SHIFT | Capítulo 27 | shift | -| BK-SUSTAIN | Capítulo 28 | sustain | -| BK-SUSTAIN-ESTADO | Capítulo 28, §7 | sustain como estado con transiciones | -| BK-DELEGATE | Capítulo 29 | delegate: quién actúa | -| BK-INTERPRETE | Capítulo 29, §11 | la frontera del intérprete | -| BK-COMPOSICION | Capítulo 30 | orden señal-mutación y dominancia | -| BK-MATRICES | Capítulo 31 | familias × canales, intents × canales | -| BK-RANGOS | Capítulo 32 | rangos orientativos | -| BK-A11Y-APLICADA | Capítulo 33 | accesibilidad aplicada | -| BK-ANTIPATRONES | Capítulo 34 | catálogo de antipatrones | -| BK-VALIDACION | Capítulo 35 | guía de validación | -| BK-SISTEMA | Capítulo 36 | el sistema de diseño | -| BK-SES | Apéndice A | formalización: el Semantic Event Spec | -| BK-REFERENCIA | Apéndice B | tablas canónicas B.1–B.3 | -| BK-PALETA-SONIDO | Apéndice B | paleta sonora inicial (TABLA B.4) | -| BK-PALETA-HAPTICA | Apéndice B | paleta háptica inicial (TABLA B.5) | -| BK-CASOS | Apéndice C | tres casos de estudio | -| BK-SEMA | Apéndice D | la gramática hecha código | -| BK-PRACTICA-CORRIGIO | Apéndice D | lo que la práctica corrigió (A-1 y D.11) | -| BK-D11 | Apéndice D | la desviación emerge/shift del diálogo | -| BK-ANCLAS | Apéndice D | tabla D.1 de anclas | -| BK-STREAMING | Apéndice E, caso 1 | el texto que fluye | -| BK-OPTIMISTA | Apéndice E, caso 2 | la interfaz optimista | -| BK-SKELETON | Apéndice E, caso 3 | la espera con forma de promesa | -| BK-SCROLL | Apéndice E, caso 4 | el campo sin fondo | -| BK-COLAB | Apéndice E, caso 5 | el otro que edita | -| BK-NOTIF-SO | Apéndice E, caso 6 | la notificación que emigra | -| BK-VOZ-XR | Apéndice E, caso 7 | voz y XR | -| BK-ITINERARIOS | Preliminares | cómo recorrer este libro | +| Ancla | Destino | Fija | +| -------------------- | ------------------ | ----------------------------------------------- | +| BK-INTERFAZ | Capítulo 1 | la interfaz como algo que ocurre | +| BK-QWERTY | Capítulo 2 | carga histórica y dependencia del camino | +| BK-PERCEPCION | Capítulo 3 | lo que la percepción ya sabía | +| BK-EVENTO | Capítulo 4 | qué cuenta como evento | +| BK-GRAMATICA | Capítulo 5 | por qué una gramática | +| BK-INT-SEMANTICA | Capítulo 6 | la interacción semántica como unidad | +| BK-CRITERIOS | Capítulo 7 | criterios para que una diferencia sea lenguaje | +| BK-FAMILIAS | Capítulo 8 | las ocho familias | +| BK-EVALUABILIDAD | Capítulo 9 | evaluable frente a estructural | +| BK-A1-EXCEPCION | Capítulo 9, §9 | la aparición-que-advierte y el test sustractivo | +| BK-INTENTS | Capítulo 10 | los seis intents | +| BK-THREAT-LOSS | Capítulo 10 | amenaza frente a pérdida | +| BK-SEMAFORO | Capítulo 10 | crítica del semáforo heredado | +| BK-CANALES | Capítulo 11 | los canales de expresión | +| BK-TIEMPO | Capítulo 12 | gramática temporal | +| BK-DOS-SUPERFICIES | Capítulo 12 | ocurrencia y estado | +| BK-MOTION | Capítulo 13 | la física percibida | +| BK-PRESENCIA | Capítulo 14 | presencia y profundidad | +| BK-FORMA | Capítulo 15 | la forma como canal | +| BK-COLOR | Capítulo 16 | color: refuerzo y valencia | +| BK-SONIDO | Capítulo 17 | el canal sonoro | +| BK-SONIDO-MATERIA | Capítulo 17 | nombrar el sonido por su materia | +| BK-HAPTICA | Capítulo 18 | el canal háptico | +| BK-HAPTICA-MATERIA | Capítulo 18 | primitivas hápticas por sensación | +| BK-MIGRACION | Capítulo 19 | migración semántica | +| BK-COORDINACION | Capítulo 20 | varios canales, un solo evento | +| BK-LEER-FAMILIAS | Capítulo 21 | cómo leer los capítulos de familia | +| BK-CONTACT | Capítulo 22 | contact | +| BK-COMMIT | Capítulo 23 | commit | +| BK-SIGNAL | Capítulo 24 | signal | +| BK-HANDLE | Capítulo 25 | handle | +| BK-MUESTREO | Capítulo 25, §5 | muestrear al ritmo de la percepción | +| BK-EMERGE | Capítulo 26 | emerge | +| BK-SHIFT | Capítulo 27 | shift | +| BK-SUSTAIN | Capítulo 28 | sustain | +| BK-SUSTAIN-ESTADO | Capítulo 28, §7 | sustain como estado con transiciones | +| BK-DELEGATE | Capítulo 29 | delegate: quién actúa | +| BK-INTERPRETE | Capítulo 29, §11 | la frontera del intérprete | +| BK-COMPOSICION | Capítulo 30 | orden señal-mutación y dominancia | +| BK-MATRICES | Capítulo 31 | familias × canales, intents × canales | +| BK-RANGOS | Capítulo 32 | rangos orientativos | +| BK-A11Y-APLICADA | Capítulo 33 | accesibilidad aplicada | +| BK-ANTIPATRONES | Capítulo 34 | catálogo de antipatrones | +| BK-VALIDACION | Capítulo 35 | guía de validación | +| BK-SISTEMA | Capítulo 36 | el sistema de diseño | +| BK-SES | Apéndice A | formalización: el Semantic Event Spec | +| BK-REFERENCIA | Apéndice B | tablas canónicas B.1–B.3 | +| BK-PALETA-SONIDO | Apéndice B | paleta sonora inicial (TABLA B.4) | +| BK-PALETA-HAPTICA | Apéndice B | paleta háptica inicial (TABLA B.5) | +| BK-CASOS | Apéndice C | tres casos de estudio | +| BK-SEMA | Apéndice D | la gramática hecha código | +| BK-PRACTICA-CORRIGIO | Apéndice D | lo que la práctica corrigió (A-1 y D.11) | +| BK-D11 | Apéndice D | la desviación emerge/shift del diálogo | +| BK-ANCLAS | Apéndice D | tabla D.1 de anclas | +| BK-STREAMING | Apéndice E, caso 1 | el texto que fluye | +| BK-OPTIMISTA | Apéndice E, caso 2 | la interfaz optimista | +| BK-SKELETON | Apéndice E, caso 3 | la espera con forma de promesa | +| BK-SCROLL | Apéndice E, caso 4 | el campo sin fondo | +| BK-COLAB | Apéndice E, caso 5 | el otro que edita | +| BK-NOTIF-SO | Apéndice E, caso 6 | la notificación que emigra | +| BK-VOZ-XR | Apéndice E, caso 7 | voz y XR | +| BK-ITINERARIOS | Preliminares | cómo recorrer este libro | ## Mapa de secciones de esta edición diff --git a/docs/building-a-component.md b/docs/building-a-component.md index 0190b49a6..a931ff320 100644 --- a/docs/building-a-component.md +++ b/docs/building-a-component.md @@ -19,23 +19,23 @@ guard that verifies it. Role note (authoring rule 4 — one source per concern): this doc contains **no content**, only sequencing. If a phase doc and this table disagree about order, -this doc wins on *order*; the phase doc wins on *content*. +this doc wins on _order_; the phase doc wins on _content_. --- ## The route -| Phase | You produce | Read THIS | Verified by | -| --- | --- | --- | --- | -| **0 · Decide** | comparison table vs ark/bits/radix (+ react-aria), membership call (soma or eidos-native), compose-first check | [`component-guide.md`](./guides/component-guide.md) §Before You Start (1–4) · membership: [`architecture/soma.md`](./architecture/soma.md) §2 | reviewer — the table goes in the component README (phase 7) | -| **1 · Morfo** | `src/uix/morfo/components/{kebab}.ts` — parts, data/aria, keyboard, events (family/verb/intent/sequence), `texts`, `expression` | [`morfo/README.md`](./architecture/morfo.md) §Authoring a new morfo · vocabulary: [`CANON.md`](./CANON.md) | `validateMorfo` + `npm run morfo:vocabulary` + audit `A-*` | -| **2 · Soma** | `{kebab}-provider.svelte.ts` + thin wrappers + `types.ts` + provider test | [`component-guide.md`](./guides/component-guide.md) (patterns + rules A1–A37) | provider test + `npm run check` | -| **3 · Sema** | pack `src/uix/sema/components/{kebab}.ts` (or explicit `expression: 'family-default'`) — selectors via `semaSelector` ONLY | [`architecture/sema.md`](./architecture/sema.md) §packs + §typed builder · pack-vs-default criteria: [`book-deviations.md`](./decisions/book-deviations.md) D.4 | `npm run morfo:vocabulary` (expression coverage) | -| **4 · Eidos wrapper** | `eidos/components/{kebab}/` — root + attached parts (option C), visual props | [`eidos/components/README.md`](../src/uix/eidos/components/README.md) (the 7 hard rules) | `component-api-contract` test + audit `E-*` | -| **5 · Recipe + theming** | recipe tokens in `lib/recipes/base.ts` + `{kebab}.css` | [`theming/guide.md`](./theming/guide.md) pasos 1–6 · **mandatory contract**: [`canon/recipe-contract.md`](./canon/recipe-contract.md) · scope (if `data-color`): [`canon/tsc.md`](./canon/tsc.md) | `npm run generate:eidos-css` + `recipe-css-contract` test + audit `R-*` (R-4.x = the contract) | -| **6 · Demo** | `web/routes/uix/components/{kebab}/+page.svelte` — v2 9-tab layout | [`demo-authoring.md`](./guides/demo-authoring.md) | audit `D-*` + `npm run smoke` | -| **7 · Component README** | Baseline · Comparativa (≥3 refs) · Decisiones · Gaps-with-disposition · Sema events table | template = any recent PASS component's README; sections audited | audit `F-*` | -| **8 · Acceptance** | nothing new — the component passes | [`completion-checklist.md`](./guides/completion-checklist.md) | `npm run component:audit --only {kebab}` + `npm run morfo:check` + `eidos-lint` | +| Phase | You produce | Read THIS | Verified by | +| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| **0 · Decide** | comparison table vs ark/bits/radix (+ react-aria), membership call (soma or eidos-native), compose-first check | [`component-guide.md`](./guides/component-guide.md) §Before You Start (1–4) · membership: [`architecture/soma.md`](./architecture/soma.md) §2 | reviewer — the table goes in the component README (phase 7) | +| **1 · Morfo** | `src/uix/morfo/components/{kebab}.ts` — parts, data/aria, keyboard, events (family/verb/intent/sequence), `texts`, `expression` | [`morfo/README.md`](./architecture/morfo.md) §Authoring a new morfo · vocabulary: [`CANON.md`](./CANON.md) | `validateMorfo` + `npm run morfo:vocabulary` + audit `A-*` | +| **2 · Soma** | `{kebab}-provider.svelte.ts` + thin wrappers + `types.ts` + provider test | [`component-guide.md`](./guides/component-guide.md) (patterns + rules A1–A37) | provider test + `npm run check` | +| **3 · Sema** | pack `src/uix/sema/components/{kebab}.ts` (or explicit `expression: 'family-default'`) — selectors via `semaSelector` ONLY | [`architecture/sema.md`](./architecture/sema.md) §packs + §typed builder · pack-vs-default criteria: [`book-deviations.md`](./decisions/book-deviations.md) D.4 | `npm run morfo:vocabulary` (expression coverage) | +| **4 · Eidos wrapper** | `eidos/components/{kebab}/` — root + attached parts (option C), visual props | [`eidos/components/README.md`](../src/uix/eidos/components/README.md) (the 7 hard rules) | `component-api-contract` test + audit `E-*` | +| **5 · Recipe + theming** | recipe tokens in `lib/recipes/base.ts` + `{kebab}.css` | [`theming/guide.md`](./theming/guide.md) pasos 1–6 · **mandatory contract**: [`canon/recipe-contract.md`](./canon/recipe-contract.md) · scope (if `data-color`): [`canon/tsc.md`](./canon/tsc.md) | `npm run generate:eidos-css` + `recipe-css-contract` test + audit `R-*` (R-4.x = the contract) | +| **6 · Demo** | `web/routes/uix/components/{kebab}/+page.svelte` — v2 9-tab layout | [`demo-authoring.md`](./guides/demo-authoring.md) | audit `D-*` + `npm run smoke` | +| **7 · Component README** | Baseline · Comparativa (≥3 refs) · Decisiones · Gaps-with-disposition · Sema events table | template = any recent PASS component's README; sections audited | audit `F-*` | +| **8 · Acceptance** | nothing new — the component passes | [`completion-checklist.md`](./guides/completion-checklist.md) | `npm run component:audit --only {kebab}` + `npm run morfo:check` + `eidos-lint` | Cross-phase invariants you will hit in every phase: the layer boundaries ([`architecture/active-architecture.md`](./architecture/active-architecture.md) §7 hard rules) and the @@ -47,7 +47,7 @@ Cross-phase invariants you will hit in every phase: the layer boundaries **The `dir` prop moves the maths and leaves the paint behind.** A component can accept `dir`, run the chain and flip its arrow keys while nothing mirrors: a -recipe's `:dir()` branch matches the direction the element *inherited* unless +recipe's `:dir()` branch matches the direction the element _inherited_ unless the provider stamps the attribute. Phase 2 and phase 5 decide that together — [`canon/direction-contract.md`](./canon/direction-contract.md). diff --git a/docs/canon/direction-contract.md b/docs/canon/direction-contract.md index 4ac72f20f..1d8e990c6 100644 --- a/docs/canon/direction-contract.md +++ b/docs/canon/direction-contract.md @@ -59,10 +59,10 @@ the page once, through the boot's automatic DOM projection on ``, and everything below inherits. Only the MATHS still needs the preference as a value (it cannot read the DOM), and that tail lives in one helper: -| Link | Who runs it | Where | -| ---------------- | ------------------------ | -------------------------------------------------- | -| `prop → ctx` | `activeDir(getter)` | the **wrapper** `.svelte`, at `Provider.create(…)` | -| `→ prefs → 'ltr'`| `resolveDir(dir, soma)` | the **provider**, once, in `resolvedDir` | +| Link | Who runs it | Where | +| ----------------- | ----------------------- | -------------------------------------------------- | +| `prop → ctx` | `activeDir(getter)` | the **wrapper** `.svelte`, at `Provider.create(…)` | +| `→ prefs → 'ltr'` | `resolveDir(dir, soma)` | the **provider**, once, in `resolvedDir` | A component with no soma provider runs the same first links through `activeEidosDir` — see §7. @@ -463,7 +463,7 @@ When you touch a component's direction behaviour: the provider root), and does the provider wire `dir: this.opts.dir` — the raw assertion? (Either half missing does not compile; the census guard crosses prop and morfo.) Does the provider default once, in `resolvedDir = - resolveDir(this.opts.dir, this.soma)`, and never elsewhere? In-place +resolveDir(this.opts.dir, this.soma)`, and never elsewhere? In-place children need nothing — `DirectionContext` carries the assertion — but a PROVIDER a component creates directly (every picker's `PopoverProvider.create`) still receives its `dir` opt: provider creation is diff --git a/docs/canon/tsc.md b/docs/canon/tsc.md index bd5dd1d90..24c242e14 100644 --- a/docs/canon/tsc.md +++ b/docs/canon/tsc.md @@ -33,11 +33,15 @@ CSS custom-property substitution is **eager**, not lazy: ```css :root { - --base: black; - --derived: var(--base); + --base: black; + --derived: var(--base); +} +.x { + --base: red; +} +.y { + background: var(--derived); } -.x { --base: red; } -.y { background: var(--derived); } ``` What color is `.x.y`? **BLACK**, not red. `--derived` is computed at `:root` @@ -48,11 +52,11 @@ Applied to the pre-TSC Toggle: ```css :root { - --toggle-palette-solid: var(--toggle-color-neutral-solid); - --toggle-solid-on-bg: var(--toggle-palette-solid); /* FROZEN */ + --toggle-palette-solid: var(--toggle-color-neutral-solid); + --toggle-solid-on-bg: var(--toggle-palette-solid); /* FROZEN */ } [data-toggle][data-color='affirm'] { - --toggle-palette-solid: var(--toggle-color-affirm-solid); /* USELESS */ + --toggle-palette-solid: var(--toggle-color-affirm-solid); /* USELESS */ } ``` @@ -66,18 +70,18 @@ The recipe config declares **where** each token is emitted: ```ts recipes.toggle = { - 'palette-solid': { - declarations: [ - { value: 'var(--toggle-color-neutral-solid)', scope: 'host' }, - { value: 'var(--toggle-color-affirm-solid)', scope: 'color:affirm' }, - { value: 'var(--toggle-color-threat-solid)', scope: 'color:threat' } - ] - }, - 'solid-on-bg': { - value: 'var(--toggle-palette-solid)', - scope: 'host' // ← mandatory: the dep lives in 'host', not in 'root' - } -} + 'palette-solid': { + declarations: [ + { value: 'var(--toggle-color-neutral-solid)', scope: 'host' }, + { value: 'var(--toggle-color-affirm-solid)', scope: 'color:affirm' }, + { value: 'var(--toggle-color-threat-solid)', scope: 'color:threat' } + ] + }, + 'solid-on-bg': { + value: 'var(--toggle-palette-solid)', + scope: 'host' // ← mandatory: the dep lives in 'host', not in 'root' + } +}; ``` The generator: @@ -91,39 +95,39 @@ The generator: ### Available scopes -| Scope | Generated selector | When to use | -|---|---|---| -| `'root'` | `:root` | A stable token. The default for bare strings. | -| `'host'` | `[data-{c}]` | The token references `var(--{c}-palette-*)` or another `host` token. | -| `color:${v}` | `[data-{c}][data-color='${v}']` | Palette override per color value. | -| `variant:${v}` | `[data-{c}][data-variant='${v}']` | Variant cascade. | -| `state:${v}` | `[data-{c}][data-state='${v}']` | State cascade. | -| `size:${v}` | `[data-{c}][data-size='${v}']` | Size cascade. | -| `event:${v}` | `[data-{c}][data-event='${v}']` | Motion token bound to a perceptual signal. | -| `[axis:v, …]` | `[data-{c}][data-X='v'][data-Y='w']` | **Composite** — multiple ANDed conditions. | +| Scope | Generated selector | When to use | +| -------------- | ------------------------------------ | -------------------------------------------------------------------- | +| `'root'` | `:root` | A stable token. The default for bare strings. | +| `'host'` | `[data-{c}]` | The token references `var(--{c}-palette-*)` or another `host` token. | +| `color:${v}` | `[data-{c}][data-color='${v}']` | Palette override per color value. | +| `variant:${v}` | `[data-{c}][data-variant='${v}']` | Variant cascade. | +| `state:${v}` | `[data-{c}][data-state='${v}']` | State cascade. | +| `size:${v}` | `[data-{c}][data-size='${v}']` | Size cascade. | +| `event:${v}` | `[data-{c}][data-event='${v}']` | Motion token bound to a perceptual signal. | +| `[axis:v, …]` | `[data-{c}][data-X='v'][data-Y='w']` | **Composite** — multiple ANDed conditions. | ### Three ways to declare a token ```ts recipes.toggle = { - // (1) Short form — implicit 'root' scope (a stable token) - 'height-md': '32px', - - // (2) Simple form — one declaration with an explicit scope - // depends is auto-inferred from var() in the value - 'solid-on-bg': { - value: 'var(--toggle-palette-solid)', - scope: 'host' - }, - - // (3) Multi-declaration form — the SAME token under different scopes - // (the CSS reality of a custom property redeclared by cascade) - 'palette-solid': { - declarations: [ - { value: 'var(--toggle-color-neutral-solid)', scope: 'host' }, - { value: 'var(--toggle-color-affirm-solid)', scope: 'color:affirm' } - ] - } + // (1) Short form — implicit 'root' scope (a stable token) + 'height-md': '32px', + + // (2) Simple form — one declaration with an explicit scope + // depends is auto-inferred from var() in the value + 'solid-on-bg': { + value: 'var(--toggle-palette-solid)', + scope: 'host' + }, + + // (3) Multi-declaration form — the SAME token under different scopes + // (the CSS reality of a custom property redeclared by cascade) + 'palette-solid': { + declarations: [ + { value: 'var(--toggle-color-neutral-solid)', scope: 'host' }, + { value: 'var(--toggle-color-affirm-solid)', scope: 'color:affirm' } + ] + } }; ``` @@ -137,17 +141,17 @@ It is not a simple total order. The rule is: Equivalently: the dep's constraints must be a **subset** of the consumer's constraints. -| consumer | dep | covers? | Reason | -|---|---|---|---| -| `host` | `root` | ✓ | host is more specific; root always applies | -| `host` | `host` | ✓ | same scope | -| `host` | `color:affirm` | ✗ | the consumer doesn't constrain color | -| `color:affirm` | `host` | ✓ | host covers the whole host scope | -| `color:affirm` | `color:affirm` | ✓ | same scope | -| `color:affirm` | `color:loss` | ✗ | incompatible scopes (different values of the same axis) | -| `color:affirm` | `size:lg` | ✗ | the consumer doesn't constrain size | -| `[color:affirm, size:lg]` | `color:affirm` | ✓ | the composite covers each component | -| `[color:affirm, size:lg]` | `size:lg` | ✓ | same | +| consumer | dep | covers? | Reason | +| ------------------------- | -------------- | ------- | ------------------------------------------------------- | +| `host` | `root` | ✓ | host is more specific; root always applies | +| `host` | `host` | ✓ | same scope | +| `host` | `color:affirm` | ✗ | the consumer doesn't constrain color | +| `color:affirm` | `host` | ✓ | host covers the whole host scope | +| `color:affirm` | `color:affirm` | ✓ | same scope | +| `color:affirm` | `color:loss` | ✗ | incompatible scopes (different values of the same axis) | +| `color:affirm` | `size:lg` | ✗ | the consumer doesn't constrain size | +| `[color:affirm, size:lg]` | `color:affirm` | ✓ | the composite covers each component | +| `[color:affirm, size:lg]` | `size:lg` | ✓ | same | ### Cross-axis collision detection @@ -197,11 +201,13 @@ selector: Generates: ```css -[data-select-trigger], [data-select-content] { - --_select-accent-track: var(--select-primary-track); +[data-select-trigger], +[data-select-content] { + --_select-accent-track: var(--select-primary-track); } -[data-select-trigger][data-color='affirm'], [data-select-content][data-color='affirm'] { - --_select-accent-track: var(--select-affirm-track); +[data-select-trigger][data-color='affirm'], +[data-select-content][data-color='affirm'] { + --_select-accent-track: var(--select-affirm-track); } ``` @@ -242,10 +248,10 @@ Generates: ```css [data-toggle-group][data-color='affirm'] [data-toggle-group-item] { - --toggle-palette-solid: var(--toggle-affirm-solid); + --toggle-palette-solid: var(--toggle-affirm-solid); } [data-toggle-group][data-color='risk'] [data-toggle-group-item] { - --toggle-palette-solid: var(--toggle-risk-solid); + --toggle-palette-solid: var(--toggle-risk-solid); } ``` @@ -305,11 +311,11 @@ recipes: { card: { container: { md: { 'pad': 'var(--space-6)' } } } } architectural exceptions — TSC v2.2 covers the 3 patterns that previously lived outside the model: -| Pattern | TSC v2.2 solution | Components | -|---|---|---| -| `data-color` per-part (not on root) | `parts: ['x', 'y']` on `RecipeTokenMultiDeclaration` (multi-part scope) | `select` (trigger + content) | -| Composite (variant × color) | `scope: ['variant:X', 'color:Y']` (TSC v2 composite) | `avatar` (root + badge) | -| Cross-recipe override from an ancestor | `composition: { foreignRecipe: { targetSelector, tokens } }` | `toggle-group` (modifies Toggle's palette) | +| Pattern | TSC v2.2 solution | Components | +| -------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------ | +| `data-color` per-part (not on root) | `parts: ['x', 'y']` on `RecipeTokenMultiDeclaration` (multi-part scope) | `select` (trigger + content) | +| Composite (variant × color) | `scope: ['variant:X', 'color:Y']` (TSC v2 composite) | `avatar` (root + badge) | +| Cross-recipe override from an ancestor | `composition: { foreignRecipe: { targetSelector, tokens } }` | `toggle-group` (modifies Toggle's palette) | The universal guard `forbids palette-derived tokens at :root scope` (in `recipe-css-contract.test.ts`) stays active as a secondary defense over the diff --git a/docs/comparison.md b/docs/comparison.md index 67dd021de..0e58e74a3 100644 --- a/docs/comparison.md +++ b/docs/comparison.md @@ -19,8 +19,8 @@ Component libraries cluster into two families, and they force a trade: - **Headless behavior** (Radix Primitives, Ark UI, bits-ui, React Aria) — solve behavior + accessibility, leave visuals to you. Excellent separation, but they - stop at behavior: there is no model of *what an interaction means* or how it - should *feel*. + stop at behavior: there is no model of _what an interaction means_ or how it + should _feel_. - **Styled systems** (Mantine, Chakra, Radix Themes, shadcn) — ship opinionated visuals and theming, but couple behavior and appearance and let a theme redefine almost anything. @@ -44,8 +44,8 @@ CSS and the docs, and drifts silently. → [`architecture/morfo`](./architecture UIX models the **meaning and feel** of an interaction: 8 perceptual families, intents (the evaluative load), verbs, and a cascade that projects the signal as sound, haptic and a `data-event-*` stamp. No mainstream component library has a -perceptual layer — they animate, but they do not have a vocabulary for *what -occurred*. The vocabulary is grounded in the book *Diseñando lo que ocurre*. → +perceptual layer — they animate, but they do not have a vocabulary for _what +occurred_. The vocabulary is grounded in the book _Diseñando lo que ocurre_. → [`CANON.md`](./CANON.md), [`architecture/sema`](./architecture/sema.md) ### 3. Two-moment motion @@ -58,7 +58,7 @@ single axis (e.g. Chakra: only `data-state` + `Presence`). → [`eidos-motion.md ### 4. Theme = retint the perceptually-fixed Variants (`solid`/`outline`/…) and the 9 color roles are **canon of the system**, -not of the theme. A theme changes *which hex* is `affirm`; it cannot invent a +not of the theme. A theme changes _which hex_ is `affirm`; it cannot invent a variant or redefine what `outline` means. This is the opposite of styled systems where a theme can redefine almost anything — UIX trades that freedom for portability and a stable perceptual meaning across themes. → [`THEMING.md`](./theming/reference.md) §19 diff --git a/docs/consuming.md b/docs/consuming.md index bc3ca7ceb..aa08d497e 100644 --- a/docs/consuming.md +++ b/docs/consuming.md @@ -92,7 +92,7 @@ legacy mode: ```js vitePlugin: { dynamicCompileOptions: ({ filename }) => - filename.includes('node_modules') ? undefined : { runes: true } + filename.includes('node_modules') ? undefined : { runes: true }; } ``` diff --git a/docs/decisions.md b/docs/decisions.md index 0215002aa..412affff1 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -82,10 +82,10 @@ Design records for component families whose doctrine spans several components ## Cross-cutting decision logs -| Document | The decision it records | -| ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [`LIBRO_VARIACIONES_Y_EXTENSIONES.md`](../src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md) | The running log of where the implementation deviates from (or extends) the book's editorial canon — verbs, adoptions, clusters, and the D.x architectural decisions. The seed for a consolidated decision-log. | -| [`GESTURES.md`](../src/uix/soma/layers/gesture/GESTURES.md) | The soma gesture layer design: `Gesture.base`/`drag`/`resize`, velocity ring-buffer, axis lock, deferred pointer capture. | -| [`architecture/active-architecture.md` §7 + `arts/adom`/`arts/perf` READMEs](./architecture/active-architecture.md) | **Sec-dom — read-timing & token-resolution (2026-06-29).** The framework governs layout READS like it governs writes: `dom.measure` (coalesced post-layout reads), `eidos.resolveToken` (token→colour in JS, no `getComputedStyle` probe), the discoverable `uix.color`/`uix.perf` surfaces. Decided: **reject** a static grep-guard (too noisy across ~120 legit reads, and it can't catch the sync-read-after-write _ordering_ nor cover routes) — the dev `uix.perf` detector (Long Animation Frames) is the runtime safety net instead. | -| [`canon/direction-contract.md`](./canon/direction-contract.md) §1, §2, §6 | **Direction endgame — physics over convention (ratified 2026-08-05).** Four decisions signed at once: (D1) the chain gains the CONTEXT link — every `activeDir` publishes its assertion and descendants consult it before prefs, the "implicit inheritance" phase reopened and ratified, closing both field-measured holes (in-place and portal) with one mechanism; (D2) the attribute stamps ONLY the assertion — prefs leave the per-component chain and reach the page once, through the now-AUTOMATIC boot projection (opt-out in standalone, opt-in in attach), with the environment SEED (`readPrefsEnvironmentFromDom`) adopting a hand-set `` at precedence `intent > env > derive(language) > default`; (D3) the stamping mechanism belongs to the MORFO — `direction: { parts }` declares it, `soma.runtime` computes the requirement from the declaration (required when declared, forbidden when not), `compileMorfo` validates part names fail-closed, and the census guard crosses prop ↔ morfo; (D4) the API census (7 components without the prop + chat-log) stays POSTPONED by explicit decision. | -| [`canon/direction-contract.md`](./canon/direction-contract.md) | **Direction — resolution, assertion and paint.** One chain resolves a component's reading direction, one native attribute asserts it to the DOM, one selector form reads it back. Decided: the resolver stops **before** the default and returns `undefined`, because _nobody asserted a direction_ is a different fact from the default; stamping `dir` is **conditional** on who reads the direction — mandatory when the recipe branches with `:dir()`, needless when the dependence is pure JavaScript; and the static guard (`RTL-1`, `npm run rtl:check`) is deliberately scoped to the one trap CSS text can reveal — a logical inline anchor paired with a physical inline translate — leaving the chain, the attribute and the selector form to review. | +| Document | The decision it records | +| ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`LIBRO_VARIACIONES_Y_EXTENSIONES.md`](../src/docs/LIBRO_VARIACIONES_Y_EXTENSIONES.md) | The running log of where the implementation deviates from (or extends) the book's editorial canon — verbs, adoptions, clusters, and the D.x architectural decisions. The seed for a consolidated decision-log. | +| [`GESTURES.md`](../src/uix/soma/layers/gesture/GESTURES.md) | The soma gesture layer design: `Gesture.base`/`drag`/`resize`, velocity ring-buffer, axis lock, deferred pointer capture. | +| [`architecture/active-architecture.md` §7 + `arts/adom`/`arts/perf` READMEs](./architecture/active-architecture.md) | **Sec-dom — read-timing & token-resolution (2026-06-29).** The framework governs layout READS like it governs writes: `dom.measure` (coalesced post-layout reads), `eidos.resolveToken` (token→colour in JS, no `getComputedStyle` probe), the discoverable `uix.color`/`uix.perf` surfaces. Decided: **reject** a static grep-guard (too noisy across ~120 legit reads, and it can't catch the sync-read-after-write _ordering_ nor cover routes) — the dev `uix.perf` detector (Long Animation Frames) is the runtime safety net instead. | +| [`canon/direction-contract.md`](./canon/direction-contract.md) §1, §2, §6 | **Direction endgame — physics over convention (ratified 2026-08-05).** Four decisions signed at once: (D1) the chain gains the CONTEXT link — every `activeDir` publishes its assertion and descendants consult it before prefs, the "implicit inheritance" phase reopened and ratified, closing both field-measured holes (in-place and portal) with one mechanism; (D2) the attribute stamps ONLY the assertion — prefs leave the per-component chain and reach the page once, through the now-AUTOMATIC boot projection (opt-out in standalone, opt-in in attach), with the environment SEED (`readPrefsEnvironmentFromDom`) adopting a hand-set `` at precedence `intent > env > derive(language) > default`; (D3) the stamping mechanism belongs to the MORFO — `direction: { parts }` declares it, `soma.runtime` computes the requirement from the declaration (required when declared, forbidden when not), `compileMorfo` validates part names fail-closed, and the census guard crosses prop ↔ morfo; (D4) the API census (7 components without the prop + chat-log) stays POSTPONED by explicit decision. | +| [`canon/direction-contract.md`](./canon/direction-contract.md) | **Direction — resolution, assertion and paint.** One chain resolves a component's reading direction, one native attribute asserts it to the DOM, one selector form reads it back. Decided: the resolver stops **before** the default and returns `undefined`, because _nobody asserted a direction_ is a different fact from the default; stamping `dir` is **conditional** on who reads the direction — mandatory when the recipe branches with `:dir()`, needless when the dependence is pure JavaScript; and the static guard (`RTL-1`, `npm run rtl:check`) is deliberately scoped to the one trap CSS text can reveal — a logical inline anchor paired with a physical inline translate — leaving the chain, the attribute and the selector form to review. | diff --git a/docs/decisions/design-chat-block.md b/docs/decisions/design-chat-block.md index c634076eb..2e8ca4c84 100644 --- a/docs/decisions/design-chat-block.md +++ b/docs/decisions/design-chat-block.md @@ -47,14 +47,14 @@ each is a capability the framework already had: 5. **Threads and reactions as gaps.** → `Feed.Thread` already carries the thread semantics; reactions ship in v1. -The success criterion follows directly: the block sits *above* the sector on -all five, and *below* the cost of the complete kits — zero dependency, zero +The success criterion follows directly: the block sits _above_ the sector on +all five, and _below_ the cost of the complete kits — zero dependency, zero backend. ## Composition topology — compose, never reinvent -The user's founding constraint: *use the components already in the ecosystem, -don't reinvent the wheel*. The block is composition all the way down. +The user's founding constraint: _use the components already in the ecosystem, +don't reinvent the wheel_. The block is composition all the way down. - **`chat-log` = triple registration.** One shell element registers as the `chat-log` provider **and** `Feed.Provider` **and** `VirtualList.Provider` @@ -101,7 +101,7 @@ pattern (TanStack Virtual / react-virtuoso): anchor to end, follow-on-append prepend compensation by anchoring a stable key + offset. Hard-won specifics: - **Re-pin on growth while sticky** — dynamic rows measure larger than the - estimate *after* the pin, so `totalSize` grows and the view drifts. + estimate _after_ the pin, so `totalSize` grows and the view drifts. - **The pin lands against the real DOM** (`scrollHeight/clientHeight` inside `dom.measure`, state fallback for jsdom) — the state mirror derives px from the real box (RO `viewportSize` vs `clientHeight`, fractional sums). diff --git a/docs/decisions/design-session.md b/docs/decisions/design-session.md index f831c2284..27ac5405a 100644 --- a/docs/decisions/design-session.md +++ b/docs/decisions/design-session.md @@ -37,16 +37,16 @@ infrastructure lives under `src/arts/`. Each subdirectory is an independent "artifact" (zero-deps, runtime-focused, headless-of-UI). Eight artifacts already exist: -| Artifact | Layer(s) | Purpose | -| ---------- | ---------------------------------------- | ------------------------------------------------- | -| `lang` | `EngineLangs`, `ActiveLangs`, `MonoLangs` | i18n: type-safe translations, BCP 47 resolution | -| `logr` | `EngineLogger` | Structured logger: levels, transports, filters | -| `fmts` | `EngineFormat`, `ActiveFormat` | Localized formatting: numbers, currency, units, dates | -| `sium` | `EngineSium` | Validation contracts (Standard Schema interop) | -| `adom` | `ActiveDom` | Reactive DOM service | -| `stor` | `EngineStorage`, `ActiveStorage` | Reactive sync key/value with pluggable adapters | -| `http` | `EngineHttp` | HTTP client with retry, schema validation, hooks | -| `aapp` | `ActiveApp` | Composition root that wires the rest | +| Artifact | Layer(s) | Purpose | +| -------- | ----------------------------------------- | ----------------------------------------------------- | +| `lang` | `EngineLangs`, `ActiveLangs`, `MonoLangs` | i18n: type-safe translations, BCP 47 resolution | +| `logr` | `EngineLogger` | Structured logger: levels, transports, filters | +| `fmts` | `EngineFormat`, `ActiveFormat` | Localized formatting: numbers, currency, units, dates | +| `sium` | `EngineSium` | Validation contracts (Standard Schema interop) | +| `adom` | `ActiveDom` | Reactive DOM service | +| `stor` | `EngineStorage`, `ActiveStorage` | Reactive sync key/value with pluggable adapters | +| `http` | `EngineHttp` | HTTP client with retry, schema validation, hooks | +| `aapp` | `ActiveApp` | Composition root that wires the rest | A ninth artifact, `arts/sess`, is being designed: **session lifecycle infrastructure on the client**. @@ -119,7 +119,7 @@ pages with forms construct their own engine via a one-line helper: ```ts // In service schema: const App = createActiveApp({ - services: { sium: defineEngineSium() } + services: { sium: defineEngineSium() } }); // At call site: @@ -151,15 +151,15 @@ with lifecycle": ```ts interface ActiveStorageEntry { - readonly key: string; - readonly fullKey: string; - /** Reactive — the only public reactive property in the artifact. */ - current: T; - set(value: T): void; - update(fn: (prev: T) => T): void; - remove(): void; - onChange(fn): () => void; - dispose(): void; + readonly key: string; + readonly fullKey: string; + /** Reactive — the only public reactive property in the artifact. */ + current: T; + set(value: T): void; + update(fn: (prev: T) => T): void; + remove(): void; + onChange(fn): () => void; + dispose(): void; } ``` @@ -184,6 +184,7 @@ From `project_layer_boundaries.md`: > choices. > Things that **do not** belong in `arts/`: +> > - Forms (state binding to inputs, generated UI) > - Generated components (buttons, inputs, dialogs) > - **Stores for domain state** (cart, user, session) — Svelte 5 runes in @@ -209,6 +210,7 @@ From `feedback_minimal_apis.md`: From `feedback_professional_design.md`: > Patterns explicitly rejected during prior artifact builds: +> > - i18n coupled into the logger > - `minLevel` single cutoff on transports (replaced by `levels` per-severity) > - Runtime arg-shape discrimination (explicit > magical) @@ -239,16 +241,17 @@ reactive cache with dedup/staleTime/gcTime sits above stor and sess. What other libraries do, briefly. **Conclusions are intentionally not drawn — they are inputs for the reviewer.** -| Library | Storage | Refresh | User | Perms | Cross-tab | Notable | -| ---------------- | -------------------- | ------------------------ | ----------------- | ------------------- | ---------------- | -------------------------------------------------------------------- | -| **Lucia v3** | Server DB + cookie | Rolling on validate | `userId` (FK) | n/a | implicit | Server-only. `Register` augmentation for session/user attrs. | -| **iron-session** | Encrypted cookie | Manual `save()` | Opaque `` | n/a | implicit | 4 KB hard limit. Password rotation supported. | -| **Supabase** | Pluggable, default localStorage | Pre-emptive ticker (30s tick, 90s margin) + visibility + 401 | Strongly typed `Session.user` | n/a (server RLS) | `BroadcastChannel` | `_refreshingDeferred` dedup. ~3000 LOC monolith. | -| **better-auth** | DB + signed cookie cache | Rolling on getSession | Inferred from server | First-class plugins | None at lib | Cookie chunking (3896 B). Plugin-driven. | -| **Auth.js** | DB + JWT | Provider-driven | Module augmentation | None | Cookie-implicit | Provider-coupled (OAuth in scope). | -| **Clerk** | Opaque session ID | Pre-emptive | First-class | `session.has(...)` first-class | `BroadcastChannel` | ~75 KB. `addListener` event bus. | +| Library | Storage | Refresh | User | Perms | Cross-tab | Notable | +| ---------------- | ------------------------------- | ------------------------------------------------------------ | ----------------------------- | ------------------------------ | ------------------ | ------------------------------------------------------------ | +| **Lucia v3** | Server DB + cookie | Rolling on validate | `userId` (FK) | n/a | implicit | Server-only. `Register` augmentation for session/user attrs. | +| **iron-session** | Encrypted cookie | Manual `save()` | Opaque `` | n/a | implicit | 4 KB hard limit. Password rotation supported. | +| **Supabase** | Pluggable, default localStorage | Pre-emptive ticker (30s tick, 90s margin) + visibility + 401 | Strongly typed `Session.user` | n/a (server RLS) | `BroadcastChannel` | `_refreshingDeferred` dedup. ~3000 LOC monolith. | +| **better-auth** | DB + signed cookie cache | Rolling on getSession | Inferred from server | First-class plugins | None at lib | Cookie chunking (3896 B). Plugin-driven. | +| **Auth.js** | DB + JWT | Provider-driven | Module augmentation | None | Cookie-implicit | Provider-coupled (OAuth in scope). | +| **Clerk** | Opaque session ID | Pre-emptive | First-class | `session.has(...)` first-class | `BroadcastChannel` | ~75 KB. `addListener` event bus. | Common patterns observable across all: + - Refresh dedup via shared promise - Pre-emptive timer + reactive 401 - `INITIAL_SESSION` synchronous on subscribe (Supabase pattern) @@ -256,6 +259,7 @@ Common patterns observable across all: - Visibility-change handling Common divergences: + - Whether user identity is in the engine or external - Whether credential shape is named or opaque - Whether permissions are first-class or external @@ -270,63 +274,75 @@ gymnastics. If a use case requires unusual ceremony, the design has a problem. ### UC-1: JWT/OAuth SPA + - Login form returns `{user, accessToken, refreshToken, expiresAt}`. - Auto-refresh fires before expiry. - All HTTP requests carry `Authorization: Bearer `. - 401 from server → refresh → retry the request. ### UC-2: Cookie-based SSR + - Server sets `HttpOnly` cookie on login. - Client never sees the credential. - Browser sends cookie automatically with `credentials: 'include'`. - "Logout" hits a server endpoint that clears the cookie. ### UC-3: API-key SaaS dashboard + - Long-lived API key stored in localStorage (or generated per-tenant). - No refresh — keys are revoked, not rotated. - All requests carry `X-Api-Key: `. ### UC-4: Anonymous session + later login (e-commerce) + - Visitor lands, server starts an anonymous session with a `cartId`. - Visitor adds items to cart without identifying. - Visitor logs in: same session, now with `user` attached, cart preserved. ### UC-5: Multi-tab logout + - User has 3 tabs open. - Tab A clicks "Logout". - Tabs B and C reflect the logout (UI shows login prompt) without polling. ### UC-6: SSR hydration without re-validation + - `+layout.server.ts` reads cookie, validates session against DB, returns `{user, expiresAt, ...}` to the client via `data`. - Client adopts the snapshot. Schema validation runs ON THE SERVER ONLY — client trusts the SSR boundary. ### UC-7: Concurrent 401s during refresh + - 5 in-flight requests all return 401 simultaneously. - ONE refresh fires, the other 4 share its promise. - After refresh, all 5 retry with the new credential. ### UC-8: Refresh failure semantics + - Network glitch (transient) → preserve session, retry later. - Server says "refresh token invalid" (fatal) → revoke locally, emit `EXPIRED` event, navigate to `/login`. - Caller must distinguish these without imposing logout-on-flaky-network. ### UC-9: Visibility-change refresh + - User closes laptop lid for 90 minutes. - Opens it. Tab becomes visible. - Engine checks expiry, refreshes if needed, before any new request goes out. ### UC-10: Cross-tab WITHOUT exposing tokens via broadcast + - Tab A refreshes → broadcast fires → tab B re-reads storage and updates local state. - Broadcast payload contains event + version, NEVER tokens. - An XSS in tab A cannot exfiltrate tokens through the broadcast channel. ### UC-11: Three-state user model (newly raised) + The author has stated the design must distinguish: + - **No-identificado** (no session OR anonymous session) - **Identificado** (session has a user) - **Autorizado** (session+user+permission for a specific action) @@ -373,9 +389,13 @@ Rationale: prevents stampede when N requests fail with 401 simultaneously. ```ts type RevokeResult = - | { localRevoked: true; globalRevoked: true; scope: 'global' } - | { localRevoked: true; globalRevoked: false; scope: 'local'; - reason?: 'missing_revoke_url' | 'network_error' | 'server_rejected' }; + | { localRevoked: true; globalRevoked: true; scope: 'global' } + | { + localRevoked: true; + globalRevoked: false; + scope: 'local'; + reason?: 'missing_revoke_url' | 'network_error' | 'server_rejected'; + }; ``` `revoke()` is **never** `Promise`. Caller must be able to distinguish @@ -428,6 +448,7 @@ type interface (~30 LOC in `$libs/standard-schema`). ### 6.11 Out of scope The following are NOT `arts/sess` responsibilities: + - OAuth flows (Google, GitHub, etc.) - Password handling / hashing - MFA / TOTP / WebAuthn @@ -449,6 +470,7 @@ cases in §5, not on the author's framing.** What shape does `SessionSnapshot` take? **Option A — Single opaque payload generic.** + ```ts SessionSnapshot = { payload: TPayload; @@ -456,12 +478,14 @@ SessionSnapshot = { issuedAt: number; } ``` + - (+) Maximum agnosticism; engine knows nothing about user/credential structure - (+) Mirrors `arts/stor.entry` precedent exactly - (–) Loses semantic structure (can't distinguish identity from credential) - (–) `Sess.current.payload.user.name` is verbose **Option B — User generic + opaque credential.** + ```ts SessionSnapshot = { user: TUser | null; // null = anonymous @@ -470,12 +494,14 @@ SessionSnapshot = { issuedAt: number; } ``` + - (+) `user: TUser | null` codifies the unidentified/anonymous/identified state machine - (+) Credential stays opaque (auth-scheme agnostic) - (–) Two generics; potentially over-modeled - (–) Forces consumer to think about user shape upfront **Option C — Three generics: user + credential + claims.** + ```ts SessionSnapshot = { user: TUser | null; @@ -485,11 +511,13 @@ SessionSnapshot = { issuedAt: number; } ``` + - (+) Mirrors Clerk's session.has({permission}) pattern - (–) `TClaims` is authorization concern — does it belong in sess? - (–) Three generics is a lot **Option D — Lucia-style fixed shape with module augmentation.** + ```ts SessionSnapshot = { id: string; @@ -498,6 +526,7 @@ SessionSnapshot = { fresh: boolean; } & SessionAttributes // also from augmentation ``` + - (+) Single shape, consumer extends globally - (–) Module augmentation is project-global (one shape per app — fine?) - (–) Doesn't match generic-on-construction patterns elsewhere in `arts/*` @@ -507,10 +536,12 @@ SessionSnapshot = { Strongly correlated with Q1. **Option A — In sess as typed field.** + - `Sess.current.user` is the consumer's user object. - Lifecycle of "user" tied to lifecycle of session. **Option B — NOT in sess.** + - Sess only knows credential + lifecycle. - Consumer's separate `.svelte.ts` module loads/caches user from `/api/me` on `ADOPTED` event. @@ -518,6 +549,7 @@ Strongly correlated with Q1. with cart" (UC-4) — consumer fully owns the distinction. **Option C — Both (minimal subject + full profile separately).** + - Sess has `subject: string | null` (login/email/userId). - Full `User` profile lives in consumer's user store. - "Identified" means `subject !== null`. @@ -525,17 +557,20 @@ Strongly correlated with Q1. ### Q3: Where does "credential" live? **Option A — Named `accessToken`/`refreshToken` fields.** + - `Sess.current.accessToken`, `Sess.current.refreshToken`. - (+) Trivial bearer header construction; first-class refresh contract - (–) Couples to JWT/OAuth; cookie-only and API-key apps don't fit **Option B — Opaque `credential: TCredential` generic.** + - Consumer types it as `{accessToken, refreshToken}` for JWT or `{apiKey}` for API-key auth or `undefined` for cookie auth. - (+) Auth-scheme agnostic - (–) Engine can't auto-construct headers; consumer composes **Option C — Not in sess at all.** + - Cookie-only model: server handles credential, client never sees it. - For JWT/API-key, consumer stores credential in their own module. - (–) Loses centralized lifecycle (refresh rotates the credential — needs @@ -544,17 +579,20 @@ Strongly correlated with Q1. ### Q4: Perms/roles in sess? **Option A — `Sess.has({permission, role})` first-class.** + - Typed via `claimsSchema` if provided. - (+) Convenient call site - (–) Authorization is per-action runtime decision, not session state - (–) Forces a permission model on consumers (RBAC vs ABAC vs claims-based) **Option B — Not in sess.** + - Consumer module exposes `has(permission)`. - (+) Consumer picks RBAC, ABAC, capability-based, anything - (–) Every consumer reimplements the wheel **Option C — Future `arts/auth` or `$libs/auth` artifact.** + - Sess provides primitives; auth artifact provides permission models. - (+) Composable - (–) Doesn't exist yet; speculative @@ -562,29 +600,34 @@ Strongly correlated with Q1. ### Q5: How is the session engine constructed? **Option A — Standalone factory, consumer composes with App.** + ```ts const Sess = createActiveSession({...}); const App = createActiveApp({ http: { headers: () => bearerHeader(Sess) ?? {} } }); ``` + - (+) Sess fully independent of App; testable in isolation - (+) Consumer controls construction order - (–) App can't auto-wire Sess→Http integrations - (–) Awkward closure pattern when `onRefresh` needs `App.http` **Option B — App accepts a pre-built Sess instance.** + ```ts const Sess = createActiveSession({...}); const App = createActiveApp({ sess: Sess, http: {...} }); // App.session === Sess; App auto-wires 401 hook ``` + - (+) Type flows from Sess to App.session - (+) App can auto-wire what's auto-wireable - (–) Two-step setup - (–) Sess construction can't reference App (circular) **Option C — App constructs Sess from config.** + ```ts const App = createActiveApp({ sess: { userSchema: MyUserSchema, ... } @@ -592,11 +635,13 @@ const App = createActiveApp({ // App.session = ActiveSession (untyped) OR // ActiveApp (App is now generic on TUser) ``` + - (+) Single setup step; matches Lang/Storage/Http pattern - (–) App gains generics for TUser/TCredential — surface bloat - (–) Or App.session is untyped — caller casts everywhere **Option D — App provides factory; consumer calls it.** + ```ts const App = createActiveApp({ auth: { // infrastructure config @@ -608,6 +653,7 @@ const Sess = App.createActiveSession({ // typed factory }); // App.session === Sess (cached); typed at call site ``` + - (+) Mirrors `App.sium` precedent - (+) Infrastructure at App, types at call-site - (+) Type flows correctly via factory generics @@ -617,17 +663,20 @@ const Sess = App.createActiveSession({ // typed factory ### Q6: Where does configuration live? **Option A — All on call-site of `createActiveSession`.** + - userSchema, storage, onRefresh, revokeUrl, autoRefresh — all together - (+) One place to look - (–) Mixes infrastructure (storage) with types (userSchema) **Option B — Split: infra on App, types on call-site.** + - App config: storage, onRefresh, revokeUrl, autoRefresh - Call site: userSchema, credentialSchema - (+) Clean separation - (–) Two sources of truth to reconcile **Option C — All on App config; factory takes only generics.** + - App config: everything including userSchema - Call site: just `App.createActiveSession()` - (–) Tight coupling between App and consumer types @@ -635,9 +684,11 @@ const Sess = App.createActiveSession({ // typed factory ### Q7: Section name in App config **Option A — `auth: { ... }`** + - Consumer-facing semantic name ("I'm configuring authentication"). **Option B — `sess: { ... }`** + - Matches the artifact name. **Option C — Both/different name (e.g., `session`, `identity`).** @@ -647,23 +698,27 @@ const Sess = App.createActiveSession({ // typed factory How tightly does the design couple `arts/sess` to `arts/http`? **Option A — App auto-wires both `headers` and `beforeError` hook.** + - Requires sess to expose `bearerHeader()` (assumes JWT/Bearer). - (+) Zero-config for the common case - (–) Wrong for cookie auth, API-key auth, custom schemes **Option B — App auto-wires only `beforeError` hook (refresh is agnostic).** + - Consumer composes headers themselves (3 lines). - (+) Auth-scheme agnostic - (–) Consumer must remember to wire bearer if they use it **Option C — No auto-wiring, consumer composes everything.** + - (+) Maximum control - (–) Boilerplate at every consumer **Option D — App auto-wires via consumer-provided header builder.** + ```ts auth: { - bearerHeader: () => Sess.current?.token // consumer provides + bearerHeader: () => Sess.current?.token; // consumer provides } // App applies it to App.http.headers ``` @@ -671,35 +726,42 @@ auth: { ### Q9: User profile loading — where? **Option A — In sess (user is part of session snapshot).** + - See Q2 Option A. **Option B — Consumer `.svelte.ts` store (no library helper).** + - Consumer subscribes to Sess events, loads user from `/api/me`, caches in `$state`. - ~30 LOC per app. **Option C — `$libs/auth` helper module.** + - Provides `createUserStore({ fetcher, cache })`. - (+) Reusable - (–) Duplicates what `arts/cache` (deferred) will provide. **Option D — Defer until `arts/cache` lands.** + - User profile is just a query; let consumers wire it manually until cache exists; then promote the pattern. ### Q10: Multi-instance support **Option A — Single `Sess` per app.** + - Second `createActiveSession` throws `SessionAlreadyCreatedError`. - (+) Simple - (–) No multi-account UX **Option B — Multiple `Sess` instances allowed.** + - Each has its own storage key, refresh, etc. - App.session is the "active" one; can be switched. - (–) Complicates auto-wiring (which Sess does http use?) **Option C — Single Sess, but with internal "scopes".** + - (–) Speculative; defer. --- diff --git a/docs/decisions/design-text-effects.md b/docs/decisions/design-text-effects.md index df4e20825..0d7b95b02 100644 --- a/docs/decisions/design-text-effects.md +++ b/docs/decisions/design-text-effects.md @@ -19,7 +19,7 @@ axis — **what the animation is applied to**: decorative leaves → they went to the **pack tier** ([`architecture/packs.md`](../architecture/packs.md), the `Ambient` pack). - **Text effects** treat REAL text content the reader consumes → the - accessibility surface *is* the contract surface → they are **canon** + accessibility surface _is_ the contract surface → they are **canon** components (this document). Both streams share one provenance rule: the seed collection @@ -31,14 +31,14 @@ ecosystem's contracts. Six components, in two roles (decision D-T1): -| Component | Role | What it does | -| --- | --- | --- | -| [`CountUp`](../../src/uix/eidos/components/count-up/README.md) | **service** | Spring-counts a number to a target on viewport entry, formatting every frame through `uix.format.numbers`. | -| [`TextGradient`](../../src/uix/eidos/components/text-gradient/README.md) | decorative (CSS) | Animated gradient painted through the text (`background-clip`); `colors` takes a free stop list or a canonical gradient name. | -| [`TextCircular`](../../src/uix/eidos/components/text-circular/README.md) | decorative (JS) | Characters on a spinning circle; hover retunes the spin. | -| [`TextBlur`](../../src/uix/eidos/components/text-blur/README.md) | decorative (WAAPI) | Staggered blur→sharp entrance per word/letter on viewport entry. | -| [`TextFocus`](../../src/uix/eidos/components/text-focus/README.md) | decorative (CSS+JS) | Every word blurred except the active one; a corner frame travels to it. | -| [`TextScramble`](../../src/uix/eidos/components/text-scramble/README.md) | decorative (JS) | Characters near the pointer flicker through a charset and settle back. | +| Component | Role | What it does | +| ------------------------------------------------------------------------ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| [`CountUp`](../../src/uix/eidos/components/count-up/README.md) | **service** | Spring-counts a number to a target on viewport entry, formatting every frame through `uix.format.numbers`. | +| [`TextGradient`](../../src/uix/eidos/components/text-gradient/README.md) | decorative (CSS) | Animated gradient painted through the text (`background-clip`); `colors` takes a free stop list or a canonical gradient name. | +| [`TextCircular`](../../src/uix/eidos/components/text-circular/README.md) | decorative (JS) | Characters on a spinning circle; hover retunes the spin. | +| [`TextBlur`](../../src/uix/eidos/components/text-blur/README.md) | decorative (WAAPI) | Staggered blur→sharp entrance per word/letter on viewport entry. | +| [`TextFocus`](../../src/uix/eidos/components/text-focus/README.md) | decorative (CSS+JS) | Every word blurred except the active one; a corner frame travels to it. | +| [`TextScramble`](../../src/uix/eidos/components/text-scramble/README.md) | decorative (JS) | Characters near the pointer flicker through a charset and settle back. | **`CountUp` is a service component**, not a text effect (D-T2): it is the animated sibling of `` — formatting is FormatNumber's job, diff --git a/docs/decisions/design-timer.md b/docs/decisions/design-timer.md index ad4ad9404..5fbe5c36b 100644 --- a/docs/decisions/design-timer.md +++ b/docs/decisions/design-timer.md @@ -150,7 +150,7 @@ ActiveTimers Dependen de: ```ts -TimerScheduler +TimerScheduler; ``` Así pueden recibir: @@ -210,13 +210,13 @@ src/arts/timer/ Nombres públicos: ```ts -EngineTimers -ActiveTimers -TimerScheduler -TimerHandle -TimerEntrySnapshot -createEngineTimers -createActiveTimers +EngineTimers; +ActiveTimers; +TimerScheduler; +TimerHandle; +TimerEntrySnapshot; +createEngineTimers; +createActiveTimers; ``` Evitar: @@ -305,37 +305,28 @@ export const DEFAULT_BACKOFF_JITTER_MS = 500; ### 6.1 Estado y tipo de timer ```ts -export type TimerStatus = - | 'pending' - | 'running' - | 'cancelled' - | 'completed' - | 'failed'; +export type TimerStatus = 'pending' | 'running' | 'cancelled' | 'completed' | 'failed'; -export type TimerKind = - | 'timeout' - | 'interval'; +export type TimerKind = 'timeout' | 'interval'; ``` ### 6.2 Task ```ts -export type TimerTask = ( - ctx: TimerTaskContext -) => void | Promise; +export type TimerTask = (ctx: TimerTaskContext) => void | Promise; export interface TimerTaskContext { - readonly key: string; - readonly kind: TimerKind; + readonly key: string; + readonly kind: TimerKind; - readonly scheduledAt: number; - readonly dueAt: number; - readonly firedAt: number; - readonly driftMs: number; + readonly scheduledAt: number; + readonly dueAt: number; + readonly firedAt: number; + readonly driftMs: number; - readonly runCount: number; + readonly runCount: number; - readonly signal: AbortSignal; + readonly signal: AbortSignal; } ``` @@ -351,25 +342,25 @@ signal se aborta al cancelar/dispose ```ts export interface TimerOptions { - /** Reemplaza un timer existente con la misma key. */ - readonly replace?: boolean; + /** Reemplaza un timer existente con la misma key. */ + readonly replace?: boolean; - /** Default true para timeout. */ - readonly removeOnComplete?: boolean; + /** Default true para timeout. */ + readonly removeOnComplete?: boolean; - /** Debug/devtools only. */ - readonly meta?: Readonly>; + /** Debug/devtools only. */ + readonly meta?: Readonly>; } export interface TimerIntervalOptions extends TimerOptions { - /** - * Si true, la siguiente iteración se agenda después de terminar la task. - * Default true. - */ - readonly awaitTask?: boolean; - - /** Undefined = infinito hasta cancelación. */ - readonly maxRuns?: number; + /** + * Si true, la siguiente iteración se agenda después de terminar la task. + * Default true. + */ + readonly awaitTask?: boolean; + + /** Undefined = infinito hasta cancelación. */ + readonly maxRuns?: number; } ``` @@ -377,12 +368,12 @@ export interface TimerIntervalOptions extends TimerOptions { ```ts export interface TimerHandle { - readonly key: string; - readonly active: boolean; + readonly key: string; + readonly active: boolean; - cancel(): boolean; + cancel(): boolean; - reschedule(delayMs: number): void; + reschedule(delayMs: number): void; } ``` @@ -398,23 +389,23 @@ reschedule() en timer inactivo lanza TimerInactiveError ```ts export interface TimerEntrySnapshot { - readonly key: string; - readonly scope: string; - readonly kind: TimerKind; - readonly status: TimerStatus; + readonly key: string; + readonly scope: string; + readonly kind: TimerKind; + readonly status: TimerStatus; - readonly scheduledAt: number; - readonly dueAt: number; - readonly delayMs: number; + readonly scheduledAt: number; + readonly dueAt: number; + readonly delayMs: number; - readonly runCount: number; - readonly lastFiredAt: number | null; - readonly lastCompletedAt: number | null; - readonly lastErrorAt: number | null; + readonly runCount: number; + readonly lastFiredAt: number | null; + readonly lastCompletedAt: number | null; + readonly lastErrorAt: number | null; - readonly error: unknown | null; + readonly error: unknown | null; - readonly meta?: Readonly>; + readonly meta?: Readonly>; } ``` @@ -426,14 +417,11 @@ Para testabilidad, no acoplar directamente a `globalThis.setTimeout`. ```ts export interface TimerClock { - now(): number; + now(): number; - setTimeout( - fn: () => void, - delayMs: number - ): TimerNativeHandle; + setTimeout(fn: () => void, delayMs: number): TimerNativeHandle; - clearTimeout(handle: TimerNativeHandle): void; + clearTimeout(handle: TimerNativeHandle): void; } export type TimerNativeHandle = ReturnType; @@ -461,36 +449,26 @@ Interfaz mínima que consumirán otros artifacts. ```ts export interface TimerScheduler { - readonly size: number; + readonly size: number; - schedule( - key: string, - delayMs: number, - task: TimerTask, - options?: TimerOptions - ): TimerHandle; + schedule(key: string, delayMs: number, task: TimerTask, options?: TimerOptions): TimerHandle; - scheduleAt( - key: string, - dueAt: number, - task: TimerTask, - options?: TimerOptions - ): TimerHandle; + scheduleAt(key: string, dueAt: number, task: TimerTask, options?: TimerOptions): TimerHandle; - interval( - key: string, - everyMs: number, - task: TimerTask, - options?: TimerIntervalOptions - ): TimerHandle; + interval( + key: string, + everyMs: number, + task: TimerTask, + options?: TimerIntervalOptions + ): TimerHandle; - cancel(key: string): boolean; + cancel(key: string): boolean; - cancelAll(scope?: string): number; + cancelAll(scope?: string): number; - has(key: string): boolean; + has(key: string): boolean; - dispose(): void; + dispose(): void; } ``` @@ -498,15 +476,15 @@ export interface TimerScheduler { ```ts export interface EngineTimers extends TimerScheduler { - readonly disposed: boolean; + readonly disposed: boolean; - keys(): readonly string[]; + keys(): readonly string[]; - entries(): readonly TimerEntrySnapshot[]; + entries(): readonly TimerEntrySnapshot[]; - entry(key: string): TimerEntrySnapshot | null; + entry(key: string): TimerEntrySnapshot | null; - onChange(listener: (event: TimerEvent) => void): () => void; + onChange(listener: (event: TimerEvent) => void): () => void; } ``` @@ -514,10 +492,10 @@ export interface EngineTimers extends TimerScheduler { ```ts export interface ActiveTimers extends EngineTimers { - readonly size: number; - readonly keysSnapshot: readonly string[]; - readonly entriesSnapshot: readonly TimerEntrySnapshot[]; - readonly scopes: readonly string[]; + readonly size: number; + readonly keysSnapshot: readonly string[]; + readonly entriesSnapshot: readonly TimerEntrySnapshot[]; + readonly scopes: readonly string[]; } ``` @@ -529,30 +507,30 @@ export interface ActiveTimers extends EngineTimers { ```ts export type TimerEvent = - | { - readonly type: 'scheduled'; - readonly entry: TimerEntrySnapshot; - } - | { - readonly type: 'cancelled'; - readonly entry: TimerEntrySnapshot; - } - | { - readonly type: 'running'; - readonly entry: TimerEntrySnapshot; - } - | { - readonly type: 'completed'; - readonly entry: TimerEntrySnapshot; - } - | { - readonly type: 'failed'; - readonly entry: TimerEntrySnapshot; - readonly error: unknown; - } - | { - readonly type: 'disposed'; - }; + | { + readonly type: 'scheduled'; + readonly entry: TimerEntrySnapshot; + } + | { + readonly type: 'cancelled'; + readonly entry: TimerEntrySnapshot; + } + | { + readonly type: 'running'; + readonly entry: TimerEntrySnapshot; + } + | { + readonly type: 'completed'; + readonly entry: TimerEntrySnapshot; + } + | { + readonly type: 'failed'; + readonly entry: TimerEntrySnapshot; + readonly error: unknown; + } + | { + readonly type: 'disposed'; + }; ``` --- @@ -591,15 +569,15 @@ cache:products:gc Scope por defecto: ```ts -scopeOf('conn:main:ack:abc') === 'conn:main:ack' -scopeOf('conn:main') === 'conn' -scopeOf('sess:auto-refresh') === 'sess' +scopeOf('conn:main:ack:abc') === 'conn:main:ack'; +scopeOf('conn:main') === 'conn'; +scopeOf('sess:auto-refresh') === 'sess'; ``` `cancelAll(scope)` usa prefijo: ```ts -cancelAll('conn:main') +cancelAll('conn:main'); ``` cancela: @@ -620,7 +598,7 @@ Helper: ```ts function isInScope(key: string, scope: string): boolean { - return key === scope || key.startsWith(`${scope}:`); + return key === scope || key.startsWith(`${scope}:`); } ``` @@ -632,37 +610,37 @@ function isInScope(key: string, scope: string): boolean { ```ts interface InternalTimerEntry { - /** Unique per entry. New replace creates a new id even if key is reused. */ - id: number; + /** Unique per entry. New replace creates a new id even if key is reused. */ + id: number; - key: string; + key: string; - /** Incremented whenever this entry future execution is invalidated. */ - version: number; - kind: TimerKind; - status: TimerStatus; + /** Incremented whenever this entry future execution is invalidated. */ + version: number; + kind: TimerKind; + status: TimerStatus; - task: TimerTask; - controller: AbortController; + task: TimerTask; + controller: AbortController; - native: TimerNativeHandle | null; + native: TimerNativeHandle | null; - scheduledAt: number; - dueAt: number; - delayMs: number; + scheduledAt: number; + dueAt: number; + delayMs: number; - intervalMs: number | null; - awaitTask: boolean; - maxRuns: number | undefined; + intervalMs: number | null; + awaitTask: boolean; + maxRuns: number | undefined; - runCount: number; - lastFiredAt: number | null; - lastCompletedAt: number | null; - lastErrorAt: number | null; - error: unknown | null; + runCount: number; + lastFiredAt: number | null; + lastCompletedAt: number | null; + lastErrorAt: number | null; + error: unknown | null; - removeOnComplete: boolean; - meta?: Readonly>; + removeOnComplete: boolean; + meta?: Readonly>; } ``` @@ -670,13 +648,11 @@ interface InternalTimerEntry { ```ts export interface EngineTimersOptions { - readonly clock?: TimerClock; - readonly logger?: Logger; + readonly clock?: TimerClock; + readonly logger?: Logger; } -export function createEngineTimers( - options?: EngineTimersOptions -): EngineTimers; +export function createEngineTimers(options?: EngineTimersOptions): EngineTimers; ``` ### 12.3 Algoritmo de `schedule` @@ -734,7 +710,7 @@ facilita drift/runCount/maxRuns Default: ```ts -awaitTask = true +awaitTask = true; ``` #### 12.5.1 Semántica de `awaitTask: false` @@ -755,23 +731,28 @@ awaitTask = true If a consumer needs the interval to stop on failure, they should: ```ts -Timers.interval('foo', everyMs, async (ctx) => { - try { - await doWork(); - } catch (err) { - // explicit stop — awaitTask:false won't auto-stop on throw - ctx.signal.throwIfAborted(); - throw err; - } -}, { awaitTask: true }); +Timers.interval( + 'foo', + everyMs, + async (ctx) => { + try { + await doWork(); + } catch (err) { + // explicit stop — awaitTask:false won't auto-stop on throw + ctx.signal.throwIfAborted(); + throw err; + } + }, + { awaitTask: true } +); ``` …or cancel explicitly from inside the task: ```ts Timers.interval('bar', everyMs, async () => { - const ok = await tryWork(); - if (!ok) Timers.cancel('bar'); + const ok = await tryWork(); + if (!ok) Timers.cancel('bar'); }); ``` @@ -835,16 +816,16 @@ Cada entry interna MUST tener: ```ts interface InternalTimerEntry { - /** Unique per entry. New `replace` creates a new id even if key is reused. */ - id: number; + /** Unique per entry. New `replace` creates a new id even if key is reused. */ + id: number; - /** Stable user-facing key. */ - key: string; + /** Stable user-facing key. */ + key: string; - /** Incremented every time this entry's future execution is invalidated. */ - version: number; + /** Incremented every time this entry's future execution is invalidated. */ + version: number; - // ...rest of entry + // ...rest of entry } ``` @@ -864,52 +845,44 @@ Al programar: ```ts function armEntry(entry: InternalTimerEntry, delayMs: number): void { - entry.version += 1; + entry.version += 1; - const capturedId = entry.id; - const capturedKey = entry.key; - const capturedVersion = entry.version; + const capturedId = entry.id; + const capturedKey = entry.key; + const capturedVersion = entry.version; - entry.native = clock.setTimeout(() => { - void runEntry(capturedId, capturedKey, capturedVersion); - }, delayMs); + entry.native = clock.setTimeout(() => { + void runEntry(capturedId, capturedKey, capturedVersion); + }, delayMs); } ``` #### 12.8.3 Guard antes de ejecutar ```ts -function getLiveEntry( - id: number, - key: string, - version: number -): InternalTimerEntry | null { - const current = entries.get(key); - - if (!current) return null; - if (current.id !== id) return null; - if (current.version !== version) return null; - if (current.controller.signal.aborted) return null; - - return current; +function getLiveEntry(id: number, key: string, version: number): InternalTimerEntry | null { + const current = entries.get(key); + + if (!current) return null; + if (current.id !== id) return null; + if (current.version !== version) return null; + if (current.controller.signal.aborted) return null; + + return current; } ``` `runEntry` debe empezar así: ```ts -async function runEntry( - id: number, - key: string, - version: number -): Promise { - const entry = getLiveEntry(id, key, version); - - if (!entry) return; - if (entry.status !== 'pending') return; - - entry.status = 'running'; - // ... ejecutar task +async function runEntry(id: number, key: string, version: number): Promise { + const entry = getLiveEntry(id, key, version); + + if (!entry) return; + if (entry.status !== 'pending') return; + + entry.status = 'running'; + // ... ejecutar task } ``` @@ -919,17 +892,17 @@ Después de `await task(ctx)`, el engine debe volver a comprobar la misma tupla ```ts try { - await entry.task(ctx); + await entry.task(ctx); - const latest = getLiveEntry(id, key, version); - if (!latest) return; + const latest = getLiveEntry(id, key, version); + if (!latest) return; - completeEntry(latest); + completeEntry(latest); } catch (error) { - const latest = getLiveEntry(id, key, version); - if (!latest) return; + const latest = getLiveEntry(id, key, version); + if (!latest) return; - failEntry(latest, error); + failEntry(latest, error); } ``` @@ -941,13 +914,13 @@ Esto evita que una ejecución vieja pueda cerrar, fallar, eliminar o reprogramar ```ts function replaceTimer(key: string, config: NewTimerConfig): TimerHandle { - const previous = entries.get(key); + const previous = entries.get(key); - if (previous) { - cancelEntry(previous, 'replaced'); - } + if (previous) { + cancelEntry(previous, 'replaced'); + } - return createEntry(key, config); + return createEntry(key, config); } ``` @@ -959,24 +932,24 @@ La nueva entry debe tener un `id` nuevo. Si un callback viejo se ejecuta tarde, ```ts function rescheduleEntry(entry: InternalTimerEntry, delayMs: number): void { - if (entry.status === 'running') { - throw new TimerInactiveError(entry.key); - } + if (entry.status === 'running') { + throw new TimerInactiveError(entry.key); + } - validateDelay(delayMs); + validateDelay(delayMs); - if (entry.native !== null) { - clock.clearTimeout(entry.native); - entry.native = null; - } + if (entry.native !== null) { + clock.clearTimeout(entry.native); + entry.native = null; + } - entry.status = 'pending'; - entry.scheduledAt = clock.now(); - entry.delayMs = delayMs; - entry.dueAt = entry.scheduledAt + delayMs; + entry.status = 'pending'; + entry.scheduledAt = clock.now(); + entry.delayMs = delayMs; + entry.dueAt = entry.scheduledAt + delayMs; - armEntry(entry, delayMs); - emitScheduled(entry); + armEntry(entry, delayMs); + emitScheduled(entry); } ``` @@ -1003,7 +976,7 @@ Every task receives `ctx.signal`. Cancelling/replacing/disposing aborts it. ```ts Timers.schedule('example:fetch', 1_000, async ({ signal }) => { - await fetch('/api', { signal }); + await fetch('/api', { signal }); }); ``` @@ -1056,38 +1029,38 @@ Patrón recomendado: ```ts export function createActiveTimers(options?: EngineTimersOptions): ActiveTimers { - const engine = createEngineTimers(options); + const engine = createEngineTimers(options); - let entries = $state(engine.entries()); + let entries = $state(engine.entries()); - const off = engine.onChange(() => { - entries = engine.entries(); - }); + const off = engine.onChange(() => { + entries = engine.entries(); + }); - return { - ...engine, + return { + ...engine, - get size() { - return entries.length; - }, + get size() { + return entries.length; + }, - get keysSnapshot() { - return entries.map((entry) => entry.key); - }, + get keysSnapshot() { + return entries.map((entry) => entry.key); + }, - get entriesSnapshot() { - return entries; - }, + get entriesSnapshot() { + return entries; + }, - get scopes() { - return Array.from(new Set(entries.map((entry) => entry.scope))); - }, + get scopes() { + return Array.from(new Set(entries.map((entry) => entry.scope))); + }, - dispose() { - off(); - engine.dispose(); - } - }; + dispose() { + off(); + engine.dispose(); + } + }; } ``` @@ -1105,16 +1078,16 @@ Esto evita effect_update_depth_exceeded. ```ts export interface BackoffOptions { - readonly minDelayMs?: number; - readonly maxDelayMs?: number; - readonly factor?: number; - readonly jitterMs?: number; + readonly minDelayMs?: number; + readonly maxDelayMs?: number; + readonly factor?: number; + readonly jitterMs?: number; } export function computeBackoffDelay( - attempt: number, - options?: BackoffOptions, - random?: () => number + attempt: number, + options?: BackoffOptions, + random?: () => number ): number; ``` @@ -1145,7 +1118,7 @@ Este helper solo calcula. No agenda timers. ```ts export interface ActiveAppTimersOptions { - readonly enabled?: boolean; + readonly enabled?: boolean; } ``` @@ -1176,7 +1149,7 @@ timers?: TimerScheduler; Cuando se crean desde App: ```ts -timers: App.timers +timers: App.timers; ``` No deben importar un scheduler global. @@ -1189,10 +1162,10 @@ No deben importar un scheduler global. ```ts const stop = withAutoRefresh(Sess, { - timers: App.timers, - tickMs, - marginMs, - jitterMs + timers: App.timers, + tickMs, + marginMs, + jitterMs }); ``` @@ -1233,7 +1206,7 @@ Reconnect: ```ts timers.schedule(`conn:${name}:reconnect`, delay, () => { - return connection.reconnect('scheduled'); + return connection.reconnect('scheduled'); }); ``` @@ -1241,7 +1214,7 @@ Heartbeat: ```ts timers.interval(`conn:${name}:heartbeat`, intervalMs, () => { - return connection.send('connection.ping', {}); + return connection.send('connection.ping', {}); }); ``` @@ -1249,7 +1222,7 @@ Ack timeout: ```ts timers.schedule(`conn:${name}:ack:${id}`, timeoutMs, () => { - resolveAckTimeout(id); + resolveAckTimeout(id); }); ``` @@ -1413,7 +1386,7 @@ No empezar integrando todos los artifacts. Primero hacer `arts/timr` sólido y t const Timers = createEngineTimers(); Timers.schedule('demo:hello', 1_000, () => { - console.log('hello'); + console.log('hello'); }); ``` @@ -1421,7 +1394,7 @@ Timers.schedule('demo:hello', 1_000, () => { ```ts Timers.schedule('search:debounce', 300, runSearch, { - replace: true + replace: true }); ``` @@ -1429,7 +1402,7 @@ Timers.schedule('search:debounce', 300, runSearch, { ```ts Timers.interval('conn:main:heartbeat', 25_000, () => { - return Main.send('connection.ping', {}); + return Main.send('connection.ping', {}); }); ``` @@ -1443,11 +1416,11 @@ Timers.cancelAll('conn:main'); ```ts const App = createActiveApp({ - timers: {} + timers: {} }); App.timers.schedule('sess:auto-refresh', 30_000, () => { - return Sess.refresh(); + return Sess.refresh(); }); ``` diff --git a/docs/decisions/guia-semantica-historica.md b/docs/decisions/guia-semantica-historica.md index d9dc248bb..dbcca2e99 100644 --- a/docs/decisions/guia-semantica-historica.md +++ b/docs/decisions/guia-semantica-historica.md @@ -20,7 +20,7 @@ source: migrated verbatim from src/docs/GUIA_IMPLEMENTACION_SEMAUIX.md (2026-07- ## De la teoría del libro a la arquitectura del framework -Este documento traduce las decisiones del libro *Semántica perceptiva de la interfaz* a la arquitectura UIX (Morfo/Soma/Sema/Eidos). No repite la teoría — la convierte en contratos, vocabularios, reglas de resolución y convenciones técnicas. +Este documento traduce las decisiones del libro _Semántica perceptiva de la interfaz_ a la arquitectura UIX (Morfo/Soma/Sema/Eidos). No repite la teoría — la convierte en contratos, vocabularios, reglas de resolución y convenciones técnicas. --- @@ -37,24 +37,24 @@ Este documento traduce las decisiones del libro *Semántica perceptiva de la int ```ts export const SEMA_FAMILIES = [ - 'contact', // ¿el sistema ha sentido mi acción? - 'commit', // ¿algo quedó fijado o tuvo consecuencia? - 'signal', // ¿algo reclama mi atención? - 'handle', // ¿estoy manipulando directamente un objeto? - 'emerge', // ¿algo entró o salió del campo perceptivo? - 'shift', // ¿cambió el marco operativo? - 'sustain', // ¿esto sigue ocurriendo? - 'delegate' // ¿quién actúa ahora? (libro cap. 29) + 'contact', // ¿el sistema ha sentido mi acción? + 'commit', // ¿algo quedó fijado o tuvo consecuencia? + 'signal', // ¿algo reclama mi atención? + 'handle', // ¿estoy manipulando directamente un objeto? + 'emerge', // ¿algo entró o salió del campo perceptivo? + 'shift', // ¿cambió el marco operativo? + 'sustain', // ¿esto sigue ocurriendo? + 'delegate' // ¿quién actúa ahora? (libro cap. 29) ] as const; ``` **Cambios respecto a la implementación actual:** -| Antes | Ahora | Razón | -|---|---|---| +| Antes | Ahora | Razón | +| -------------------- | -------------------------- | ------------------------------------------------ | | `alert` como familia | `signal` con verbo `alert` | alert es intensidad dentro de signal, no familia | -| sin `shift` | `shift` añadido | emerge ≠ shift — dropdown ≠ modal | -| sin `loss` | `loss` como intent | threat ≠ loss — amenaza ≠ pérdida consumada | +| sin `shift` | `shift` añadido | emerge ≠ shift — dropdown ≠ modal | +| sin `loss` | `loss` como intent | threat ≠ loss — amenaza ≠ pérdida consumada | ### 1.2. Intents @@ -74,48 +74,61 @@ redeclaran ni prefijan este vocabulario. ```ts export const SEMA_VERBS = { - contact: [ - 'press', 'tap', 'activate', 'focus', 'trigger', 'release' - ], - commit: [ - 'select', 'unselect', 'toggle', 'save', 'submit', 'confirm', - 'complete', 'fail', 'cancel', 'reset', 'discard', 'delete', - 'restore', 'expire', 'acknowledge', 'apply', 'partial', 'block', - 'move', 'set', 'remove', 'reorder', 'upload' - ], - signal: [ - 'announce', 'notify', 'warn', 'alert', 'inform', 'emphasize', 'remind' - ], - handle: [ - 'pick', 'carry', 'drop', 'drag', 'resize', 'reorder', 'rotate', - 'scroll', 'zoom' - ], - emerge: [ - 'present', 'dismiss', 'open', 'close', 'expand', 'collapse', - 'reveal', 'hide' - ], - shift: [ - 'enter-mode', 'exit-mode', 'navigate', 'route', 'step', - 'return', 'context' - ], - sustain: [ - 'start', 'progress', 'loading', 'waiting', 'syncing', - 'processing', 'streaming', 'pending', 'retrying', 'upload', 'end' - ], - delegate: [ - 'offer', 'plan', 'authorize', 'act', 'review', 'escalate', 'return' - ] + contact: ['press', 'tap', 'activate', 'focus', 'trigger', 'release'], + commit: [ + 'select', + 'unselect', + 'toggle', + 'save', + 'submit', + 'confirm', + 'complete', + 'fail', + 'cancel', + 'reset', + 'discard', + 'delete', + 'restore', + 'expire', + 'acknowledge', + 'apply', + 'partial', + 'block', + 'move', + 'set', + 'remove', + 'reorder', + 'upload' + ], + signal: ['announce', 'notify', 'warn', 'alert', 'inform', 'emphasize', 'remind'], + handle: ['pick', 'carry', 'drop', 'drag', 'resize', 'reorder', 'rotate', 'scroll', 'zoom'], + emerge: ['present', 'dismiss', 'open', 'close', 'expand', 'collapse', 'reveal', 'hide'], + shift: ['enter-mode', 'exit-mode', 'navigate', 'route', 'step', 'return', 'context'], + sustain: [ + 'start', + 'progress', + 'loading', + 'waiting', + 'syncing', + 'processing', + 'streaming', + 'pending', + 'retrying', + 'upload', + 'end' + ], + delegate: ['offer', 'plan', 'authorize', 'act', 'review', 'escalate', 'return'] } as const; ``` **Cambios de verbs:** -| Antes | Ahora | Razón | -|---|---|---| -| `contact.select` | `commit.select` | seleccionar fija estado = commit | -| `contact.toggle` | `commit.toggle` | alternar fija estado = commit | -| `handle.acknowledge` | `commit.acknowledge` | reconocer cierra algo = commit | -| `handle.edit` | `shift.enter-mode` | editar cambia régimen = shift | +| Antes | Ahora | Razón | +| -------------------- | -------------------- | -------------------------------- | +| `contact.select` | `commit.select` | seleccionar fija estado = commit | +| `contact.toggle` | `commit.toggle` | alternar fija estado = commit | +| `handle.acknowledge` | `commit.acknowledge` | reconocer cierra algo = commit | +| `handle.edit` | `shift.enter-mode` | editar cambia régimen = shift | --- @@ -134,10 +147,12 @@ export const SEMA_VERBS = { Dos ejes ortogonales: **Jerarquía** (sin carga evaluativa): + - `primary` — acción principal - `secondary` — acción secundaria **Intent** (carga evaluativa): + - `neutral` — sin juicio fuerte - `affirm` — confirmación suave - `fulfill` — objetivo cumplido @@ -159,22 +174,26 @@ Un componente recibe dos props ortogonales con prioridad clara: - `color` (jerárquico) — opcional, solo `primary | secondary`. Override visual cuando NO hay carga evaluativa. Si pasas `color='primary'` con `intent='threat'`, el intent gana. ```svelte - - - - + + + + + + + + ``` ### 2.3. Mapeo desde convención heredada -| Convención CSS | Token UIX | Nota | -|---|---|---| -| primary | `primary` | jerarquía, no intent | -| secondary | `secondary` | jerarquía, no intent | -| success | `affirm` o `fulfill` | affirm = confirmación suave, fulfill = objetivo cumplido | -| warning | `risk` | problema corregible | -| danger | `threat` o `loss` | threat = antes, loss = después | -| info | `signal.announce + neutral` | info no es intent, es función de atención | +| Convención CSS | Token UIX | Nota | +| -------------- | --------------------------- | -------------------------------------------------------- | +| primary | `primary` | jerarquía, no intent | +| secondary | `secondary` | jerarquía, no intent | +| success | `affirm` o `fulfill` | affirm = confirmación suave, fulfill = objetivo cumplido | +| warning | `risk` | problema corregible | +| danger | `threat` o `loss` | threat = antes, loss = después | +| info | `signal.announce + neutral` | info no es intent, es función de atención | ### 2.4. Tokens CSS por theme @@ -209,24 +228,24 @@ No todo componente acepta los 8 valores. Cada componente declara su subset permi ### 3.1. Tabla de subsets -| Componente | color | intent permitidos | -|---|---|---| -| Button (acción) | primary, secondary | neutral, affirm, fulfill, risk, threat, loss | -| Button (nav) | primary, secondary | neutral | -| Toggle / Switch | primary, secondary | neutral, affirm, risk, threat | -| Checkbox / Radio | primary, secondary | neutral, affirm | -| Input | — | neutral, risk | -| Select / Combobox | primary, secondary | neutral, affirm | -| Slider | primary | neutral, affirm | -| Alert inline | — | risk | -| Alert crítica | — | threat | -| Toast / Snackbar | — | neutral, affirm, risk | -| Toast con undo | — | loss | -| Banner | — | neutral, risk | -| Badge | — | neutral, risk | -| Modal | — | neutral, threat, risk | -| Spinner / Skeleton | — | no acepta intent | -| Progress bar | — | no acepta intent | +| Componente | color | intent permitidos | +| ------------------ | ------------------ | -------------------------------------------- | +| Button (acción) | primary, secondary | neutral, affirm, fulfill, risk, threat, loss | +| Button (nav) | primary, secondary | neutral | +| Toggle / Switch | primary, secondary | neutral, affirm, risk, threat | +| Checkbox / Radio | primary, secondary | neutral, affirm | +| Input | — | neutral, risk | +| Select / Combobox | primary, secondary | neutral, affirm | +| Slider | primary | neutral, affirm | +| Alert inline | — | risk | +| Alert crítica | — | threat | +| Toast / Snackbar | — | neutral, affirm, risk | +| Toast con undo | — | loss | +| Banner | — | neutral, risk | +| Badge | — | neutral, risk | +| Modal | — | neutral, threat, risk | +| Spinner / Skeleton | — | no acepta intent | +| Progress bar | — | no acepta intent | ### 3.2. Regla de subset @@ -261,19 +280,19 @@ La estética tiene libertad dentro del rango que la semántica permite. Un affir > [`book-deviations.md`](./book-deviations.md) D.11). Ante > cualquier duda, la familia real de un componente es la de su morfo. -| Componente | Familia | Razón | -|---|---|---| -| Dropdown | emerge.open | aparición local, no cambia marco | -| Popover | emerge.open | aparición anclada | -| Tooltip | emerge.present | información auxiliar | -| Accordion | emerge.expand | contenido contenido | -| Modal / Dialog | emerge.open (implementado) | el libro lo doctrina shift.enter-mode; el morfo declara emerge | -| Command Palette | shift.enter-mode | cambia régimen operativo | -| Edit mode | shift.enter-mode | cambia qué puede hacerse | -| Wizard step | shift.step | avanza en proceso | -| Route change | shift.navigate | nuevo contexto | -| Drawer (pesado) | shift.enter-mode | si bloquea fondo y captura foco | -| Drawer (ligero) | emerge.open | si no bloquea ni captura | +| Componente | Familia | Razón | +| --------------- | -------------------------- | -------------------------------------------------------------- | +| Dropdown | emerge.open | aparición local, no cambia marco | +| Popover | emerge.open | aparición anclada | +| Tooltip | emerge.present | información auxiliar | +| Accordion | emerge.expand | contenido contenido | +| Modal / Dialog | emerge.open (implementado) | el libro lo doctrina shift.enter-mode; el morfo declara emerge | +| Command Palette | shift.enter-mode | cambia régimen operativo | +| Edit mode | shift.enter-mode | cambia qué puede hacerse | +| Wizard step | shift.step | avanza en proceso | +| Route change | shift.navigate | nuevo contexto | +| Drawer (pesado) | shift.enter-mode | si bloquea fondo y captura foco | +| Drawer (ligero) | emerge.open | si no bloquea ni captura | ### 4.2. contact vs commit @@ -301,30 +320,30 @@ Un toast que aparece es emerge.present. El mensaje dentro puede ser signal.notif ```ts events: [ - { - name: 'commit-toggle', - semantic: { - family: 'commit', - verb: 'toggle', - intent: undefined, // lo decide el provider según contexto - target: v.partRef('root'), - sequence: 'post' // la señal ocurre después del cambio de estado - } - } -] + { + name: 'commit-toggle', + semantic: { + family: 'commit', + verb: 'toggle', + intent: undefined, // lo decide el provider según contexto + target: v.partRef('root'), + sequence: 'post' // la señal ocurre después del cambio de estado + } + } +]; ``` ### 5.2. Campo `sequence` (timing del evento) ```ts -sequence: 'pre' | 'coincident' | 'post' +sequence: 'pre' | 'coincident' | 'post'; ``` -| Valor | Cuándo usar | Ejemplo | -|---|---|---| -| `pre` | señal perceptiva antes del cambio estructural | emerge.dismiss (animar salida antes de cerrar) | -| `coincident` | señal durante el proceso | sustain.progress | -| `post` | señal después del resultado real | commit.save + affirm (confirmar después de guardar) | +| Valor | Cuándo usar | Ejemplo | +| ------------ | --------------------------------------------- | --------------------------------------------------- | +| `pre` | señal perceptiva antes del cambio estructural | emerge.dismiss (animar salida antes de cerrar) | +| `coincident` | señal durante el proceso | sustain.progress | +| `post` | señal después del resultado real | commit.save + affirm (confirmar después de guardar) | No todo evento debe ser `pre` como el Toast dismiss. Contact debe ser `post` (inmediato, sin bloquear estado). Commit.save debe ser `post` (no celebrar antes de que exista resultado). @@ -339,25 +358,25 @@ D.11). Del morfo real del Dialog: ```ts events: [ - { - name: 'close', - semantic: { - family: 'emerge', // default - verb: 'close', - target: v.partRef('content'), - sequence: 'pre', - persistence: 'transient', - allowedFamilies: ['emerge', 'commit', 'signal'] - } - } -] + { + name: 'close', + semantic: { + family: 'emerge', // default + verb: 'close', + target: v.partRef('content'), + sequence: 'pre', + persistence: 'transient', + allowedFamilies: ['emerge', 'commit', 'signal'] + } + } +]; ``` El provider concreta con un override validado contra `allowedFamilies`: ```ts runtime.trigger('close', { - semantic: { family: 'commit', verb: 'save', intent: 'fulfill' } + semantic: { family: 'commit', verb: 'save', intent: 'fulfill' } }); ``` @@ -368,16 +387,16 @@ runtime.trigger('close', { ### 6.1. Separar hold expresivo de persistencia semántica ```ts -hold: number // duración expresiva mínima del evento (ms) -persistence: 'transient' | 'untilAction' | 'untilFix' | 'stateBound' +hold: number; // duración expresiva mínima del evento (ms) +persistence: 'transient' | 'untilAction' | 'untilFix' | 'stateBound'; ``` -| Tipo | Significado | Ejemplo | -|---|---|---| -| `transient` | desaparece tras hold | contact.press, commit.save + affirm | -| `untilAction` | persiste hasta que el usuario actúe | signal.alert + threat | -| `untilFix` | persiste hasta corrección | signal.warn + risk | -| `stateBound` | ligado al estado del proceso | sustain.progress | +| Tipo | Significado | Ejemplo | +| ------------- | ----------------------------------- | ----------------------------------- | +| `transient` | desaparece tras hold | contact.press, commit.save + affirm | +| `untilAction` | persiste hasta que el usuario actúe | signal.alert + threat | +| `untilFix` | persiste hasta corrección | signal.warn + risk | +| `stateBound` | ligado al estado del proceso | sustain.progress | ### 6.2. Holds por familia e intent @@ -404,11 +423,8 @@ Sustain no es un evento transitorio. Es un estado que dura mientras dura el proc ### 7.1. Señales transitorias (Sema escribe, Eidos lee) ```html -data-event="commit-toggle" -data-event-id="sig-42" -data-event-phase="active" -data-event-family="commit" -data-event-intent="affirm" +data-event="commit-toggle" data-event-id="sig-42" data-event-phase="active" +data-event-family="commit" data-event-intent="affirm" ``` Viven durante el hold. Se limpian antes de resolver la Promise. @@ -416,11 +432,7 @@ Viven durante el hold. Se limpian antes de resolver la Promise. ### 7.2. Estado persistente (Soma/Effects escriben, Eidos lee) ```html -data-state="open" -data-color="primary" -data-intent="risk" -data-disabled -data-pressed +data-state="open" data-color="primary" data-intent="risk" data-disabled data-pressed ``` Viven mientras el estado sea verdadero. No son señales transitorias. @@ -436,6 +448,7 @@ El canal visual nunca toca atributos de estado (`data-state`, `data-intent`, `da ### 8.1. V1: un solo evento activo por target Mantener un solo `data-event` activo. Suficiente para: + - dismiss, open, press, complete, toggle, select ### 8.2. V2 (futuro): slots semánticos @@ -451,9 +464,7 @@ handle.carry + signal.warn + threat Añadir slots: ```html -data-event-frame="shift.enter-mode" -data-event-signal="signal.alert" -data-event-intent="threat" +data-event-frame="shift.enter-mode" data-event-signal="signal.alert" data-event-intent="threat" ``` Así el modal (shift) no absorbe el intent de su contenido (signal). @@ -597,15 +608,15 @@ no usar: alarma sostenida de threat Relación orientativa entre archetipos de Morfo y familias: -| Archetype | Familias frecuentes | -|---|---| -| trigger | contact, emerge, shift, commit | -| content | emerge, shift, signal | -| item | commit, handle, signal | -| overlay | shift | -| indicator | sustain, commit | -| thumb | handle | -| track | handle, sustain | +| Archetype | Familias frecuentes | +| --------- | ------------------------------ | +| trigger | contact, emerge, shift, commit | +| content | emerge, shift, signal | +| item | commit, handle, signal | +| overlay | shift | +| indicator | sustain, commit | +| thumb | handle | +| track | handle, sustain | No como regla rígida, sino como documentación que ayuda a decidir qué eventos puede expresar un componente. @@ -645,14 +656,14 @@ visual directo: ```svelte - Open - - - - Title - Close - - + Open + + + + Title + Close + + ``` @@ -720,18 +731,18 @@ mantiene un segundo API flat con snippets que duplique el compound. ### 14.6. Componentes por migrar -| Componente | CSS legacy | Wrapper | Subset color | -|---|---|---|---| -| toggle | — | ✅ piloto | primary, secondary, neutral, affirm, risk, threat | -| switch | switch.css | ⏳ | primary, secondary, neutral, affirm, risk, threat | -| dialog | dialog.css | ⏳ | neutral, risk, threat | -| drawer | drawer.css | ⏳ | neutral | -| popover | popover.css | ⏳ | neutral | -| toast | toast.css | ⏳ | neutral, affirm, risk, loss | -| checkbox | checkbox.css | ⏳ | primary, secondary, neutral, affirm | -| accordion | accordion.css | ⏳ | neutral | -| tabs | tabs.css | ⏳ | neutral | -| tooltip | tooltip.css | ⏳ | neutral | +| Componente | CSS legacy | Wrapper | Subset color | +| ---------- | ------------- | --------- | ------------------------------------------------- | +| toggle | — | ✅ piloto | primary, secondary, neutral, affirm, risk, threat | +| switch | switch.css | ⏳ | primary, secondary, neutral, affirm, risk, threat | +| dialog | dialog.css | ⏳ | neutral, risk, threat | +| drawer | drawer.css | ⏳ | neutral | +| popover | popover.css | ⏳ | neutral | +| toast | toast.css | ⏳ | neutral, affirm, risk, loss | +| checkbox | checkbox.css | ⏳ | primary, secondary, neutral, affirm | +| accordion | accordion.css | ⏳ | neutral | +| tabs | tabs.css | ⏳ | neutral | +| tooltip | tooltip.css | ⏳ | neutral | --- @@ -750,4 +761,4 @@ mantiene un segundo API flat con snippets que duplique el compound. --- -*Documento derivado de "Semántica perceptiva de la interfaz" (Navarro Leal) y la arquitectura UIX (Morfo/Soma/Sema/Eidos).* +_Documento derivado de "Semántica perceptiva de la interfaz" (Navarro Leal) y la arquitectura UIX (Morfo/Soma/Sema/Eidos)._ diff --git a/docs/guides/component-audit.md b/docs/guides/component-audit.md index 6b962ed57..5554e5e1f 100644 --- a/docs/guides/component-audit.md +++ b/docs/guides/component-audit.md @@ -54,18 +54,18 @@ Every component must be audited against the relevant references below. Pick at least 3, including Radix Themes when it's a visual primitive and Ark UI / React Aria when it's a headless behaviour. -| Library | URL | Use for | -| --- | --- | --- | -| **Radix Themes** | https://radix-ui.com/themes | Visual primitives (Box / Flex / Grid / Container / Text). Canonical "Box-style split" architecture. | -| **Radix Primitives** | https://radix-ui.com/primitives | Headless behaviour (Dialog / Popover / Toast / DropdownMenu). ARIA reference. | -| **Chakra UI** | https://chakra-ui.com | Style-props heavy. Useful to spot props users expect on `Box` that we deliberately moved to `Flex`/`Grid`. | -| **Mantine** | https://mantine.dev | Strong typography + layout primitives (`Stack`, `Group`, `Container`, `Text`, `Title`). | -| **MUI** | https://mui.com | Reference for `sx` prop, transitions, theme system. | -| **React Aria** | https://react-spectrum.adobe.com/react-aria | Industry-strength a11y. Use when keyboard or screen-reader contract is non-trivial. | -| **Ark UI** | https://ark-ui.com | Headless state machines (combobox, picker, tags-input). Mirror their event names when possible. | -| **Bits UI** | https://bits-ui.com | Svelte-native parallel to Radix Primitives — useful sanity check on component shape. | -| **shadcn/ui** | https://ui.shadcn.com | Mostly visual recipes layered on Radix. Useful for opinionated styling defaults. | -| **WAI-ARIA APG** | https://www.w3.org/WAI/ARIA/apg/patterns/ | The contract. Required reading for any role / aria audit. | +| Library | URL | Use for | +| -------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| **Radix Themes** | https://radix-ui.com/themes | Visual primitives (Box / Flex / Grid / Container / Text). Canonical "Box-style split" architecture. | +| **Radix Primitives** | https://radix-ui.com/primitives | Headless behaviour (Dialog / Popover / Toast / DropdownMenu). ARIA reference. | +| **Chakra UI** | https://chakra-ui.com | Style-props heavy. Useful to spot props users expect on `Box` that we deliberately moved to `Flex`/`Grid`. | +| **Mantine** | https://mantine.dev | Strong typography + layout primitives (`Stack`, `Group`, `Container`, `Text`, `Title`). | +| **MUI** | https://mui.com | Reference for `sx` prop, transitions, theme system. | +| **React Aria** | https://react-spectrum.adobe.com/react-aria | Industry-strength a11y. Use when keyboard or screen-reader contract is non-trivial. | +| **Ark UI** | https://ark-ui.com | Headless state machines (combobox, picker, tags-input). Mirror their event names when possible. | +| **Bits UI** | https://bits-ui.com | Svelte-native parallel to Radix Primitives — useful sanity check on component shape. | +| **shadcn/ui** | https://ui.shadcn.com | Mostly visual recipes layered on Radix. Useful for opinionated styling defaults. | +| **WAI-ARIA APG** | https://www.w3.org/WAI/ARIA/apg/patterns/ | The contract. Required reading for any role / aria audit. | **Anti-rule:** "I'll just look at how air did it" is NOT a reference audit. Air's port reflects the old architecture and the gaps from the @@ -75,12 +75,12 @@ previous era. Audit fresh. Before deciding where a feature lives, recall the 4-layer architecture: -| Layer | Owns | Examples | -| --- | --- | --- | -| **morfo** | declarative contract: parts, data-attrs, ARIA, keyboard, events | `src/uix/morfo/components/{name}.ts` | -| **soma** | headless behaviour: state, effects, runtime providers, focus, ARIA wiring | `src/uix/soma/components/{name}/` | -| **sema** | perceptual side-effects: sound, motion, haptic projections of semantic events | `src/uix/sema/components/{name}/` | -| **eidos** | pure CSS visual recipe + `*Provider` wrappers that pipe `data-*` from morfo into the cascade | `src/uix/eidos/components/{name}/` | +| Layer | Owns | Examples | +| --------- | -------------------------------------------------------------------------------------------- | ------------------------------------ | +| **morfo** | declarative contract: parts, data-attrs, ARIA, keyboard, events | `src/uix/morfo/components/{name}.ts` | +| **soma** | headless behaviour: state, effects, runtime providers, focus, ARIA wiring | `src/uix/soma/components/{name}/` | +| **sema** | perceptual side-effects: sound, motion, haptic projections of semantic events | `src/uix/sema/components/{name}/` | +| **eidos** | pure CSS visual recipe + `*Provider` wrappers that pipe `data-*` from morfo into the cascade | `src/uix/eidos/components/{name}/` | **The 2-of-3 rule:** a morfo extension is only justified if at least 2 of 3 consumer layers (soma / sema / eidos) need it. (`feedback_2of3_rule`.) @@ -232,6 +232,7 @@ why recipes don't read `--style-{name}-*` directly, comparison vs Radix Themes / Chakra / Mantine / MUI). Allowed escape valves: + - `var(...)` wrapping the value - numeric identities: `0`, `0px`, `0em`, `0rem`, `1` - keywords: `inherit`, `initial`, `unset` @@ -282,32 +283,32 @@ the v2 worked reference). Layout primitives have their own canaries — These produced rework or rejected commits. Don't propose them again without naming the reason. -| Anti-pattern | Where it failed | Why | -| --- | --- | --- | -| Shallow 100-line demo "marketing page" | Layout Batch 1 v1 (`9ec2a57a`) | Violates `DEMO_AUTHORING_GUIDE`. Reverted + redone. | -| `matchAnchorWidth` on date-picker popover | 2026-05-22 morning | Visual gap when popover content narrower than input. User explicitly abandoned. | -| Visibility via `*Button` boolean props | DatePicker pre-`69`, removed | Inverts composition. Use `{#if showX}` around the part instead. | -| Inline chips inside Combobox control | Combobox v1–v3 (2026-05-22) | Fights flex-wrap; popover covers chips. Switched to band-above-Control (Chakra). | -| `onpointerdown` + `preventDefault` for item picks | SearchField v1 | Eats the synthetic click — onclick never fires. Use plain `