Formato puro: `npm run format` (dos pases; siete ficheros no convergían en el
primero) sobre todo lo que .prettierignore no excluye (web/ congelado,
.claude/, artefactos generados). Este commit va en .git-blame-ignore-revs.
Neutralidad medida fichero a fichero, compilando y minificando con esbuild (y
con el compilador de Svelte, cliente y servidor, para .svelte) la versión de
HEAD y la formateada: 1 031 ficheros de código idénticos; 9 CSS que solo
difieren en espacios pegados a un paréntesis (`var( --x )` frente a
`var(--x)`), que CSS no tokeniza como significativos; 510 Markdown sin
compilación posible (docs:check los valida). Los dos generados y el guard que
dependía del formato se resolvieron en el commit anterior.
Verificación sobre este árbol: npm run lint exit 0 · gate entero verde salvo
focus-census, arreglado en el commit anterior y re-ejecutado (9/9): check:gate
OK (src/ y scripts/ a cero) · docs:check 0/0 · arts/blocks/packs/rtl/
translations/agent · eidos:lint 0 inválidos · apps:check (boot 27/27, 0
errores) · suite 465/466 → 466/466 con el guard corregido · build de la raíz
exit 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
| 3 | No hay capa de gesto radial | ✅ RESUELTO | `Gesture.rotate` 4ª especialización en `soma/layers/gesture` (`18b0d44e`); el Knob lo consume |
| 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 |
| 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 |
| 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 |
| 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) |
| 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 |
| 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<T>()` vs `$state`, `role` opcional en Provider, `Without<>`/`PrimitiveDivAttributes`, ownership de pointermove/up del gesture |
| 9 | Fricciones menores (docs) | ✅ RESUELTO | `### Authoring notes` en `soma.md` §6: `state<T>()` vs `$state`, `role` opcional en Provider, `Without<>`/`PrimitiveDivAttributes`, ownership de pointermove/up del gesture |
## 1. Vocabularios canónicos invisibles desde los docs
## 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
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
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
`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`
`SEMA_MAP.families[*].hold` (¿cuánto dura el hold de `handle`?), los `kind`
categóricos del HapticChannel, y el contenido de `commonLangs`.
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
(`npm run docs:vocabularies`). Generado = sin objeción de drift. Para un
agente, ese apéndice es la diferencia entre declarar y adivinar.
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
## 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,
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
checklist A-1.3) pero ninguno enseña su _forma_: ¿export nombrado?, ¿record
`{ key: { en, es } }` o `{ en: { key } }`?, ¿claves anidadas o planas con
`{ 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
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
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
> `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
> debe resolver a un token declarado en algún sitio del árbol CSS de eidos
> (foundation + capas + auto-declaraciones del componente); las `var(--x,
> (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
> exentas por construcción. El guard cazó **3 fantasmas reales** además de los
> del Knob — `card-group --color-content-default`→`-primary`, `link-preview
> del Knob — `card-group --color-content-default`→`-primary`, `link-preview
| [`architecture/eidos.md`](./architecture/eidos.md) | The visual layer |
| [`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-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` |
| [`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` |
| [`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/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 |
| [`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 |
| [`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`) |
| [`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`) |
| [`docs/CANON.md`](./CANON.md) | The semantic vocabulary — authoritative |
| [`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/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/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/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) |
| [`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) |
| [`docs/decisions.md`](./decisions.md) | The RFC/design index — entry to all rationale |
| [`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 |
| [`docs/next-features.md`](./next-features.md) | The initiative registry — user-decided future work (scope, sequencing, dependencies), fed by audits and sessions |
| [`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/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/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) |
| [`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/channels.md`](./theming/channels.md) | The eight expression channels, synthesized |
| [`theming/changelog.md`](./theming/changelog.md) | The theming chronicle — the dated history behind the reference's standing decisions (§13, §20–§38) |
| [`theming/changelog.md`](./theming/changelog.md) | The theming chronicle — the dated history behind the reference's standing decisions (§13, §20–§38) |
| [`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) |
| [`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/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) |
| [`guides/completion-checklist.md`](./guides/completion-checklist.md) | Decide when a component is _done_ (machine-audited) |
| [`theming/motion.md`](./theming/motion.md) | The motion model — two moments, F1–F7, the preset/signature system (reference) |
| [`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) |
| [`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 |
| [`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) |
| [`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/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 |
| [`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 |
| [`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
### E5 — Module reference
@ -125,33 +125,33 @@ and the server-authoritative engines in `src/svrs/`.
| Know what a family / intent / verb means | `docs/CANON.md` (doctrine) · [`canon/vocabularies.md`](./canon/vocabularies.md) (the generated closed sets) |
| 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 |
| 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 |
| **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`) |
| 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) |
| Theme it / add a token | [`theming/reference.md`](./theming/reference.md) + [`canon/tsc.md`](./canon/tsc.md) |
| Make it work in RTL (assert a direction · mirror the paint) | [`canon/direction-contract.md`](./canon/direction-contract.md) (`npm run rtl:check`) |
| 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) |
| 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) |
| 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 |
| **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 |
| Test or validate a change | [`docs/testing-and-tooling.md`](./testing-and-tooling.md) — tests, validators, codegen, SSR |
## Authoritative sources & rules
## 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.
- **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)
## Process (ephemeral — not a source of truth)
[`docs/process/`](./process/) holds session hand-offs, architecture snapshots and
[`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
| `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` 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 |
| `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 |
| `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 |
| `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 |
| `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 |
| `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 |
| **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. |
| **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 (`<Text>`, `<Heading>`, `<Display>`, `<Code>`, `<Link>`, …) | The user-facing API to compose content — the USER picked "label" or "body" and wants that semantic role honored. |
| **Named styles** (`--style-label-*`, `--style-body-*`, `--style-caption-*`, `--style-h{1..6}-*`, `--style-{hero,prose,code}-*`) | typography primitives (`<Text>`, `<Heading>`, `<Display>`, `<Code>`, `<Link>`, …) | 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
**The two layers are not redundant**: they serve different contexts. The
numerical one vertebrates the system's _interior_; the semantic one
numerical one vertebrates the system's _interior_; the semantic one
@ -738,25 +737,25 @@ authored value references the style.)
| 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-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-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-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-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-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-6 | Off-screen and hidden-tab work is paused; a scene budget guards against unbounded concurrent GL contexts (warn via logger). |
| Demos fuera del fix | **Decisión de producto (2026-07-10): frame NUEVO para las demos del sistema** → reglas D-\* verdict-neutral | `e53bf0e8` |
| 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` |
| 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` |
| 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` |
| Lote 6 final | Los 5 últimos dossiers + apg timeline + literales anotados → **134/0/0**| `7f2bcc28` |
**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.
**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)
### 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.
(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
### B.3 Valor primario: `value` genérico vs palabra de dominio
| Patrón | Quién |
|---|---|
| Patrón | Quién |
| `value` genérico | fields, grupos, select/combobox/listbox, **carousel** (:83 — ¡para el índice de slide!) |
**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.
**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
### 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.
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
### 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).
`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
### 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`).
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
### 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`.
`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
### 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.
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)
### 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.
`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
### 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.
`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
### 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.
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)
## C. Filas heredadas del pass de fields (siguen vivas)
| Visibilidad de contenido | `visible`/`onVisibilityChange` + `purpose` | password-field (concepto DISTINTO del `open` de overlays — legítimo, documentar la distinción) |
| 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) |
| Cardinalidad | `enabledSelections` + `whenFull`| toggle-group (dueño único ✓ por decisión 2026-06-27) |
| Render condicional | `onlyWhen{Required·Optional·Invalid}` | field ×3 |
| 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?) |
| 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?) |
## D. Reglas de API que el veredicto debe ratificar (el "style guide" de props)
## 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.
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.
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.)_
| 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-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-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-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-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-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-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-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-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-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 |
| 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 |
- ~~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.
- ~~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.
- **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).
- **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.
| 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) |
| 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 |
| 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` |
| 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 |
| N4 | Sufijo **`{eje}Value` SOLO multi-eje** (tree-view queda: `selectedValue`/`expandedValue`); mono-eje = `value` a secas |
| 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) |
| 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) |
| 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) |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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) |
| 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 |
| 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) |
| 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) |
| 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) |
| C1 | Matriz de intent del deshacer **CANONIZADA tal cual** (colección-unselect=affirm ×5 · binario-uncheck=neutral · reset/cancel=neutral) — solo doc |
| 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 |
| 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) |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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)
## Fixes obvios (aprobados sin veto durante el checkpoint)
| 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 |
| 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 ✓ |
| resto | | ✓ | pack ✓; tests ✓; README ×2 ✓ |
## Hallazgos y propuestas
## Hallazgos y propuestas
@ -22,4 +22,4 @@ Censo verbos emerge (accordion open/close vs collapsible expand/collapse — mis
| 8 | Contrato morfo | ~~✓ ejemplar~~ → corregido (A-109) | era `<header data-banner role='banner'>` con la cita de la spec; hoy `<section data-banner>` sin rol propio. `data-intent` → canal color-role estándar |
| 8 | Contrato morfo | ~~✓ ejemplar~~ → corregido (A-109) | era `<header data-banner role='banner'>` con la cita de la spec; hoy `<section data-banner>` 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 |
| 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 |
| 14 | Docs | gap menor | Clasificación formal → SYS-7 |
| 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) |
| 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 ✓ |
| 8 | Contrato morfo | ✓ | Nav landmark, current-page aria, apg ✓ |
| **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 |
| **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
## Escalan al sistema
@ -24,4 +24,4 @@ Censo "0-eventos sin línea de razón" (breadcrumb, link-preview — vs 6 ejempl
- **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).
- **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).
- **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.
- **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.
| 1 | Composición vs declaración | ✓ | API compositiva; `child` (asChild) ✓; Spinner morfo-declarado con razón documentada (derivado de estado, no composición) |
| 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) |
| 2 | Naming de props | ✓ con hallazgo | Props canónicas (`disabled/loading/type/color/intent`); ver **S-1** (hooks CSS mixtos clase/data) |
| 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) |
| 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) |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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) |
| 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 |
| 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í |
| 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ó |
| 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 |
| 16 | i18n | ✓ | `texts` del morfo localizados (`#?components.button.*`); `loadingText` del consumidor; sin strings hardcoded |
## Hallazgos y propuestas de solución — del componente
## Hallazgos y propuestas de solución — del componente
| **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-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-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-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-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-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-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** |
| **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
## Hallazgos que escalan al sistema / theming
| ID | Hallazgo (visto en button, alcance = catálogo) | Propuesta |
| 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-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-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-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 |
| **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
## 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)_
| 2 | Naming | ✓ + censo | `type: 'single'\|'multiple'` (provider :63 — range va aparte en range-calendar, split limpio ✓); **`preventDeselect`** (types :112, Radix-style) — nuevo concepto para _naming; `CalendarView = 'day'\|'month'\|'year'` (:25, el view-switch) |
| 2 | Naming | ✓ + censo | `type: 'single'\|'multiple'` (provider :63 — range va aparte en range-calendar, split limpio ✓); **`preventDeselect`** (types :112, Radix-style) — nuevo concepto para \_naming; `CalendarView = 'day'\|'month'\|'year'` (:25, el view-switch) |
| 3 | Coherencia semántica | ✓ con cuestión | `commit-select`/`commit-unselect` (affirm, target day) + `shift-navigate` (sin intent ✓ shift); pack propio ✓; **F-5**: `commit-unselect` con intent **affirm** mientras date-field `commit-reset` es neutral y editable `commit-cancel` neutral — ¿la DES-selección es afirmativa? → censo transversal "intent de deshacer" |
| 3 | Coherencia semántica | ✓ con cuestión | `commit-select`/`commit-unselect` (affirm, target day) + `shift-navigate` (sin intent ✓ shift); pack propio ✓; **F-5**: `commit-unselect` con intent **affirm** mientras date-field `commit-reset` es neutral y editable `commit-cancel` neutral — ¿la DES-selección es afirmativa? → censo transversal "intent de deshacer" |
| 4 | API | ✓ | min/maxValue + **minViewValue** (:118, límite del view-switch); preventDeselect; isDateHoliday inyectable (types :56) |
| 4 | API | ✓ | min/maxValue + **minViewValue** (:118, límite del view-switch); preventDeselect; isDateHoliday inyectable (types :56) |
| 5 | Tokens | ✓ | Acentos por privados `--_calendar-accent-*` con 8 retunes por color ✓; antecedente B.2 resuelto |
| 5 | Tokens | ✓ | Acentos por privados `--_calendar-accent-*` con 8 retunes por color ✓; antecedente B.2 resuelto |
| 6 | Sema | ✓ | Pack propio existe (`sema/components/calendar.ts`) — el único de la familia calendar con carácter |
| 6 | Sema | ✓ | Pack propio existe (`sema/components/calendar.ts`) — el único de la familia calendar con carácter |
| 7 | Animaciones | ✓ | Sin keyframes en morfo/recipe leídos |
| 7 | Animaciones | ✓ | Sin keyframes en morfo/recipe leídos |
| 8 | Contrato morfo | ✓ ejemplar con 2 gaps | **TRES razones de no-archetype escritas** (GridRow :248-250 "hover over the entire week bug" · Cell :271-274 chrome de listbox-row · Day :309-313 el item-tint pisaba el fill de selección) — memoria institucional del gotcha archetype-item ×3 ✓; ariaBoolean con nota de compat boolToStr ×6; **F-3**: `data-type` declarado SIN enum (morfo :54) cuando soma tiene unión cerrada (provider :63) — el compilador no puede validar valores |
| 8 | Contrato morfo | ✓ ejemplar con 2 gaps | **TRES razones de no-archetype escritas** (GridRow :248-250 "hover over the entire week bug" · Cell :271-274 chrome de listbox-row · Day :309-313 el item-tint pisaba el fill de selección) — memoria institucional del gotcha archetype-item ×3 ✓; ariaBoolean con nota de compat boolToStr ×6; **F-3**: `data-type` declarado SIN enum (morfo :54) cuando soma tiene unión cerrada (provider :63) — el compilador no puede validar valores |
| 9 | A11y | **2 hallazgos** | Teclado APG-grid COMPLETO en Provider (:70-81: flechas, PageUp/Down mes, Home/End semana, Enter/Space) ✓ — date-field F-1 (Home/End) aquí SÍ está; **F-1**: Provider `role='application'` (:51) — decisión pesada (desactiva el cursor virtual del lector para TODO el árbol) SIN razón documentada, en un morfo que documenta hasta 3 razones de archetype (react-aria la usa en Calendar; bits-ui no — es debate APG real, pero aquí está muda); **F-2**: Day `role='button'` lleva `aria-selected` (:340-344) — ARIA inválido (aria-selected solo soporta gridcell/option/row/tab/treeitem...); el Cell (role=gridcell :277,:292-297) YA la lleva legítimamente |
| 9 | A11y | **2 hallazgos** | Teclado APG-grid COMPLETO en Provider (:70-81: flechas, PageUp/Down mes, Home/End semana, Enter/Space) ✓ — date-field F-1 (Home/End) aquí SÍ está; **F-1**: Provider `role='application'` (:51) — decisión pesada (desactiva el cursor virtual del lector para TODO el árbol) SIN razón documentada, en un morfo que documenta hasta 3 razones de archetype (react-aria la usa en Calendar; bits-ui no — es debate APG real, pero aquí está muda); **F-2**: Day `role='button'` lleva `aria-selected` (:340-344) — ARIA inválido (aria-selected solo soporta gridcell/option/row/tab/treeitem...); el Cell (role=gridcell :277,:292-297) YA la lleva legítimamente |
| 10 | Theming | ✓ | Focus outline (o1) → censo SYS-5; vocabulario de estado de celda rico (selected/today/weekend/holiday/unavailable/outside-month) |
| 10 | Theming | ✓ | Focus outline (o1) → censo SYS-5; vocabulario de estado de celda rico (selected/today/weekend/holiday/unavailable/outside-month) |
| 11 | Composición interna | **el mandato de familia** | Es el DONANTE informal: range-calendar (79 tokens), month/year-grid (65), pickers (38+4) chupan su vocabulario cross-component → mandato calendar-surface (SYS-8, ya en next-features); chronos beberá de ella |
| 11 | Composición interna | **el mandato de familia** | Es el DONANTE informal: range-calendar (79 tokens), month/year-grid (65), pickers (38+4) chupan su vocabulario cross-component → mandato calendar-surface (SYS-8, ya en next-features); chronos beberá de ella |
| 12 | Estados | ✓ | 8 data-attrs por celda/día; readonly/disabled/invalid en provider |
| 12 | Estados | ✓ | 8 data-attrs por celda/día; readonly/disabled/invalid en provider |
| 13 | Runtime | ✓ | `runtime.trigger('shift-navigate', {targetOverride})` (:328) — el patrón targetOverride correcto para partes doble-registradas |
| 13 | Runtime | ✓ | `runtime.trigger('shift-navigate', {targetOverride})` (:328) — el patrón targetOverride correcto para partes doble-registradas |
| 14 | Docs+tests | ✓ | README ×2; tests ✓ |
| 14 | Docs+tests | ✓ | README ×2; tests ✓ |
| 15 | i18n | **hallazgo** | texts + commonRefs localizados ✓; PERO **F-4**: `weekend` = "Saturday or Sunday (weekend in Gregorian)" HARDCODEADO (types :53-54) — el weekend es por-LOCALE, no por calendario (ar-SA/IL = vie/sáb): `Intl.Locale.getWeekInfo()` existe; un framework con locale reactivo pintando `data-weekend` incorrecto en media región del mundo |
| 15 | i18n | **hallazgo** | texts + commonRefs localizados ✓; PERO **F-4**: `weekend` = "Saturday or Sunday (weekend in Gregorian)" HARDCODEADO (types :53-54) — el weekend es por-LOCALE, no por calendario (ar-SA/IL = vie/sáb): `Intl.Locale.getWeekInfo()` existe; un framework con locale reactivo pintando `data-weekend` incorrecto en media región del mundo |
| **F-1** | `role='application'` sin razón documentada (morfo :51) | Decisión + doc in-place: mantenerla (estilo react-aria, intercepta flechas en SRs antiguos) documentando el porqué, o bajar a sin-role/`region` (estilo bits-ui) — el teclado ya vive en el grid APG. El barrido censa si otros overlays/grids la usan |
| **F-1** | `role='application'` sin razón documentada (morfo :51) | Decisión + doc in-place: mantenerla (estilo react-aria, intercepta flechas en SRs antiguos) documentando el porqué, o bajar a sin-role/`region` (estilo bits-ui) — el teclado ya vive en el grid APG. El barrido censa si otros overlays/grids la usan |
| **F-2** | `aria-selected` sobre `role='button'` (Day :340-344) — ARIA inválido; duplicada con el Cell | Quitar aria-selected del Day (el gridcell la posee) o cambiar Day a aria-pressed si se quiere estado en el botón; validar con el linter ARIA que la máquina use |
| **F-2** | `aria-selected` sobre `role='button'` (Day :340-344) — ARIA inválido; duplicada con el Cell | Quitar aria-selected del Day (el gridcell la posee) o cambiar Day a aria-pressed si se quiere estado en el botón; validar con el linter ARIA que la máquina use |
| **F-3** | `data-type` sin enum en morfo (unión cerrada en soma :63) | Declarar `enum: ['single','multiple']` en el morfo — el compilador valida y eidos puede selector por valor. Censar más data-attrs sin enum en la familia |
| **F-3** | `data-type` sin enum en morfo (unión cerrada en soma :63) | Declarar `enum: ['single','multiple']` en el morfo — el compilador valida y eidos puede selector por valor. Censar más data-attrs sin enum en la familia |
| **F-4** | `weekend` hardcodea sáb/dom (types :53-54) — incorrecto en locales vie/sáb | `$libs/days`: exponer weekInfo por locale (Intl.Locale.getWeekInfo con fallback) y derivar `weekend` de ahí; el hardcode queda como fallback documentado |
| **F-4** | `weekend` hardcodea sáb/dom (types :53-54) — incorrecto en locales vie/sáb | `$libs/days`: exponer weekInfo por locale (Intl.Locale.getWeekInfo con fallback) y derivar `weekend` de ahí; el hardcode queda como fallback documentado |
| **F-5** | `commit-unselect` intent affirm vs neutral de los deshacer de la familia fields | Censo transversal "intent de deshacer/des-seleccionar" (calendar unselect=affirm · date-field reset=neutral · editable cancel=neutral) → doctrina única en el veredicto |
| **F-5** | `commit-unselect` intent affirm vs neutral de los deshacer de la familia fields | Censo transversal "intent de deshacer/des-seleccionar" (calendar unselect=affirm · date-field reset=neutral · editable cancel=neutral) → doctrina única en el veredicto |
| F-6 | Marcas holiday/event scoped al host — los prestatarios NO las reciben (antecedente B.2) | Cubierto por el mandato calendar-surface (SYS-8) |
| F-6 | Marcas holiday/event scoped al host — los prestatarios NO las reciben (antecedente B.2) | Cubierto por el mandato calendar-surface (SYS-8) |
| F-1: composición eidos sin morfo — mismo debate que color-swatch/capas | SYS-7: clasificar formalmente (composición-de-componentes vs átomo vs capa); si el criterio exige justificación escrita, añadirla |
| F-1: composición eidos sin morfo — mismo debate que color-swatch/capas | SYS-7: clasificar formalmente (composición-de-componentes vs átomo vs capa); si el criterio exige justificación escrita, añadirla |
| 3 | Coherencia semántica | ✓ declarado | 1 evento (la variante interactiva) + family-default ESCRITO — más contrato del que el pass superficial le reconocía ("pack —") |
| 3 | Coherencia semántica | ✓ declarado | 1 evento (la variante interactiva) + family-default ESCRITO — más contrato del que el pass superficial le reconocía ("pack —") |
| 8 | Contrato morfo | hallazgo | **F-1**: sin APG para la variante clickable/link-card (A-1.4) |
| 8 | Contrato morfo | hallazgo | **F-1**: sin APG para la variante clickable/link-card (A-1.4) |
| 3 | Coherencia semántica | ✓ declarado | family-default escrito (corrección: no es hueco); si el paginado de slides merece carácter → SYS-3 candidato menor |
| 3 | Coherencia semántica | ✓ declarado | family-default escrito (corrección: no es hueco); si el paginado de slides merece carácter → SYS-3 candidato menor |
| 12 | Runtime | candidato D13 | El scroll-snap/medición de slides — verificar dom.measure en provider (censo D13) |
| 12 | Runtime | candidato D13 | El scroll-snap/medición de slides — verificar dom.measure en provider (censo D13) |
| F-1: componente en fase temprana — su completitud (interacción, a11y de series, CVD de paletas de datos) llegará con las fases de la iniciativa plots | Registrado; no es drift sino roadmap. El hueco CVD de series comparte doctrina con meter (D-3) |
| F-1: componente en fase temprana — su completitud (interacción, a11y de series, CVD de paletas de datos) llegará con las fases de la iniciativa plots | Registrado; no es drift sino roadmap. El hueco CVD de series comparte doctrina con meter (D-3) |
| 3 | Coherencia semántica | ✓ **modelo direccional** | DOS eventos estáticos: `commit-toggle-check`**affirm** (:20-25) / `commit-toggle-uncheck`**neutral** (:38-43) — la doctrina "per-event intent is intrinsic" aplicada literalmente. **Dato de censo**: contrasta con el modelo fromProp de switch/toggle (UN evento, intent del consumidor, default neutral) — DOS respuestas de la familia a la misma pregunta "¿el on/off binario tiene dirección intrínseca?" → veredicto de coherencia (ambas razonables; debe quedar escrita la distinción: marcar-hecho ≈ affirm intrínseco vs conmutar-modo ≈ neutral parametrizable) |
| 3 | Coherencia semántica | ✓ **modelo direccional** | DOS eventos estáticos: `commit-toggle-check`**affirm** (:20-25) / `commit-toggle-uncheck`**neutral** (:38-43) — la doctrina "per-event intent is intrinsic" aplicada literalmente. **Dato de censo**: contrasta con el modelo fromProp de switch/toggle (UN evento, intent del consumidor, default neutral) — DOS respuestas de la familia a la misma pregunta "¿el on/off binario tiene dirección intrínseca?" → veredicto de coherencia (ambas razonables; debe quedar escrita la distinción: marcar-hecho ≈ affirm intrínseco vs conmutar-modo ≈ neutral parametrizable) |
| 8 | Contrato morfo | ✓ | APG checkbox; indeterminate/checked/disabled completos; hidden-input form ✓ |
| 8 | Contrato morfo | ✓ | APG checkbox; indeterminate/checked/disabled completos; hidden-input form ✓ |
| 1 | Composición | ✓ | Partes compositivas + **FormatSelect** como parte propia (native `<select>` de formato — PrimitiveSelectAttributes, types :14) — composición, no prop-tree |
| 1 | Composición | ✓ | Partes compositivas + **FormatSelect** como parte propia (native `<select>` 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) |
| 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) |
| 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 ✓) |
| 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) |
| 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) |
| 6 | Sema | declarado, cuestionado | `family-default` explícito — mismo veredicto de trío que time-field (SYS-3) |
| 7 | Animaciones | ✓ | Patrón familia |
| 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 |
| 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) |
| 9 | A11y | con F-2 | Sin Home/End (especialmente útil aquí: canales 0-255/0-360) |
| **F-1** | Sin `commit-reset` (limpiar no suena) | Con time-field, en el veredicto de trío |
| **F-1**| Sin `commit-reset` (limpiar no suena) | Con time-field, en el veredicto de trío |
| **F-2** | Sin Home/End | Pass de familia |
| **F-2**| Sin Home/End | Pass de familia |
| **F-3** | Bloque ARIA del Input a unificar en el trío | Adoptar el rico de time-field en los tres |
| **F-3**| Bloque ARIA del Input a unificar en el trío | Adoptar el rico de time-field en los tres |
| **F-4** | `height-{k}` duplicadas | SYS-6 |
| **F-4**| `height-{k}` duplicadas | SYS-6 |
| **Nota ✓** | Las razones tipadas de `onInvalid` son MEJOR API que la de date/time | Propagar el patrón al trío en la normalización (nivelar hacia arriba) |
| **Nota ✓** | Las razones tipadas de `onInvalid` son MEJOR API que la de date/time | Propagar el patrón al trío en la normalización (nivelar hacia arriba) |
## Escalan al sistema
## Escalan al sistema
@ -41,4 +41,4 @@ SYS-3 (trío: expression + reset + ARIA + onInvalid — un solo veredicto de niv
| 3 | Coherencia semántica | ✓ **estrena handle** | 4 eventos: `handle-pick` (:23-26) + `handle-drag` (:32-35) — PRIMERA aparición de la familia handle en el censo (manipulación continua del área/thumb) + `commit-set`**neutral** (:44-49 ✓ canon de fields: fijar valor = neutral) + `commit-reset` neutral; `expression: 'pack'` + pack ✓ COHERENTES (a diferencia de gradient-*) |
| 3 | Coherencia semántica | ✓ **estrena handle** | 4 eventos: `handle-pick` (:23-26) + `handle-drag` (:32-35) — PRIMERA aparición de la familia handle en el censo (manipulación continua del área/thumb) + `commit-set`**neutral** (:44-49 ✓ canon de fields: fijar valor = neutral) + `commit-reset` neutral; `expression: 'pack'` + pack ✓ COHERENTES (a diferencia de gradient-\*) |
| 1 | Composición | ✓ completa | Anatomía palette entera: Input/List/Viewport/Item/**LinkItem**/Group/GroupHeading/GroupItems/**Empty**/**Loading**/Separator (:34-219) — estados vacío/cargando como partes ✓ |
| 1 | Composición | ✓ completa | Anatomía palette entera: Input/List/Viewport/Item/**LinkItem**/Group/GroupHeading/GroupItems/**Empty**/**Loading**/Separator (:34-219) — estados vacío/cargando como partes ✓ |
| 3 | Coherencia semántica | ✓ **razonada con el libro** | `commit-submit-invoke` affirm (:22-27) con la cita exacta: cap. 23 `commit.submit` + cap. 22 §8 — "the command's actual outcome fires downstream; celebrating at the click is *celebrate before time*. Same precedent as Button". `expression: "family-default"` (:9) = TERCERA delegación-a-default documentada (con field-langs y button) → la lista "default legítimo" del veredicto SYS-3 |
| 3 | Coherencia semántica | ✓ **razonada con el libro** | `commit-submit-invoke` affirm (:22-27) con la cita exacta: cap. 23 `commit.submit` + cap. 22 §8 — "the command's actual outcome fires downstream; celebrating at the click is _celebrate before time_. Same precedent as Button". `expression: "family-default"` (:9) = TERCERA delegación-a-default documentada (con field-langs y button) → la lista "default legítimo" del veredicto SYS-3 |
| 8 | Contrato morfo | ✓ | APG combobox citado (:8) — el patrón correcto para palette |
| 8 | Contrato morfo | ✓ | APG combobox citado (:8) — el patrón correcto para palette |
| 14 | Docs+tests | gap | Sin README eidos (E-2.3) → SYS-4; tests ✓ |
| 14 | Docs+tests | gap | Sin README eidos (E-2.3) → SYS-4; tests ✓ |
| resto | | ✓ | list-surface ✓; squircle Tier A ✓ |
| resto | | ✓ | list-surface ✓; squircle Tier A ✓ |
| ~~F-2~~ | *(retirado — el family-default está razonado con citas del libro; ver dim 3)* | Citar command en el veredicto SYS-3 como ejemplo de default legítimo |
| ~~F-2~~ | _(retirado — el family-default está razonado con citas del libro; ver dim 3)_ | Citar command en el veredicto SYS-3 como ejemplo de default legítimo |
| 2 | Naming | hallazgo SYS-1 | **F-1: 8 hooks-clase** — el MAYOR ofensor del catálogo (button 6, image-picker 6) |
| 2 | Naming | hallazgo SYS-1 | **F-1: 8 hooks-clase** — el MAYOR ofensor del catálogo (button 6, image-picker 6) |
| 3 | Coherencia semántica | ✓ con desajuste | `handle-drag`/`handle-resize`/`handle-zoom` (:31-63 ✓ censo handle) + commit affirm (:64-69); **F-2**: el evento se llama `commit-crop` pero su verb es `'apply'` (:64-67) — el nombre rompe `{familia}-{verbo}` (sería commit-apply) y la regla A-3.6 no lo flagueó (cuarto agujero de regla) |
| 3 | Coherencia semántica | ✓ con desajuste | `handle-drag`/`handle-resize`/`handle-zoom` (:31-63 ✓ censo handle) + commit affirm (:64-69); **F-2**: el evento se llama `commit-crop` pero su verb es `'apply'` (:64-67) — el nombre rompe `{familia}-{verbo}` (sería commit-apply) y la regla A-3.6 no lo flagueó (cuarto agujero de regla) |
| 8 | Contrato morfo | hallazgo | **F-3**: sin cita `apg:` (censo = 0) con PASS — el A-1.4 tampoco lo cazó; caso "sin patrón oficial" |
| 8 | Contrato morfo | hallazgo | **F-3**: sin cita `apg:` (censo = 0) con PASS — el A-1.4 tampoco lo cazó; caso "sin patrón oficial" |
| 12 | Runtime | pendiente familia | Candidato del censo D13 (write-then-read): sus gestos miden el box — verificar `dom.measure` en provider al ejecutar |
| 12 | Runtime | pendiente familia | Candidato del censo D13 (write-then-read): sus gestos miden el box — verificar `dom.measure` en provider al ejecutar |
| resto | | ✓ | Gestos, aspect locks, teclado de nudge; pack ✓; tests ✓ |
| resto | | ✓ | Gestos, aspect locks, teclado de nudge; pack ✓; tests ✓ |
| ~~F-2~~ | *(RETIRADO en P1: `apply` ES canónico — extensión de autor, verbs.ts :97 — y `commit-crop` es etiqueta de dominio autorizada por el libro cap. 8 §1; vocab-check lo lista como warn de descubribilidad, no violación. Rename a `commit-apply-crop` = pulido opcional)* | — |
| ~~F-2~~ | _(RETIRADO en P1: `apply` ES canónico — extensión de autor, verbs.ts :97 — y `commit-crop` es etiqueta de dominio autorizada por el libro cap. 8 §1; vocab-check lo lista como warn de descubribilidad, no violación. Rename a `commit-apply-crop` = pulido opcional)_ | — |
| **F-3** | Sin APG (regla no cazó) | Regla "apg: none — rationale" + fix del script |
| **F-3** | Sin APG (regla no cazó) | Regla "apg: none — rationale" + fix del script |
## Escalan al sistema
## Escalan al sistema
@ -28,4 +28,4 @@ SYS-1 (top) · censos A-3.6/A-1.4 (agujeros del script ×4 ya) · censo D13 (can
- **Método**: solo análisis — fixes tras el barrido completo; la ficha registra hallazgos + propuestas de solución (locales y las que escalan al sistema).
- **Método**: solo análisis — fixes tras el barrido completo; la ficha registra hallazgos + propuestas de solución (locales y las que escalan al sistema).
| 1 | Composición vs declaración | ✓ | Partes compositivas (`Provider/Input/Segment/Label` como children); snippet `segments` ordenados para render libre |
| 1 | Composición vs declaración | ✓ | Partes compositivas (`Provider/Input/Segment/Label` como children); snippet `segments` ordenados para render libre |
| 2 | Naming de props | ✓ | `value/onValueChange` + `placeholder/onPlaceholderChange` (patrón bindable canónico); `minValue/maxValue`; `readonlySegments`; `kind` ⊥ `granularity` con la ortogonalidad DOCUMENTADA (consistencia de trío) |
| 2 | Naming de props | ✓ | `value/onValueChange` + `placeholder/onPlaceholderChange` (patrón bindable canónico); `minValue/maxValue`; `readonlySegments`; `kind` ⊥ `granularity` con la ortogonalidad DOCUMENTADA (consistencia de trío) |
| 3 | Coherencia semántica | ✓ | `commit-set`/`commit-reset` post, intent neutral (canon de fields); expression `'pack'` |
| 3 | Coherencia semántica | ✓ | `commit-set`/`commit-reset` post, intent neutral (canon de fields); expression `'pack'` |
| 4 | API normalizada | ✓ | Bindables declarados; `validate`/`onInvalid` (Standard-Schema-friendly); `locale` con fallback reactivo `soma.langs`; `dir` RTL invierte flechas; `hourCycle` con cadena de fallbacks |
| 4 | API normalizada | ✓ | Bindables declarados; `validate`/`onInvalid` (Standard-Schema-friendly); `locale` con fallback reactivo `soma.langs`; `dir` RTL invierte flechas; `hourCycle` con cadena de fallbacks |
| 5 | Tokens usados | ✓ | CSS consume SOLO sus tokens de recipe (`--date-field-*`) — TSC-canónico; segment-height fantasma eliminado en auditoría B.1 (antecedente resuelto) |
| 5 | Tokens usados | ✓ | CSS consume SOLO sus tokens de recipe (`--date-field-*`) — TSC-canónico; segment-height fantasma eliminado en auditoría B.1 (antecedente resuelto) |
| 6 | Parte semántica | ✓ ejemplar | Pack con `semaSelector` tipado, targets al part `input` (nota de dónde estampa el engine), tunings COMPUESTOS (`form.commit.soft` / `subtle` + delta `{op:'add', -80}` + contour) — el patrón de doctrina exacto |
| 6 | Parte semántica | ✓ ejemplar | Pack con `semaSelector` tipado, targets al part `input` (nota de dónde estampa el engine), tunings COMPUESTOS (`form.commit.soft` / `subtle` + delta `{op:'add', -80}` + contour) — el patrón de doctrina exacto |
| 7 | Animaciones | ✓ | Transición SOLO de background con razón documentada (border-color instantáneo: el increment provoca blur/refocus y un border transicionado parpadea) |
| 7 | Animaciones | ✓ | Transición SOLO de background con razón documentada (border-color instantáneo: el increment provoca blur/refocus y un border transicionado parpadea) |
| 8 | Contrato morfo | ✓ | APG spinbutton enlazado; `role='group'` en Input + `role='spinbutton'` por segmento con `aria-value{min,max,now,text}` completos; teclado declarado; `ariaBoolean` para aria-disabled con nota de por qué |
| 8 | Contrato morfo | ✓ | APG spinbutton enlazado; `role='group'` en Input + `role='spinbutton'` por segmento con `aria-value{min,max,now,text}` completos; teclado declarado; `ariaBoolean` para aria-disabled con nota de por qué |
| 9 | Accesibilidad | ✓ con hallazgo | Labels/`aria-labelledby` condicionales por part-present; `errorMessageId` → describedby; **F-1**: faltan `Home`/`End` (APG spinbutton: saltar a min/max) |
| 9 | Accesibilidad | ✓ con hallazgo | Labels/`aria-labelledby` condicionales por part-present; `errorMessageId` → describedby; **F-1**: faltan `Home`/`End` (APG spinbutton: saltar a min/max) |
| 10 | Adopción theming | hallazgo doctrinal | Focus = **outline flush** (`:has(segment:focus-visible)` + border swap) con razón técnica sólida — pero la doctrina §32 vigente dice "los CAMPOS quedan en box-shadow (anillo interior parametrizable)" → **S-1**. State-layer de segmentos vía capa compartida ✓ (`data-field-segment` estampado en soma) |
| 10 | Adopción theming | hallazgo doctrinal | Focus = **outline flush** (`:has(segment:focus-visible)` + border swap) con razón técnica sólida — pero la doctrina §32 vigente dice "los CAMPOS quedan en box-shadow (anillo interior parametrizable)" → **S-1**. State-layer de segmentos vía capa compartida ✓ (`data-field-segment` estampado en soma) |
| 11 | Composición interna | **el mandato** | `FieldProvider` consumido en soma (merge) ✓ — pero **sin wrapper Field a nivel DOM/eidos**: `[data-field][data-size]` ausente (sondado en la familia), Label propio en vez de la base Field → **S-2** (mandato B.1 ya registrado) |
| 11 | Composición interna | **el mandato** | `FieldProvider` consumido en soma (merge) ✓ — pero **sin wrapper Field a nivel DOM/eidos**: `[data-field][data-size]` ausente (sondado en la familia), Label propio en vez de la base Field → **S-2** (mandato B.1 ya registrado) |
| 12 | Estados y ciclo | ✓ | invalid/disabled/readonly/required en data-attrs por parte; `readonlySegments` granular; hidden-input para FormData (`name` + ISO) ✓; controlled/uncontrolled ✓ |
| 12 | Estados y ciclo | ✓ | invalid/disabled/readonly/required en data-attrs por parte; `readonlySegments` granular; hidden-input para FormData (`name` + ISO) ✓; controlled/uncontrolled ✓ |
| 13 | Disciplina runtime | ✓ | 0 timers/listeners crudos a nivel grep en 57KB de provider; memoria institucional dentro (incidencia range-placeholder 2026-05-20 #1 documentada con su porqué) |
| 13 | Disciplina runtime | ✓ | 0 timers/listeners crudos a nivel grep en 57KB de provider; memoria institucional dentro (incidencia range-placeholder 2026-05-20 #1 documentada con su porqué) |
| 14 | ~~Demo testbed~~ | fuera de alcance | Demos excluidas de la auditoría (corrección 2026-07-07) |
| 14 | ~~Demo testbed~~| fuera de alcance | Demos excluidas de la auditoría (corrección 2026-07-07) |
| 15 | Docs + tests | ✓ | README en AMBOS niveles (soma 14KB con `## Sema events`; eidos 4KB) — el patrón que S-4 de button propone ratificar; tests de provider (8KB) ✓ |
| 15 | Docs + tests | ✓ | README en AMBOS niveles (soma 14KB con `## Sema events`; eidos 4KB) — el patrón que S-4 de button propone ratificar; tests de provider (8KB) ✓ |
| 16 | i18n | ✓ | `langs.ts` propio; `texts` de morfo localizados; formatter por `locale` reactivo; tipos `$libs/days` (doctrina, nunca @internationalized/date) |
| 16 | i18n | ✓ | `langs.ts` propio; `texts` de morfo localizados; formatter por `locale` reactivo; tipos `$libs/days` (doctrina, nunca @internationalized/date) |
## Hallazgos y propuestas de solución — del componente
## Hallazgos y propuestas de solución — del componente
| **F-1** | Teclado de segmento sin `Home`/`End` (APG spinbutton los recomienda: saltar a `aria-valuemin`/`max`) | Añadir `{ key: 'Home', action: 'set-min' }` / `{ key: 'End', action: 'set-max' }` al morfo + implementación en provider; extensivo a time-field/color-field (misma familia de segmentos) |
| **F-1** | Teclado de segmento sin `Home`/`End` (APG spinbutton los recomienda: saltar a `aria-valuemin`/`max`) | Añadir `{ key: 'Home', action: 'set-min' }` / `{ key: 'End', action: 'set-max' }` al morfo + implementación en provider; extensivo a time-field/color-field (misma familia de segmentos) |
| **F-2** | El outline de focus lleva `outline-offset: 0` hardcodeado (razonado: flush, sin hueco) y no participa `--focus-ring-inner-width` | Sin acción si S-1 canoniza el modelo outline-flush para segment-fields (el 0 es semántica del modelo, anotarlo como literal razonado); si S-1 devuelve a box-shadow, se resuelve solo |
| **F-2** | El outline de focus lleva `outline-offset: 0` hardcodeado (razonado: flush, sin hueco) y no participa `--focus-ring-inner-width`| Sin acción si S-1 canoniza el modelo outline-flush para segment-fields (el 0 es semántica del modelo, anotarlo como literal razonado); si S-1 devuelve a box-shadow, se resuelve solo |
| **F-3** | Alturas re-declaradas: el recipe date-field duplica `height-{xs..xl}` = `var(--size-{k}-control-height)` que la familia field ya expone | Cubierta por **S-2** (des-duplicación de familia — mandato B.1) |
| **F-3** | Alturas re-declaradas: el recipe date-field duplica `height-{xs..xl}` = `var(--size-{k}-control-height)` que la familia field ya expone | Cubierta por **S-2** (des-duplicación de familia — mandato B.1) |
| **S-1** | **Doctrina §32 vs realidad de los segment-fields**: §32/changelog dicen "los campos quedan en box-shadow (anillo interior parametrizable)", pero date-field migró a **outline flush** con razón técnica sólida y documentada (un border/box-shadow transicionado parpadea en cada increment por el blur/refocus del segmento; el outline sobrevive forced-colors). Afecta a toda la familia de segment-fields (date/time/color y sus range) | **Decisión doctrinal** (checkpoint post-barrido): (a) matizar §32 — *text-fields = dos anillos box-shadow; segment-fields = outline flush* (canonizar la razón del flicker, nota fechada en changelog §32) — **recomendada**; o (b) devolver los segment-fields a box-shadow resolviendo el flicker de otra forma. El barrido debe censar qué hace cada field (time/color/search/password/mask) para dimensionar |
| **S-1** | **Doctrina §32 vs realidad de los segment-fields**: §32/changelog dicen "los campos quedan en box-shadow (anillo interior parametrizable)", pero date-field migró a **outline flush** con razón técnica sólida y documentada (un border/box-shadow transicionado parpadea en cada increment por el blur/refocus del segmento; el outline sobrevive forced-colors). Afecta a toda la familia de segment-fields (date/time/color y sus range) | **Decisión doctrinal** (checkpoint post-barrido): (a) matizar §32 — _text-fields = dos anillos box-shadow; segment-fields = outline flush_ (canonizar la razón del flicker, nota fechada en changelog §32) — **recomendada**; o (b) devolver los segment-fields a box-shadow resolviendo el flicker de otra forma. El barrido debe censar qué hace cada field (time/color/search/password/mask) para dimensionar |
| **S-2** | **Composición Field (mandato B.1, evidencia por-componente)**: el soma participa de `FieldProvider` (merge ✓) pero el DOM no envuelve en `[data-field][data-size]`; Label propio; alturas duplicadas; el token de familia `--field-segment-height` (size-driven, TSC scope `size:`) no puede aterrizar hasta esto | Ya mandatado en `next-features.md`: incorporar la base Field (wrapper + label/helper/error), token de familia con scope `size:`, morir las copias `height-{k}` y `segment-height` per-componente. Esta ficha aporta la lista concreta de piezas de date-field a tocar |
| **S-2** | **Composición Field (mandato B.1, evidencia por-componente)**: el soma participa de `FieldProvider` (merge ✓) pero el DOM no envuelve en `[data-field][data-size]`; Label propio; alturas duplicadas; el token de familia `--field-segment-height` (size-driven, TSC scope `size:`) no puede aterrizar hasta esto | Ya mandatado en `next-features.md`: incorporar la base Field (wrapper + label/helper/error), token de familia con scope `size:`, morir las copias `height-{k}` y `segment-height` per-componente. Esta ficha aporta la lista concreta de piezas de date-field a tocar |
| 1 | Composición | ✓ **el patrón canónico** | Compone date-field + calendar + Popover (de-dialoged 2026-06-27: Popover = único `role=dialog`) ✓; partes propias solo para lo suyo (Trigger con `aria-haspopup=dialog`, MonthView/YearView del view-switch) |
| 1 | Composición | ✓ **el patrón canónico** | Compone date-field + calendar + Popover (de-dialoged 2026-06-27: Popover = único `role=dialog`) ✓; partes propias solo para lo suyo (Trigger con `aria-haspopup=dialog`, MonthView/YearView del view-switch) |
| 3 | Coherencia semántica | ✓ **la referencia SYS-11** | **`expression: "delegated"`** (:18 — el canon 2026-05-26 aplicado) + UN evento propio `commit-reset` neutral (:30-36) = exactamente la regla "el compuesto declara solo los gestos que el hijo no posee". Los commit-select/set suenan por los embebidos (date-field pack ✓ tuneado — este picker suena bien HOY) |
| 3 | Coherencia semántica | ✓ **la referencia SYS-11** | **`expression: "delegated"`** (:18 — el canon 2026-05-26 aplicado) + UN evento propio `commit-reset` neutral (:30-36) = exactamente la regla "el compuesto declara solo los gestos que el hijo no posee". Los commit-select/set suenan por los embebidos (date-field pack ✓ tuneado — este picker suena bien HOY) |
| 8 | Contrato morfo | ✓ | apg ×2 (dialog + grid); censo de literales ingleses: LIMPIO (a diferencia del drp) |
| 8 | Contrato morfo | ✓ | apg ×2 (dialog + grid); censo de literales ingleses: LIMPIO (a diferencia del drp) |
| 12 | Estados | hallazgo máquina | R-1.3 readonly sin materializar (patrón familia) |
| 12 | Estados | hallazgo máquina | R-1.3 readonly sin materializar (patrón familia) |
| 11 | Composición interna | ✓ menor | Borrower menor de calendar (4 tokens) — SYS-8 lo absorbe |
| 11 | Composición interna | ✓ menor | Borrower menor de calendar (4 tokens) — SYS-8 lo absorbe |
| 6 | Sema | ✓ by-design | Delegado; la asimetría del trío (SYS-3) le afectará cuando exista time-picker equivalente |
| 6 | Sema | ✓ by-design | Delegado; la asimetría del trío (SYS-3) le afectará cuando exista time-picker equivalente |
| resto | | ✓ | Hereda contratos de los embebidos; tests ✓; README ×2 ✓ |
| resto | | ✓ | Hereda contratos de los embebidos; tests ✓; README ×2 ✓ |
| **F-1** | readonly sin estilo (R-1.3) | Patrón familia (composición Field, SYS-6) |
| **F-1**| readonly sin estilo (R-1.3) | Patrón familia (composición Field, SYS-6) |
| **Nota ✓✓** | `expression: 'delegated'` + evento-propio-mínimo = EL ejemplo a citar en el canon SYS-11 | Referencia para el fix de los field-ranges |
| **Nota ✓✓** | `expression: 'delegated'` + evento-propio-mínimo = EL ejemplo a citar en el canon SYS-11 | Referencia para el fix de los field-ranges |
| 1 | Composición | ✓ ejemplar | Cada endpoint es un DateField COMPLETO ligado a `provider.startValue/endValue` (README :44); `Segment` re-export directo del single (exports :5-7) — cero re-implementación de la maquinaria de segmentos; validación/placeholder/granularity/locale compartidos simétricamente |
| 1 | Composición | ✓ ejemplar | Cada endpoint es un DateField COMPLETO ligado a `provider.startValue/endValue` (README :44); `Segment` re-export directo del single (exports :5-7) — cero re-implementación de la maquinaria de segmentos; validación/placeholder/granularity/locale compartidos simétricamente |
| 2 | Naming | ✓ + dato de censo | `value: DateRange` + `onValueChange` ✓; **`onStartValueChange`/`onEndValueChange`** — PRIMER patrón de callbacks por sub-valor del catálogo (censar en otros dobles); `placeholder` compartido único (la incidencia 2026-05-20 #1 — el END auto-paginaba al START — documentada en el provider del single con la regla `false` en endpoints) |
| 2 | Naming | ✓ + dato de censo | `value: DateRange` + `onValueChange` ✓; **`onStartValueChange`/`onEndValueChange`** — PRIMER patrón de callbacks por sub-valor del catálogo (censar en otros dobles); `placeholder` compartido único (la incidencia 2026-05-20 #1 — el END auto-paginaba al START — documentada en el provider del single con la regla `false` en endpoints) |
| 3 | Coherencia semántica | ✓ delegada + **cuestión de diseño** | El morfo propio declara 0 eventos: la semántica vive en los singles embebidos (cada endpoint comete su `commit-set` al fijarse). **F-1**: NO existe evento de *rango completo* — fijar el segundo extremo (el momento terminal del gesto, cuando `{start,end}` queda sellado) suena igual que fijar el primero. ¿Debe el sellado del par tener su propio commit (fulfill) o bastan los dos del single? → veredicto |
| 3 | Coherencia semántica | ✓ delegada + **cuestión de diseño** | El morfo propio declara 0 eventos: la semántica vive en los singles embebidos (cada endpoint comete su `commit-set` al fijarse). **F-1**: NO existe evento de _rango completo_ — fijar el segundo extremo (el momento terminal del gesto, cuando `{start,end}` queda sellado) suena igual que fijar el primero. ¿Debe el sellado del par tener su propio commit (fulfill) o bastan los dos del single? → veredicto |
| 4 | API | ✓ | `validate: DateRangeValidator` + `onInvalid: DateOnInvalid` + min/max; hereda la razón de invalidez SIN tipar del single (censo validación) |
| 4 | API | ✓ | `validate: DateRangeValidator` + `onInvalid: DateOnInvalid` + min/max; hereda la razón de invalidez SIN tipar del single (censo validación) |
| 6 | Sema | condicionada | Sin pack propio (correcto: delega); el carácter depende del pack del single embebido → date ✓ tuneado. La decisión SYS-3 del trío debe cubrir los ranges explícitamente |
| 6 | Sema | condicionada | Sin pack propio (correcto: delega); el carácter depende del pack del single embebido → date ✓ tuneado. La decisión SYS-3 del trío debe cubrir los ranges explícitamente |
| 8 | Contrato morfo | ✓ | APG spinbutton citado; ARIA heredada de los segments del single (README :58) |
| 8 | Contrato morfo | ✓ | APG spinbutton citado; ARIA heredada de los segments del single (README :58) |
| 5, 7, 9-15 | | heredan del single | Wrapper de composición: tokens/recipe/i18n/a11y = los de date-field; focus delegado a los embebidos ✓ |
| 5, 7, 9-15 | | heredan del single | Wrapper de composición: tokens/recipe/i18n/a11y = los de date-field; focus delegado a los embebidos ✓ |
| **F-1** | Sin evento de rango-completo (el sellado del par no se distingue del commit de un extremo) | Decidir en checkpoint: (a) declarar `commit-set` a nivel range cuando ambos extremos fijan (fulfill — el "cierre" perceptible), o (b) documentar que los dos commits del single bastan. Si (a), resolver los DOS ranges en el mismo fix |
| **F-1** | Sin evento de rango-completo (el sellado del par no se distingue del commit de un extremo) | Decidir en checkpoint: (a) declarar `commit-set` a nivel range cuando ambos extremos fijan (fulfill — el "cierre" perceptible), o (b) documentar que los dos commits del single bastan. Si (a), resolver los DOS ranges en el mismo fix |
| **F-2** | Razón de invalidez sin tipar (heredada) | Veredicto de validación de la familia (nivelar a las razones tipadas de color-field) |
| **F-2** | Razón de invalidez sin tipar (heredada) | Veredicto de validación de la familia (nivelar a las razones tipadas de color-field) |
| **F-3** | Compuesto SIN `expression` declarada — el canon existe (`'delegated'`, morfo/types.ts, decisión 2026-05-26) y sus primos date-picker/drp lo usan | Añadir `expression: 'delegated'` (una línea) — SYS-11 |
| **F-3** | Compuesto SIN `expression` declarada — el canon existe (`'delegated'`, morfo/types.ts, decisión 2026-05-26) y sus primos date-picker/drp lo usan | Añadir `expression: 'delegated'` (una línea) — SYS-11 |
## Escalan al sistema
## Escalan al sistema
SYS-3 (la decisión del trío debe nombrar a los ranges) · _naming (callbacks por sub-valor) · patrón "delegación semántica por composición" — segundo caso documentado tras field-langs (el morfo compuesto NO re-declara los eventos del embebido) → regla candidata para el canon.
SYS-3 (la decisión del trío debe nombrar a los ranges) · \_naming (callbacks por sub-valor) · patrón "delegación semántica por composición" — segundo caso documentado tras field-langs (el morfo compuesto NO re-declara los eventos del embebido) → regla candidata para el canon.
| 1 | Composición | ✓ con razón in-place | `expression: "delegated"` (:15) + `commit-reset` propio (:25-31) ✓ patrón canónico; **MonthView NO duplica month-grid**: es el "month-view range bridge for `kind='month'`" con su state machine empty/pending/complete + year stepper (razón :146-148) y partes flat por namespacing `month-view-*` (:165-166) — funcionalidad distinta (rango de meses vs mes único), legítima y documentada |
| 1 | Composición | ✓ con razón in-place | `expression: "delegated"` (:15) + `commit-reset` propio (:25-31) ✓ patrón canónico; **MonthView NO duplica month-grid**: es el "month-view range bridge for `kind='month'`" con su state machine empty/pending/complete + year stepper (razón :146-148) y partes flat por namespacing `month-view-*` (:165-166) — funcionalidad distinta (rango de meses vs mes único), legítima y documentada |
| 3 | Coherencia semántica | ✓ delegada | Suena por date-field (tuneado hoy); la coherencia del cuarteto range depende de SYS-3 |
| 3 | Coherencia semántica | ✓ delegada | Suena por date-field (tuneado hoy); la coherencia del cuarteto range depende de SYS-3 |
| 8 | Contrato morfo | **2 hallazgos** | **F-1**: aria-label `v.literal('Select month range')` (:160) y `v.literal('Select year range')` (:248) — inglés HARDCODEADO no localizable, cuando calendar usa `v.commonRef(key, fallback)`; único caso de la familia · MonthViewCell/YearViewCell declaran `data-range-start/end` (:228-229,:316-317) ✓ caps; **verificar en fase de fixes** cómo pinta eidos el TRAMO MEDIO del rango pendiente (sin hook data-* de in-range visible en el morfo) |
| 8 | Contrato morfo | **2 hallazgos** | **F-1**: aria-label `v.literal('Select month range')` (:160) y `v.literal('Select year range')` (:248) — inglés HARDCODEADO no localizable, cuando calendar usa `v.commonRef(key, fallback)`; único caso de la familia · MonthViewCell/YearViewCell declaran `data-range-start/end` (:228-229,:316-317) ✓ caps; **verificar en fase de fixes** cómo pinta eidos el TRAMO MEDIO del rango pendiente (sin hook data-\* de in-range visible en el morfo) |
| 10 | Theming | **hallazgo doctrina** | **F-2**: su CSS re-declara privados AJENOS `--_calendar-range-{start,end}-*` con roles hardcodeados (affirm/accent :21-28; secondary :111-114) — cross-component private tuning: el anti-patrón "override del compuesto desde el host" (doctrina respect-composed-styles) en su forma más densa; + re-declara alturas de date-field per-size ×5 |
| 10 | Theming | **hallazgo doctrina** | **F-2**: su CSS re-declara privados AJENOS `--_calendar-range-{start,end}-*` con roles hardcodeados (affirm/accent :21-28; secondary :111-114) — cross-component private tuning: el anti-patrón "override del compuesto desde el host" (doctrina respect-composed-styles) en su forma más densa; + re-declara alturas de date-field per-size ×5 |
| 12 | Estados | ✓ con memoria | Placeholder compartido de endpoints (incidencia 2026-05-20 #1) documentado en el provider del single ✓ |
| 12 | Estados | ✓ con memoria | Placeholder compartido de endpoints (incidencia 2026-05-20 #1) documentado en el provider del single ✓ |
| 9 | A11y | ✓ con F-1 | Content: Escape close + Tab/Shift+Tab trap declarado (:140-142) |
| 9 | A11y | ✓ con F-1 | Content: Escape close + Tab/Shift+Tab trap declarado (:140-142) |
| **F-1** | 2 aria-labels en inglés hardcodeado (`v.literal`) — no localizables | Migrar a `v.commonRef('date-range-picker.month-range', 'Select month range')` + entrada de texts; censar `v.literal` con texto humano en el resto del catálogo (guard candidato: literal con espacios = sospechoso) |
| **F-1** | 2 aria-labels en inglés hardcodeado (`v.literal`) — no localizables | Migrar a `v.commonRef('date-range-picker.month-range', 'Select month range')` + entrada de texts; censar `v.literal` con texto humano en el resto del catálogo (guard candidato: literal con espacios = sospechoso) |
| **F-2** | Re-declaración masiva de privados ajenos (`--_calendar-range-*` con roles + alturas date-field ×5) | SYS-8 (capa calendar-surface con los tokens de rango COMO API pública de la capa) + SYS-6 (token de familia field); las dos capas formales eliminan las copias |
| **F-2** | Re-declaración masiva de privados ajenos (`--_calendar-range-*` con roles + alturas date-field ×5) | SYS-8 (capa calendar-surface con los tokens de rango COMO API pública de la capa) + SYS-6 (token de familia field); las dos capas formales eliminan las copias |
| **F-3** | Hook del tramo medio del rango sin declarar en morfo (solo caps) | Verificación dirigida al ejecutar SYS-8: si eidos pinta in-range por selector no-morfo, es drift del contrato |
| **F-3** | Hook del tramo medio del rango sin declarar en morfo (solo caps) | Verificación dirigida al ejecutar SYS-8: si eidos pinta in-range por selector no-morfo, es drift del contrato |
| 3 | Coherencia semántica | ✓ **el overlay-handle completo** | 6 eventos: emerge present/close + la trilogía handle pick/drag/drop **más resize** — el arrastre del drawer (snap points) tiene contrato semántico COMPLETO, en contraste directo con float-panel (mismo tipo de gesto, 28 mutaciones sin señal — su F-1). Drawer es la referencia interna para ese fix; pack ✓ |
| 3 | Coherencia semántica | ✓ **el overlay-handle completo** | 6 eventos: emerge present/close + la trilogía handle pick/drag/drop **más resize** — el arrastre del drawer (snap points) tiene contrato semántico COMPLETO, en contraste directo con float-panel (mismo tipo de gesto, 28 mutaciones sin señal — su F-1). Drawer es la referencia interna para ese fix; pack ✓ |
| 2 | Naming | **4 datos de censo** | **`defaultValue`** (types :16-21 — PRIMER uncontrolled-initial del catálogo, doc React-style "read once at mount") → ¿canon o redundante con `$bindable(inicial)`? · par `onValueChange` (cambia el comprometido) + `onValueCommit` (cada commit) :60-63 — distinción sutil que el resto no hace · **`onValueRevert: () => void`** crudo SIN payload (:64-65 — encaja la regla "crudo solo señal sin dato" ✓) · `editing/onEditingChange` par bindable ✓ |
| 2 | Naming | **4 datos de censo** | **`defaultValue`** (types :16-21 — PRIMER uncontrolled-initial del catálogo, doc React-style "read once at mount") → ¿canon o redundante con `$bindable(inicial)`? · par `onValueChange` (cambia el comprometido) + `onValueCommit` (cada commit) :60-63 — distinción sutil que el resto no hace · **`onValueRevert: () => void`** crudo SIN payload (:64-65 — encaja la regla "crudo solo señal sin dato" ✓) · `editing/onEditingChange` par bindable ✓ |
| 3 | Coherencia semántica | ✓ | `shift-enter-mode` (shift, sin intent — cambio de contexto ✓) · `commit-save` affirm · `commit-cancel`**neutral** (commit exige intent por política de familia; neutral es correcto — verificado contra la doctrina) |
| 3 | Coherencia semántica | ✓ | `shift-enter-mode` (shift, sin intent — cambio de contexto ✓) · `commit-save` affirm · `commit-cancel`**neutral** (commit exige intent por política de familia; neutral es correcto — verificado contra la doctrina) |
| 4 | API | ✓ rica | `activationMode: focus\|click\|dblclick\|none` + `submitMode: enter\|blur\|both\|none` (:32-49); `selectOnFocus`; hidden-input por `name` (:26-27) |
| 4 | API | ✓ rica | `activationMode: focus\|click\|dblclick\|none` + `submitMode: enter\|blur\|both\|none` (:32-49); `selectOnFocus`; hidden-input por `name` (:26-27) |
| 11 | Composición interna | verificar | Como tags-input: sin mención de OR-merge con Field en types → verificación dirigida (pass SYS-6) |
| 11 | Composición interna | verificar | Como tags-input: sin mención de OR-merge con Field en types → verificación dirigida (pass SYS-6) |
| 12 | Estados | ✓ con gap menor | editing/empty/disabled/readonly/invalid ✓; **F-1**: `maxLength` (:54-55) SIN señal de overflow (0 hits) — textarea tiene el contrato `signal-warn-count-overflow` untilFix para lo mismo |
| 12 | Estados | ✓ con gap menor | editing/empty/disabled/readonly/invalid ✓; **F-1**: `maxLength` (:54-55) SIN señal de overflow (0 hits) — textarea tiene el contrato `signal-warn-count-overflow` untilFix para lo mismo |
| 13 | Runtime | ✓ **por diseño** | `autoResize` dimensiona por `ch` desde el valor + `min-width: 2ch` (provider :374-377) — **cero medición de DOM** (evita la clase de reflow de textarea F-1 por construcción); el contraste correcto: ch para una línea, medición-diferida para multilínea |
| 13 | Runtime | ✓ **por diseño** | `autoResize` dimensiona por `ch` desde el valor + `min-width: 2ch` (provider :374-377) — **cero medición de DOM** (evita la clase de reflow de textarea F-1 por construcción); el contraste correcto: ch para una línea, medición-diferida para multilínea |
| 14 | Docs+tests | ✓ | README ×2, suite ✓ (+ el error de tipos baseline a absorber) |
| 14 | Docs+tests | ✓ | README ×2, suite ✓ (+ el error de tipos baseline a absorber) |
| **F-1** | `maxLength` sin contrato de overflow (asimetría con textarea) | Adoptar `signal-warn-count-overflow` del hermano (untilFix + persistent-trace) o documentar por qué no aplica al in-place |
| **F-1** | `maxLength` sin contrato de overflow (asimetría con textarea) | Adoptar `signal-warn-count-overflow` del hermano (untilFix + persistent-trace) o documentar por qué no aplica al in-place |
| **F-2** | `defaultValue` — patrón uncontrolled único en el catálogo | Veredicto _naming: ¿se canoniza (y entonces quién más lo necesita) o se poda (el `$bindable(inicial)` de Svelte 5 cubre el caso)? |
| **F-2** | `defaultValue` — patrón uncontrolled único en el catálogo | Veredicto \_naming: ¿se canoniza (y entonces quién más lo necesita) o se poda (el `$bindable(inicial)` de Svelte 5 cubre el caso)? |
| **F-3** | Error de tipos preexistente en su suite (baseline check) | Fix trivial en fase de fixes |
| **F-3** | Error de tipos preexistente en su suite (baseline check) | Fix trivial en fase de fixes |
| **F-4** | Participación Field sin documentar | Verificación dirigida (con tags-input) |
| **F-4** | Participación Field sin documentar | Verificación dirigida (con tags-input) |
## Escalan al sistema
## Escalan al sistema
_naming (defaultValue · el par Change/Commit · Revert crudo ✓ como regla) · patrón "ch-sizing vs medición-diferida" como doctrina de autoResize (exportable).
\_naming (defaultValue · el par Change/Commit · Revert crudo ✓ como regla) · patrón "ch-sizing vs medición-diferida" como doctrina de autoResize (exportable).
| 3 | Coherencia semántica | ✓ declarado | commit-submit (cargar más presumible) + shift-navigate; family-default escrito; APG feed ✓ (el patrón oficial existe y está citado) |
| 3 | Coherencia semántica | ✓ declarado | commit-submit (cargar más presumible) + shift-navigate; family-default escrito; APG feed ✓ (el patrón oficial existe y está citado) |
- **Máquina**: PASS con `R-2.7` (*`line-height: 1.2` literal sin anotar*) · tests **0** · README soma **✗** / eidos ✓
- **Máquina**: PASS con `R-2.7` (_`line-height: 1.2` literal sin anotar_) · tests **0** · README soma **✗** / eidos ✓
- **Corrección sobre el pass superficial**: el antiguo F-4 ("semántica del conmutador sin declarar") era FALSO — el morfo la declara y razona (abajo). La ficha superficial no había leído el contrato.
- **Corrección sobre el pass superficial**: el antiguo F-4 ("semántica del conmutador sin declarar") era FALSO — el morfo la declara y razona (abajo). La ficha superficial no había leído el contrato.
| 1 | Composición | ✓ ejemplar | Patrón picker A27 citado (morfo :9-13): compone el eidos `<Field>` + conmutador in-control cuya FORMA se adapta (segmented ≤3 idiomas · Select ghost >3 o forzado); **Switcher `kind: 'private'`** con razón doble (:77-84): el `commit-toggle` del ToggleGroup es semántica EQUIVOCADA para navegación de contexto + su chrome se colaría en el control — radiogroup propio en segmented |
| 1 | Composición | ✓ ejemplar | Patrón picker A27 citado (morfo :9-13): compone el eidos `<Field>` + conmutador in-control cuya FORMA se adapta (segmented ≤3 idiomas · Select ghost >3 o forzado); **Switcher `kind: 'private'`** con razón doble (:77-84): el `commit-toggle` del ToggleGroup es semántica EQUIVOCADA para navegación de contexto + su chrome se colaría en el control — radiogroup propio en segmented |
| 3 | Coherencia semántica | ✓ **declarada y razonada** | `shift-navigate` con cap. 27 citado (cambio de régimen entre valores por-idioma, NO fijación — :39-52); `expression: 'family-default'` RAZONADO (:29-33): el settle visual es la base de shift, el tap del press lo pone el pack del ToggleGroup compuesto — decisión sema COMPOSICIONAL documentada (modelo para el veredicto SYS-3: family-default puede ser elección arquitectural, no hueco) |
| 3 | Coherencia semántica | ✓ **declarada y razonada** | `shift-navigate` con cap. 27 citado (cambio de régimen entre valores por-idioma, NO fijación — :39-52); `expression: 'family-default'` RAZONADO (:29-33): el settle visual es la base de shift, el tap del press lo pone el pack del ToggleGroup compuesto — decisión sema COMPOSICIONAL documentada (modelo para el veredicto SYS-3: family-default puede ser elección arquitectural, no hueco) |
| 4 | API | ✓ | `languages` + `required: LangCode[]` + `missingRequired` derivado en snippet (validación por idioma) |
| 4 | API | ✓ | `languages` + `required: LangCode[]` + `missingRequired` derivado en snippet (validación por idioma) |
| **Nota ✓✓** | La razón escrita del `family-default` composicional (:29-33) es EL modelo para el veredicto SYS-3 | Citarla en el checkpoint: "default legítimo cuando el carácter lo aporta el compuesto" |
| **Nota ✓✓** | La razón escrita del `family-default` composicional (:29-33) es EL modelo para el veredicto SYS-3 | Citarla en el checkpoint: "default legítimo cuando el carácter lo aporta el compuesto" |
## Escalan al sistema
## Escalan al sistema
@ -38,4 +38,4 @@ SYS-2 · SYS-4 · **SYS-3 gana su segundo criterio documentado** (composición a
| 1 | Composición vs declaración | ✓ | 10 partes como children compositivos (morfo field.ts:13-180); indicators/error con render condicional por prop (`onlyWhenRequired/Optional/Invalid`, types.ts:89/96/121) — no árboles declarativos |
| 1 | Composición vs declaración | ✓ | 10 partes como children compositivos (morfo field.ts:13-180); indicators/error con render condicional por prop (`onlyWhenRequired/Optional/Invalid`, types.ts:89/96/121) — no árboles declarativos |
| 2 | Naming de props | ✓ canónico | `value`+`onValueChange` (types.ts:107-109 — EL patrón del catálogo); `disabled/readonly/required/invalid` planos; overrides `dirty/touched/state` documentados (types.ts:63-78); patrón `onlyWhen*`×3 coherente → alimenta [_naming.md](./_naming.md) |
| 2 | Naming de props | ✓ canónico | `value`+`onValueChange` (types.ts:107-109 — EL patrón del catálogo); `disabled/readonly/required/invalid` planos; overrides `dirty/touched/state` documentados (types.ts:63-78); patrón `onlyWhen*`×3 coherente → alimenta [\_naming.md](./_naming.md) |
| 3 | Coherencia semántica | ✓ con hallazgo | La base NO emite eventos y lo DECLARA en scope (field.ts:7 ✓); pero **F-2**: Field.Input (texto de una línea) no tiene contrato de commit mientras textarea tiene `commit-submit` (textarea.ts, verificado) — el par de inputs de texto es asimétrico |
| 3 | Coherencia semántica | ✓ con hallazgo | La base NO emite eventos y lo DECLARA en scope (field.ts:7 ✓); pero **F-2**: Field.Input (texto de una línea) no tiene contrato de commit mientras textarea tiene `commit-submit` (textarea.ts, verificado) — el par de inputs de texto es asimétrico |
| 4 | API normalizada | ✓ | Estado compuesto con precedencia documentada y verificada (provider 145-152: explicit > invalid > disabled > readonly); snippetProps completos (154-164); OR-merge con Form (types.ts:36-38, provider 138-142 markTouched→form) |
| 4 | API normalizada | ✓ | Estado compuesto con precedencia documentada y verificada (provider 145-152: explicit > invalid > disabled > readonly); snippetProps completos (154-164); OR-merge con Form (types.ts:36-38, provider 138-142 markTouched→form) |
| 5 | Tokens usados | ✓ | Recipe 58 claves TODAS canónicas §1 (control-height-{k}, padding-inline, gap, radius, font-size 1:1, floating-label-top temable, segment-active-* de V4); css consume solo `--field-*` |
| 5 | Tokens usados | ✓ | Recipe 58 claves TODAS canónicas §1 (control-height-{k}, padding-inline, gap, radius, font-size 1:1, floating-label-top temable, segment-active-_ de V4); css consume solo `--field-_` |
| 6 | Parte semántica | ✓ by-contract | Sin sema por scope (los commits los emiten los fields concretos) — coherente, salvo F-2 |
| 6 | Parte semántica | ✓ by-contract | Sin sema por scope (los commits los emiten los fields concretos) — coherente, salvo F-2 |
| 8 | Contrato morfo | **hallazgo doble** | **F-1**: enum `data-state` de 5 valores emitido en Provider/Control/Input (field.ts:24-27/88-91/106-110) con **0 consumidores en TODO el CSS de eidos** (verificado catálogo entero) — el estilo real va por los flags de presencia (data-invalid ×4, data-disabled ×7 en field.css); además emite `data-state="idle"` en reposo — el patrón exacto que button PURGÓ (su morfo documenta el clobbering, audit 05-27). Doble codificación: enum muerto + flags vivos |
| 8 | Contrato morfo | **hallazgo doble** | **F-1**: enum `data-state` de 5 valores emitido en Provider/Control/Input (field.ts:24-27/88-91/106-110) con **0 consumidores en TODO el CSS de eidos** (verificado catálogo entero) — el estilo real va por los flags de presencia (data-invalid ×4, data-disabled ×7 en field.css); además emite `data-state="idle"` en reposo — el patrón exacto que button PURGÓ (su morfo documenta el clobbering, audit 05-27). Doble codificación: enum muerto + flags vivos |
| 9 | Accesibilidad | ✓ ejemplar | Auto-wiring completo: label for→input condicional part-present (field.ts:52-56), aria-invalid ariaBoolean (120), aria-busy loading (128-132), aria-required (134-138), ErrorText role=alert+aria-live (157-160); `describedBy` une helper+error-solo-si-invalid y SOBRESCRIBE el morfo con el valor más rico — documentado (provider 344-350 + 371-373) |
| 9 | Accesibilidad | ✓ ejemplar | Auto-wiring completo: label for→input condicional part-present (field.ts:52-56), aria-invalid ariaBoolean (120), aria-busy loading (128-132), aria-required (134-138), ErrorText role=alert+aria-live (157-160); `describedBy` une helper+error-solo-si-invalid y SOBRESCRIBE el morfo con el valor más rico — documentado (provider 344-350 + 371-373) |
| 10 | Adopción theming | ✓ con remanente | Focus: el ÚNICO box-shadow dos-anillos superviviente del catálogo vive aquí → ancla del veredicto SYS-5; states-layer n/a (chrome no interactivo) |
| 10 | Adopción theming | ✓ con remanente | Focus: el ÚNICO box-shadow dos-anillos superviviente del catálogo vive aquí → ancla del veredicto SYS-5; states-layer n/a (chrome no interactivo) |
| 11 | Composición interna | mandato | Es la base que los hijos DEBEN envolver (SYS-6/B.1): la familia de alturas + el chrome label/helper/error existen aquí y los segment-fields los re-implementan |
| 11 | Composición interna | mandato | Es la base que los hijos DEBEN envolver (SYS-6/B.1): la familia de alturas + el chrome label/helper/error existen aquí y los segment-fields los re-implementan |
| 12 | Estados y ciclo | ✓ con gap visual | invalid/disabled/readonly estilados; **`loading` definido en el contrato (morfo+types+aria-busy) pero SIN visual en css** (0 reglas) — estado fantasma a nivel eidos (parte de F-1) |
| 12 | Estados y ciclo | ✓ con gap visual | invalid/disabled/readonly estilados; **`loading` definido en el contrato (morfo+types+aria-busy) pero SIN visual en css** (0 reglas) — estado fantasma a nivel eidos (parte de F-1) |
| **F-1** | Enum `data-state` (5 valores, 3 partes) sin NINGÚN consumidor CSS + `idle` emitido en reposo + `loading` sin visual — doble codificación con los flags | Dos salidas para el veredicto: (a) **podar el enum** (como button: flags de presencia bastan; `loading` se convierte en flag `data-loading` + visual definido) — recomendada por coherencia con la doctrina button; (b) adoptarlo (estilar error/loading/readonly por el enum y podar flags redundantes). En ambas: definir el visual de `loading` que hoy no existe |
| **F-1** | Enum `data-state` (5 valores, 3 partes) sin NINGÚN consumidor CSS + `idle` emitido en reposo + `loading` sin visual — doble codificación con los flags | Dos salidas para el veredicto: (a) **podar el enum** (como button: flags de presencia bastan; `loading` se convierte en flag `data-loading` + visual definido) — recomendada por coherencia con la doctrina button; (b) adoptarlo (estilar error/loading/readonly por el enum y podar flags redundantes). En ambas: definir el visual de `loading` que hoy no existe |
| **F-2** | Field.Input sin contrato de commit vs textarea `commit-submit` — el par texto asimétrico. **Refinado tras leer password-field**: la doctrina de familia YA existe y está documentada con incidente (password-field morfo :40-47 — teclear NO es commit; `commit-submit` al Enter ES el patrón, alineado con editable/number/search/combobox) | Field.Input adopta `commit-submit` (Enter) siguiendo la doctrina escrita de password-field — morfo + provider; el veredicto es de adopción, no de diseño (el diseño ya está canonizado en el hermano) |
| **F-2** | Field.Input sin contrato de commit vs textarea `commit-submit` — el par texto asimétrico. **Refinado tras leer password-field**: la doctrina de familia YA existe y está documentada con incidente (password-field morfo :40-47 — teclear NO es commit; `commit-submit` al Enter ES el patrón, alineado con editable/number/search/combobox) | Field.Input adopta `commit-submit` (Enter) siguiendo la doctrina escrita de password-field — morfo + provider; el veredicto es de adopción, no de diseño (el diseño ya está canonizado en el hermano) |
| **F-3** | El morfo declara `aria-describedby` solo desde helper-text; el valor real (helper+error) lo compone soma en override | Menor: reflejar en el morfo la relación con error-text (severity optional + condición) para que el contrato no infra-declare lo que se renderiza |
| **F-3** | El morfo declara `aria-describedby` solo desde helper-text; el valor real (helper+error) lo compone soma en override | Menor: reflejar en el morfo la relación con error-text (severity optional + condición) para que el contrato no infra-declare lo que se renderiza |
## Escalan al sistema
## Escalan al sistema
- **F-1 alimenta una regla de catálogo**: "enum de estado solo si el CSS lo consume; si los flags bastan, el enum es deuda" — censar en el resto de la re-auditoría quién más emite enums muertos (→ SYS-10 nuevo en _system al cierre de familia).
- **F-1 alimenta una regla de catálogo**: "enum de estado solo si el CSS lo consume; si los flags bastan, el enum es deuda" — censar en el resto de la re-auditoría quién más emite enums muertos (→ SYS-10 nuevo en \_system al cierre de familia).
- SYS-5 (ancla del focus) · SYS-6 (destino del mandato) — ya registrados.
- SYS-5 (ancla del focus) · SYS-6 (destino del mandato) — ya registrados.
| 3/8 | Coherencia semántica | **la brecha teclas↔eventos más grande del catálogo** | **F-1**: declara pick/drop/resize pero NO `drag` (drawer sí lo tiene — la comparación directa: mismo gesto, contrato menor) y sus 28 mutaciones de teclado (move/resize/snap por flechas) no emiten señal — el usuario sordo-perceptual: mueve el panel y el sistema no lo "siente". Drawer = referencia interna del fix |
| 3/8 | Coherencia semántica | **la brecha teclas↔eventos más grande del catálogo** | **F-1**: declara pick/drop/resize pero NO `drag` (drawer sí lo tiene — la comparación directa: mismo gesto, contrato menor) y sus 28 mutaciones de teclado (move/resize/snap por flechas) no emiten señal — el usuario sordo-perceptual: mueve el panel y el sistema no lo "siente". Drawer = referencia interna del fix |
| 10 | Theming | hallazgo | **F-2**: depth SIN estampar (0) — panel flotante que consume `--ring-inset-width` ✓ pero no declara plano |
| 10 | Theming | hallazgo | **F-2**: depth SIN estampar (0) — panel flotante que consume `--ring-inset-width` ✓ pero no declara plano |
| 14 | Docs | gap | Expediente pre-flight a medias (sin Baseline; Comparativa 0 refs; Gaps sin disposición) |
| 14 | Docs | gap | Expediente pre-flight a medias (sin Baseline; Comparativa 0 refs; Gaps sin disposición) |
| **F-1** | 28 mutaciones / 6 eventos (A-3.7); sin `handle-drag` | Sesión de contrato: mapear a `shift.*`/`handle-drag` con la doctrina de holds (agregación al soltar, no evento-por-tecla) — usar drawer como plantilla |
| **F-1** | 28 mutaciones / 6 eventos (A-3.7); sin `handle-drag` | Sesión de contrato: mapear a `shift.*`/`handle-drag` con la doctrina de holds (agregación al soltar, no evento-por-tecla) — usar drawer como plantilla |
| **F-2** | Sin `data-depth` | Pass conjunto de depth |
| **F-2** | Sin `data-depth`| Pass conjunto de depth |
| 2 | Naming | **dato de censo** | **`isPending`/`data-pending`** para async-en-vuelo (morfo :75, types) mientras button/field usan `loading` — DOS nombres para el concepto → _naming; `disableWhenPristine` en Reset ✓ expresivo; `submitCount` en snippet |
| 2 | Naming | **dato de censo** | **`isPending`/`data-pending`** para async-en-vuelo (morfo :75, types) mientras button/field usan `loading` — DOS nombres para el concepto → \_naming; `disableWhenPristine` en Reset ✓ expresivo; `submitCount` en snippet |
| 3 | Coherencia semántica | ✓ **referencia** | `commit-submit` con intent **fulfill** (:16-24 — el cierre terminal donde la doctrina de button dice que vive el intent fuerte ✓); **`signal-warn-invalid`** untilFix + `requiresFocusMove` + persistent-trace con §6.2 y §9.2 citados y el ciclo clearSignal/clearTarget documentado (:26-53) — CUARTO contrato de referencia de la familia; `commit-reset` neutral ✓ |
| 3 | Coherencia semántica | ✓ **referencia** | `commit-submit` con intent **fulfill** (:16-24 — el cierre terminal donde la doctrina de button dice que vive el intent fuerte ✓); **`signal-warn-invalid`** untilFix + `requiresFocusMove` + persistent-trace con §6.2 y §9.2 citados y el ciclo clearSignal/clearTarget documentado (:26-53) — CUARTO contrato de referencia de la familia; `commit-reset` neutral ✓ |
| 4 | API | ✓ | Standard-Schema (el adaptador always-async con `untrack` — antecedente de memoria); estados dirty/touched/submitted proyectados |
| 4 | API | ✓ | Standard-Schema (el adaptador always-async con `untrack` — antecedente de memoria); estados dirty/touched/submitted proyectados |
| 5 | Tokens | ✓ | Gaps por size vía recipe |
| 5 | Tokens | ✓ | Gaps por size vía recipe |
| 6 | Sema | ✓ | Pack ✓ — el submit ES el sitio del carácter |
| 6 | Sema | ✓ | Pack ✓ — el submit ES el sitio del carácter |
| 7 | Animaciones | ✓ | Sin keyframes |
| 7 | Animaciones | ✓ | Sin keyframes |
| 8 | Contrato morfo | ✓ | APG landmark form CORRECTO (:9); `data-{pending,dirty,touched,invalid,submitted}` + aria-busy condicional (:74-88) |
| 8 | Contrato morfo | ✓ | APG landmark form CORRECTO (:9); `data-{pending,dirty,touched,invalid,submitted}` + aria-busy condicional (:74-88) |
| 9 | A11y | ✓ ejemplar | El focus-move del warn aterriza en el form y `focusFirstInvalid()` perfora al primer campo inválido (documentado :43-48); ErrorSummary con títulos singular/plural interpolados (:12-13) |
| 9 | A11y | ✓ ejemplar | El focus-move del warn aterriza en el form y `focusFirstInvalid()` perfora al primer campo inválido (documentado :43-48); ErrorSummary con títulos singular/plural interpolados (:12-13) |
| 10 | Theming | ✓ | Focus o1 contenedor |
| 10 | Theming | ✓ | Focus o1 contenedor |
| 11 | Composición interna | ✓ | ES el otro extremo del OR-merge: Field se registra y deriva dirty/touched/errors de aquí |
| 11 | Composición interna | ✓ | ES el otro extremo del OR-merge: Field se registra y deriva dirty/touched/errors de aquí |
| **F-1** | `isPending` vs `loading` (dos nombres para async-en-vuelo en el catálogo) | Veredicto _naming: elegir uno (nota: `pending` es el término del form nativo/React 19; `loading` el de controles — puede ser DOS conceptos legítimos [form en vuelo vs control ocupado] — decidir si se unifica o se documenta la distinción) |
| **F-1** | `isPending` vs `loading` (dos nombres para async-en-vuelo en el catálogo) | Veredicto \_naming: elegir uno (nota: `pending` es el término del form nativo/React 19; `loading` el de controles — puede ser DOS conceptos legítimos [form en vuelo vs control ocupado] — decidir si se unifica o se documenta la distinción) |
- Ver **trans.md** — misma clase, mismo pass conjunto (expediente compartido + demo snippet). Recordar: `format.getLocale()` NO es reactivo — pasar locale per-call (la memoria del gotcha).
- Ver **trans.md** — misma clase, mismo pass conjunto (expediente compartido + demo snippet). Recordar: `format.getLocale()` NO es reactivo — pasar locale per-call (la memoria del gotcha).
| **F-3** | Sin APG · cero tests | Declarar patrón + suite — con gradient-picker |
| **F-3** | Sin APG · cero tests | Declarar patrón + suite — con gradient-picker |
## Escalan al sistema
## Escalan al sistema
SYS-2 · SYS-9 (cerrar gradient-* como paquete) · SYS-11 (d) · **nota de proceso**: la etiqueta BROKEN del catálogo era un artefacto de demo — al excluir demos, ningún componente del catálogo está BROKEN a nivel de capas.
SYS-2 · SYS-9 (cerrar gradient-\* como paquete) · SYS-11 (d) · **nota de proceso**: la etiqueta BROKEN del catálogo era un artefacto de demo — al excluir demos, ningún componente del catálogo está BROKEN a nivel de capas.
| 3 | Coherencia semántica | ✓ con **contradicción de vocabulario** | Eventos propios: `commit-reset` neutral (:29-35) + **`commit-save` FULFILL** (:42-48 — guardar a presets = fulfill, coherente con la doctrina del cierre terminal ✓) + `commit-remove`; **F-1**: declara `expression: 'delegated'` (:19 = "expression lives in children's packs") teniendo 3 eventos PROPIOS y pack PROPIO — la declaración miente; debería ser `'pack'`. El guard `morfo-vocabulary-check` NO lo caza (su regla :37-41 da OK con solo existir el pack, sin cruzar coherencia expression↔pack) |
| 3 | Coherencia semántica | ✓ con **contradicción de vocabulario** | Eventos propios: `commit-reset` neutral (:29-35) + **`commit-save` FULFILL** (:42-48 — guardar a presets = fulfill, coherente con la doctrina del cierre terminal ✓) + `commit-remove`; **F-1**: declara `expression: 'delegated'` (:19 = "expression lives in children's packs") teniendo 3 eventos PROPIOS y pack PROPIO — la declaración miente; debería ser `'pack'`. El guard `morfo-vocabulary-check` NO lo caza (su regla :37-41 da OK con solo existir el pack, sin cruzar coherencia expression↔pack) |
| 9 | A11y | gap | Sin APG (stops draggables ≈ slider multi-thumb) → F-3 |
| 9 | A11y | gap | Sin APG (stops draggables ≈ slider multi-thumb) → F-3 |
| 14 | Docs+tests | **gap doble de proceso** | Expediente pre-flight nunca escrito (F-1.1–1.4) + **cero tests** — la iniciativa quedó a medias (SYS-9) |
| 14 | Docs+tests | **gap doble de proceso** | Expediente pre-flight nunca escrito (F-1.1–1.4) + **cero tests** — la iniciativa quedó a medias (SYS-9) |
| 6 | Sema | ✓ | Pack propio ✓ (funciona pese a F-1: el engine registra por cascada, no por expression) |
| 6 | Sema | ✓ | Pack propio ✓ (funciona pese a F-1: el engine registra por cascada, no por expression) |
| 8 | Contrato morfo | **hallazgo nuevo** | **F-2**: `apg:` AUSENTE (censo = 0) y la máquina lo dio por PASS — el censo A-1.4 del informe no lo lista: segundo agujero de regla detectado por la re-auditoría (con el A-3.1 de pin-input) |
| 8 | Contrato morfo | **hallazgo nuevo** | **F-2**: `apg:` AUSENTE (censo = 0) y la máquina lo dio por PASS — el censo A-1.4 del informe no lo lista: segundo agujero de regla detectado por la re-auditoría (con el A-3.1 de pin-input) |
| **F-1** | 6 hooks-clase | Migrar a `data-image-picker-*` con el codemod del censo SYS-1 (no parche aislado) |
| **F-1** | 6 hooks-clase | Migrar a `data-image-picker-*` con el codemod del censo SYS-1 (no parche aislado) |
| **F-2** | Sin cita APG y la regla A-1.4 no lo cazó | Declarar patrón (¿file-upload composite? sin APG oficial → usar la regla nueva "apg: none — rationale" que pide el censo) + arreglar la regla del script |
| **F-2** | Sin cita APG y la regla A-1.4 no lo cazó | Declarar patrón (¿file-upload composite? sin APG oficial → usar la regla nueva "apg: none — rationale" que pide el censo) + arreglar la regla del script |
## Escalan al sistema
## Escalan al sistema
@ -27,4 +27,4 @@ SYS-1 · censo A-1.4 (+1 y agujero de regla) · la regla "sin patrón APG oficia
| 1 | Composición | ✓ ejemplar | Anatomía completa: Control/Indicator/Label/ValueText/**ValueField** (:159 — compone NumberField DENTRO, el patrón compound-in-component de memoria: aislamiento de eventos, maxLength) /**HiddenInput** (:172 — form-bridge declarado como radio-group) |
| 1 | Composición | ✓ ejemplar | Anatomía completa: Control/Indicator/Label/ValueText/**ValueField** (:159 — compone NumberField DENTRO, el patrón compound-in-component de memoria: aislamiento de eventos, maxLength) /**HiddenInput** (:172 — form-bridge declarado como radio-group) |
| 3 | Coherencia semántica | ✓ ciclo completo | **`handle-pick`/`handle-drag`/`handle-drop`** (:188-215 — la trilogía de manipulación MÁS completa del censo handle: incluye el drop que color-picker/gradient no declaran) + `commit-set` (:226) + `commit-reset` neutral (:242-248); pack ✓; `Gesture.rotate` (C7) ✓ |
| 3 | Coherencia semántica | ✓ ciclo completo | **`handle-pick`/`handle-drag`/`handle-drop`** (:188-215 — la trilogía de manipulación MÁS completa del censo handle: incluye el drop que color-picker/gradient no declaran) + `commit-set` (:226) + `commit-reset` neutral (:242-248); pack ✓; `Gesture.rotate` (C7) ✓ |
| 3 | Coherencia semántica | ✓ consistente pero MUDA | 0 eventos + scope sin sema = silencio by-design coherente (hover-card: sonido en hover sería ruido)… pero es el ÚNICO 0-eventos de overlays SIN una línea de razón (ntp/alert-dialog/radio-cards/fab/banner todos la escriben) → F-2 |
| 3 | Coherencia semántica | ✓ consistente pero MUDA | 0 eventos + scope sin sema = silencio by-design coherente (hover-card: sonido en hover sería ruido)… pero es el ÚNICO 0-eventos de overlays SIN una línea de razón (ntp/alert-dialog/radio-cards/fab/banner todos la escriben) → F-2 |
| **F-2** | Silencio sin razón escrita (los 5 pares delegados/pasivos de la familia la tienen) | Una línea en el morfo ("hover-signal pasiva: present/dismiss viajan por el canal motion; sonido = ruido en hover") — cierra el caso como by-design documentado (SYS-3) |
| **F-2** | Silencio sin razón escrita (los 5 pares delegados/pasivos de la familia la tienen) | Una línea en el morfo ("hover-signal pasiva: present/dismiss viajan por el canal motion; sonido = ruido en hover") — cierra el caso como by-design documentado (SYS-3) |
## Escalan al sistema
## Escalan al sistema
@ -24,4 +24,4 @@ SYS-3 (candidato a silencio by-design — solo falta escribirlo) · SYS-4.
| 3/8 | Semántica y contrato | ✓ pasivo documentado | Header: "Passive on the morfo surface… renders `<a>` by default (or `<span aria-disabled>` when disabled). **Inherits ambient typography — no own style prop — so it composes mid-paragraph without breaking the text scale**" — la razón de diseño escrita (semántica nativa del anchor; sin robo de escala tipográfica) ✓ |
| 3/8 | Semántica y contrato | ✓ pasivo documentado | Header: "Passive on the morfo surface… renders `<a>` by default (or `<span aria-disabled>` when disabled). **Inherits ambient typography — no own style prop — so it composes mid-paragraph without breaking the text scale**" — la razón de diseño escrita (semántica nativa del anchor; sin robo de escala tipográfica) ✓ |
| 4 | API | verificación pendiente | **F-1**: la doctrina de primitivas tipográficas exigió que Link exponga size/color como Text (fue el ejemplo prop-less a NO imitar) — el header dice "no own style prop" A PROPÓSITO (composición mid-paragraph): puede haber TENSIÓN entre la doctrina y esta razón → el veredicto decide (¿props opcionales que preservan herencia por defecto?) |
| 4 | API | verificación pendiente | **F-1**: la doctrina de primitivas tipográficas exigió que Link exponga size/color como Text (fue el ejemplo prop-less a NO imitar) — el header dice "no own style prop" A PROPÓSITO (composición mid-paragraph): puede haber TENSIÓN entre la doctrina y esta razón → el veredicto decide (¿props opcionales que preservan herencia por defecto?) |
| **F-1** | Doctrina typographic-props vs razón "inherits ambient" del header | Veredicto: props visuales OPCIONALES con default herencia (satisface ambas) o excepción documentada |
| **F-1** | Doctrina typographic-props vs razón "inherits ambient" del header | Veredicto: props visuales OPCIONALES con default herencia (satisface ambas) o excepción documentada |
## Escalan al sistema
## Escalan al sistema
@ -23,4 +23,4 @@ La tensión doctrina↔header (dato para el veredicto de primitivas tipográfica
| 3 | Coherencia semántica | ✓ declarado, con pregunta | select/unselect affirm (censo ×5) + family-default DECLARADO — pero su hermano select (misma semántica de selección) tiene pack: la asimetría es la pregunta SYS-3 en su forma más nítida (¿el standalone hereda el criterio del compuesto?) |
| 3 | Coherencia semántica | ✓ declarado, con pregunta | select/unselect affirm (censo ×5) + family-default DECLARADO — pero su hermano select (misma semántica de selección) tiene pack: la asimetría es la pregunta SYS-3 en su forma más nítida (¿el standalone hereda el criterio del compuesto?) |
| 12 | Estados | hallazgo | readonly sin materializar (R-1.3) |
| 12 | Estados | hallazgo | readonly sin materializar (R-1.3) |
| 1 | Composición | ✓ | Single-input compositivo; el morfo RAZONA por qué NO se fragmenta en fake-segments (eso es de Date/Time — :18-20) |
| 1 | Composición | ✓ | Single-input compositivo; el morfo RAZONA por qué NO se fragmenta en fake-segments (eso es de Date/Time — :18-20) |
| 2 | Naming | ✓ con dato nuevo | `value/onValueChange` ✓ + **`onValueCommit: OnChangeFn`** para blur/Enter (types :75-76) — TERCER patrón de callback terminal del catálogo (vs `onSubmit` crudo de search/password) → fila clave en _naming; `showMask/placeholderChar/definitions` (decisiones locked); `unmaskedValue` como derivado read-only en snippet (:19-20) |
| 2 | Naming | ✓ con dato nuevo | `value/onValueChange` ✓ + **`onValueCommit: OnChangeFn`** para blur/Enter (types :75-76) — TERCER patrón de callback terminal del catálogo (vs `onSubmit` crudo de search/password) → fila clave en \_naming; `showMask/placeholderChar/definitions` (decisiones locked); `unmaskedValue` como derivado read-only en snippet (:19-20) |
| 3 | Coherencia semántica | ✓ | `commit-set` solo en confirmación — la doctrina teclear≠commit citada y alineada (morfo :43-47); `expression: 'pack'` + pack ✓ |
| 3 | Coherencia semántica | ✓ | `commit-set` solo en confirmación — la doctrina teclear≠commit citada y alineada (morfo :43-47); `expression: 'pack'` + pack ✓ |
| 4 | API | ✓ ejemplar | Alfabeto de tokens documentado con escape y cola opcional (`\9`, `[ ... ]` — types :47-53); `definitions` merge-over con RegExp por slot (:55-60); modelo de valor = string enmascarado con la convención de la industria citada (morfo :25-28) |
| 4 | API | ✓ ejemplar | Alfabeto de tokens documentado con escape y cola opcional (`\9`, `[ ... ]` — types :47-53); `definitions` merge-over con RegExp por slot (:55-60); modelo de valor = string enmascarado con la convención de la industria citada (morfo :25-28) |
| 5 | Tokens | ✓ con dup | `height-{k}` duplicada (SYS-6); tokens 9-A-* del engine per decisión |
| 5 | Tokens | ✓ con dup | `height-{k}` duplicada (SYS-6); tokens 9-A-\* del engine per decisión |
| 8 | Contrato morfo | ✓ | APG textbox CORRECTO (:41 — un solo textbox, no combobox como search); `data-complete` con razón (intrínseco a máscara, gate de submit sin re-derivar — :74-77); flags focused/empty ✓ |
| 8 | Contrato morfo | ✓ | APG textbox CORRECTO (:41 — un solo textbox, no combobox como search); `data-complete` con razón (intrínseco a máscara, gate de submit sin re-derivar — :74-77); flags focused/empty ✓ |
| 9 | A11y | ✓ con doctrina propia | **Valor anunciado siempre veraz**: los keystrokes rechazados REVIERTEN visiblemente en vez de tragarse en silencio (el lector siempre relee el valor comprometido — morfo :21-23) — doctrina exportable a cualquier input con formateo |
| 9 | A11y | ✓ con doctrina propia | **Valor anunciado siempre veraz**: los keystrokes rechazados REVIERTEN visiblemente en vez de tragarse en silencio (el lector siempre relee el valor comprometido — morfo :21-23) — doctrina exportable a cualquier input con formateo |
| 10 | Theming | hallazgo | R-1.3 readonly sin estilo (patrón familia) |
| 10 | Theming | hallazgo | R-1.3 readonly sin estilo (patrón familia) |
| 11 | Composición interna | ✓ documentada | Field integration EXPLÍCITA: el Input se registra en `Field.inputId` → Label/Helper/Error se auto-cablean (types :35-38) — de lo poco de la familia que documenta el wiring |
| 11 | Composición interna | ✓ documentada | Field integration EXPLÍCITA: el Input se registra en `Field.inputId` → Label/Helper/Error se auto-cablean (types :35-38) — de lo poco de la familia que documenta el wiring |
| 12 | Estados | ✓ | complete/focused/empty + OR-merge; `name` envía el valor ENMASCARADO (documentado :89) |
| 12 | Estados | ✓ | complete/focused/empty + OR-merge; `name` envía el valor ENMASCARADO (documentado :89) |
| 13 | Runtime | ✓ | Engine puro con suite propia; sin timers/DOM crudo |
| 13 | Runtime | ✓ | Engine puro con suite propia; sin timers/DOM crudo |
| 14 | Docs+tests | ✓ el mejor de la familia | 2 suites (engine + provider) |
| 14 | Docs+tests | ✓ el mejor de la familia | 2 suites (engine + provider) |
| **F-1** | readonly sin estilo (R-1.3) | Tratamiento único de familia |
| **F-1**| readonly sin estilo (R-1.3) | Tratamiento único de familia |
| **F-2** | `height-{k}` duplicada | SYS-6 |
| **F-2**| `height-{k}` duplicada | SYS-6 |
| **Nota ✓** | `onValueCommit` (OnChangeFn) es candidato a CANON del callback terminal (vs `onSubmit` crudo) — coincide con la convención Radix/bits del slider | Veredicto _naming: unificar el trío de patrones |
| **Nota ✓** | `onValueCommit` (OnChangeFn) es candidato a CANON del callback terminal (vs `onSubmit` crudo) — coincide con la convención Radix/bits del slider | Veredicto \_naming: unificar el trío de patrones |
## Escalan al sistema
## Escalan al sistema
_naming (callback terminal: onValueCommit vs onSubmit vs solo-evento) · SYS-5 · SYS-6 · doctrina "valor anunciado veraz" exportable.
\_naming (callback terminal: onValueCommit vs onSubmit vs solo-evento) · SYS-5 · SYS-6 · doctrina "valor anunciado veraz" exportable.
- **Familia**: media (genérico, sin baseline APG per registro 06-29) · **Método**: solo análisis · demos fuera
- **Familia**: media (genérico, sin baseline APG per registro 06-29) · **Método**: solo análisis · demos fuera
- **Capas leídas**: [morfo](../../../src/uix/morfo/components/media-player.ts) (525 líneas — el más grande del catálogo; **12 eventos** con `family-default` declarado, apg=0)
- **Capas leídas**: [morfo](../../../src/uix/morfo/components/media-player.ts) (525 líneas — el más grande del catálogo; **12 eventos** con `family-default` declarado, apg=0)
| 3 | Coherencia semántica | ✓ declarado con 2 hallazgos | 12 eventos (el contrato interactivo más rico) + family-default ESCRITO; **F-1**: 4 nombres violan `{familia}-{verbo}` (los únicos A-3.6 confirmados del catálogo junto al commit-crop de cropper) · **F-2**: family-default para play/pause/seek/volume — SYS-3 candidato mayor (12 eventos con carácter genérico) |
| 3 | Coherencia semántica | ✓ declarado con 2 hallazgos | 12 eventos (el contrato interactivo más rico) + family-default ESCRITO; **F-1**: 4 nombres violan `{familia}-{verbo}` (los únicos A-3.6 confirmados del catálogo junto al commit-crop de cropper) · **F-2**: family-default para play/pause/seek/volume — SYS-3 candidato mayor (12 eventos con carácter genérico) |
| 8 | Contrato morfo | hallazgo documentado | Sin APG — decisión REGISTRADA (06-29: no existe patrón de media player) pero el morfo no la lleva → la regla "apg: none — rationale" nació de este caso |
| 8 | Contrato morfo | hallazgo documentado | Sin APG — decisión REGISTRADA (06-29: no existe patrón de media player) pero el morfo no la lleva → la regla "apg: none — rationale" nació de este caso |
| 12 | Gap registrado | | **F-4**: Slider sin buffered-range (la pista de buffer) — diseño en fase de fixes (slider WIP usuario) |
| 12 | Gap registrado | | **F-4**: Slider sin buffered-range (la pista de buffer) — diseño en fase de fixes (slider WIP usuario) |
| **F-1** | 4 nombres fuera de gramática (A-3.6) | Renombrar (p.ej. `shift-fullscreen-enter` o par `shift.mode`) — decisión de vocabulario en checkpoint |
| **F-1** | 4 nombres fuera de gramática (A-3.6) | Renombrar (p.ej. `shift-fullscreen-enter` o par `shift.mode`) — decisión de vocabulario en checkpoint |
| **F-2** | 12 eventos a default | SYS-3 (pack tras F-1) |
| **F-2** | 12 eventos a default | SYS-3 (pack tras F-1) |
| **F-3** | APG "none" sin campo | La regla nueva + anotar la referencia de diseño usada |
| **F-3** | APG "none" sin campo | La regla nueva + anotar la referencia de diseño usada |
| **F-4** | Buffered-range del Slider | Con el pass del slider post-WIP |
| **F-4** | Buffered-range del Slider | Con el pass del slider post-WIP |
## Escalan al sistema
## Escalan al sistema
@ -27,4 +27,4 @@ SYS-3 · censo A-3.6 (con cropper) · la regla "sin patrón APG" (caso fundador)
| 3 | Coherencia semántica | ✓ razonada in-place | `expression: 'pack'` con la nota (:8-10): "subtle commit + tap haptic for **high-frequency top-level menu triggers**" — el pack existe Y su porqué está escrito; UN evento select (los submenús delegan en dropdown-menu compuesto) |
| 3 | Coherencia semántica | ✓ razonada in-place | `expression: 'pack'` con la nota (:8-10): "subtle commit + tap haptic for **high-frequency top-level menu triggers**" — el pack existe Y su porqué está escrito; UN evento select (los submenús delegan en dropdown-menu compuesto) |
| 8 | Contrato morfo | ✓ | APG menubar (:12); morfo thin coherente con la delegación |
| 8 | Contrato morfo | ✓ | APG menubar (:12); morfo thin coherente con la delegación |
| 14 | Docs | gap | Sin README eidos → SYS-4 |
| 14 | Docs | gap | Sin README eidos → SYS-4 |
## Hallazgos y propuestas
## Hallazgos y propuestas
| ID | Hallazgo | Propuesta |
| ID | Hallazgo | Propuesta |
|---|---|---|
| -------| -------------------- |--------- |
| **F-1** | README eidos ausente | SYS-4 |
| **F-1** | README eidos ausente | SYS-4 |
## Escalan al sistema
## Escalan al sistema
@ -24,4 +24,4 @@ SYS-4 · quinta cita del patrón delegación (submenús → dropdown-menu).
- **HUECO CVD ABIERTO documentado** (G3 §25 + decisión D-3 del informe de theming): las zonas son SOLO-color (WCAG 1.4.1) — es EL caso pendiente del catálogo.
- **HUECO CVD ABIERTO documentado** (G3 §25 + decisión D-3 del informe de theming): las zonas son SOLO-color (WCAG 1.4.1) — es EL caso pendiente del catálogo.
| F-1: zonas solo-color (hueco G3) | Resolver con la decisión D-3 del informe (cue no-cromático: patrón/tick por zona, o dev-warning) — el veredicto es del checkpoint de theming, este es su componente |
| F-1: zonas solo-color (hueco G3) | Resolver con la decisión D-3 del informe (cue no-cromático: patrón/tick por zona, o dev-warning) — el veredicto es del checkpoint de theming, este es su componente |
| 1 | Composición | ✓ | Provider/Header/Heading/Prev/Next/Grid/Cell — SIN Day interno: el Cell `role='gridcell'` (:187) es el objetivo directo con `data-focused` (:194) → roving sobre gridcells, APG-válido, y **`aria-selected` AQUÍ SÍ es legal** (:200 sobre gridcell) — la estructura de 2 niveles del calendar (Cell+Day button) no se replicó y eso EVITA el ARIA inválido ✓ |
| 1 | Composición | ✓ | Provider/Header/Heading/Prev/Next/Grid/Cell — SIN Day interno: el Cell `role='gridcell'` (:187) es el objetivo directo con `data-focused` (:194) → roving sobre gridcells, APG-válido, y **`aria-selected` AQUÍ SÍ es legal** (:200 sobre gridcell) — la estructura de 2 niveles del calendar (Cell+Day button) no se replicó y eso EVITA el ARIA inválido ✓ |
| 2 | Naming | ✓ | `data-current` (:192 — el "hoy" de meses) + data-value/selected/disabled/focused |
| 2 | Naming | ✓ | `data-current` (:192 — el "hoy" de meses) + data-value/selected/disabled/focused |
| 3 | Coherencia semántica | ✓ | `commit-set` (affirm :17-22 — fija mes, coherente con date-field) + `shift-navigate-step` (:31-34, cualificador legal de la gramática) |
| 3 | Coherencia semántica | ✓ | `commit-set` (affirm :17-22 — fija mes, coherente con date-field) + `shift-navigate-step` (:31-34, cualificador legal de la gramática) |
| 6 | Sema | gap | Sin pack → SYS-3 (censo familia: 0 packs salvo donante) |
| 6 | Sema | gap | Sin pack → SYS-3 (censo familia: 0 packs salvo donante) |
| 8 | Contrato morfo | con censo | **F-1**: `role='application'` (:47) heredado sin razón — censo familia ×4 |
| 8 | Contrato morfo | con censo | **F-1**: `role='application'` (:47) heredado sin razón — censo familia ×4 |
| 14 | Docs+tests | gap | **Cero tests** (navegación de grid, select, rangos disabled) → SYS-2 |
| 14 | Docs+tests | gap | **Cero tests** (navegación de grid, select, rangos disabled) → SYS-2 |
| 11 | Composición interna | **borrower doble-roto** | 65 `--calendar-*` + `event-shadow` SIN declarar siquiera el privado de acento (0 declaraciones — B.2): marcas doblemente muertas → SYS-8 |
| 11 | Composición interna | **borrower doble-roto** | 65 `--calendar-*` + `event-shadow` SIN declarar siquiera el privado de acento (0 declaraciones — B.2): marcas doblemente muertas → SYS-8 |
| resto | | ✓ | Espejo estructural del donante en grid de meses |
| resto | | ✓ | Espejo estructural del donante en grid de meses |
| **F-1** | `role='application'` sin razón | Decisión única de familia (con calendar F-1) |
| **F-1**| `role='application'` sin razón | Decisión única de familia (con calendar F-1) |
| **F-2** | Marcas de evento doblemente muertas (borrow + privado ausente) | SYS-8: con la capa formal recibe marcas Y acentos |
| **F-2**| Marcas de evento doblemente muertas (borrow + privado ausente) | SYS-8: con la capa formal recibe marcas Y acentos |
| **F-3** | Cero tests | SYS-2 |
| **F-3**| Cero tests | SYS-2 |
| **Nota ✓** | La estructura Cell-directo (sin Day button) evita el ARIA inválido del donante | Citar en el fix de calendar F-2 como referencia interna |
| **Nota ✓** | La estructura Cell-directo (sin Day button) evita el ARIA inválido del donante | Citar en el fix de calendar F-2 como referencia interna |
| 1 | Composición | ✓ documentada | Header :10-15: solo el gradiente daylight es bespoke; todo lo demás compone primitivos del sistema — el espíritu "compose existing components" cumplido y ESCRITO |
| 1 | Composición | ✓ documentada | Header :10-15: solo el gradiente daylight es bespoke; todo lo demás compone primitivos del sistema — el espíritu "compose existing components" cumplido y ESCRITO |
| 3 | Coherencia semántica | **gap heredado, no propio** | **F-1**: la cadena semántica tiene un eslabón hueco — el valor comete por TimeField (commit-set ✓ del embebido), pero Listo/Cancelar/Borrar fluyen por el Picker genérico cuyo morfo declara 0 eventos (deuda RECONOCIDA en picker :18-19 "candidate commit-* events for a later sema pass"). El "Listo" de un picker (el gesto más commit del patrón) suena a Button contact genérico |
| 3 | Coherencia semántica | **gap heredado, no propio** | **F-1**: la cadena semántica tiene un eslabón hueco — el valor comete por TimeField (commit-set ✓ del embebido), pero Listo/Cancelar/Borrar fluyen por el Picker genérico cuyo morfo declara 0 eventos (deuda RECONOCIDA en picker :18-19 "candidate commit-\* events for a later sema pass"). El "Listo" de un picker (el gesto más commit del patrón) suena a Button contact genérico |
| 5 | Tokens | hallazgo matizado | Los 9 hex (R-2.1) son los CIELOS — la excepción *physically-fixed* existe justo para esto PERO exige anotación `/* literal: … */` línea a línea y no la llevan → F-2 (anotar, NO tokenizar) |
| 5 | Tokens | hallazgo matizado | Los 9 hex (R-2.1) son los CIELOS — la excepción _physically-fixed_ existe justo para esto PERO exige anotación `/* literal: … */` línea a línea y no la llevan → F-2 (anotar, NO tokenizar) |
| 8 | Contrato morfo | ✓ moderno | `data-state` CON values `['open','closed']` enumerados (:39,:75,:96 — el patrón que calendar F-3 pide) + `data-starting-style`/`data-ending-style` condicionales (:97-106, hooks de motion) + flags disabled/readonly/required/invalid |
| 8 | Contrato morfo | ✓ moderno | `data-state` CON values `['open','closed']` enumerados (:39,:75,:96 — el patrón que calendar F-3 pide) + `data-starting-style`/`data-ending-style` condicionales (:97-106, hooks de motion) + flags disabled/readonly/required/invalid |
| 9 | A11y | gap menor | Trigger completo (haspopup dialog + expanded + label vía translationRef ✓ :76-85); Content con Escape/Tab trap (:109-113); **F-3**: sin cita APG (A-1.4) — la razón "Popover posee el diálogo" merece la cita del patrón compuesto como hace date-picker (apg ×2) |
| 9 | A11y | gap menor | Trigger completo (haspopup dialog + expanded + label vía translationRef ✓ :76-85); Content con Escape/Tab trap (:109-113); **F-3**: sin cita APG (A-1.4) — la razón "Popover posee el diálogo" merece la cita del patrón compuesto como hace date-picker (apg ×2) |
| 12 | Estados | hallazgo máquina | R-1.3/R-1.4 readonly/invalid sin materializar |
| 12 | Estados | hallazgo máquina | R-1.3/R-1.4 readonly/invalid sin materializar |
| 14 | Docs+tests | **gap triple** | Sin README (ninguno de los dos niveles), sin tests |
| 14 | Docs+tests | **gap triple**| Sin README (ninguno de los dos niveles), sin tests |
| **F-1** | El footer transaccional no tiene semántica (deuda del Picker genérico, reconocida allí) | Resolver EN el Picker genérico (commit-set fulfill en Accept / commit-cancel neutral / commit-clear neutral) — ntp y los 5 pickers a migrar lo heredan gratis; NO declarar eventos propios aquí (rompería la delegación correcta) |
| **F-1** | El footer transaccional no tiene semántica (deuda del Picker genérico, reconocida allí) | Resolver EN el Picker genérico (commit-set fulfill en Accept / commit-cancel neutral / commit-clear neutral) — ntp y los 5 pickers a migrar lo heredan gratis; NO declarar eventos propios aquí (rompería la delegación correcta) |
| **F-2** | 9 hex sin anotación (R-2.1) | Anotar cada uno `/* literal: physically-fixed sky/astro color */`; NO tokenizar |
| **F-2** | 9 hex sin anotación (R-2.1) | Anotar cada uno `/* literal: physically-fixed sky/astro color */`; NO tokenizar |
| **F-3** | Sin cita APG (A-1.4) | Citar el patrón compuesto (dialog vía Popover) como date-picker; valorar además slider APG para la banda |
| **F-3** | Sin cita APG (A-1.4) | Citar el patrón compuesto (dialog vía Popover) como date-picker; valorar además slider APG para la banda |
| **F-4** | readonly/invalid sin estilo | Patrón familia |
| **F-4** | readonly/invalid sin estilo | Patrón familia |
| **F-5** | Sin README ×2, sin tests | Paquete de estándares completo (SYS-9: iniciativa a medias) |
| **F-5** | Sin README ×2, sin tests | Paquete de estándares completo (SYS-9: iniciativa a medias) |
## Escalan al sistema
## Escalan al sistema
@ -34,4 +34,4 @@ SYS-2 · SYS-4 · SYS-9 · **corrección del censo A-3.1**: ntp EXONERADO con ma
| F-1 | Ninguno propio detectado a nivel análisis (el WIP del usuario está tocando README/scrubber) | Re-pasar la ficha cuando su WIP aterrice (marcar en el checkpoint) |
| F-1 | Ninguno propio detectado a nivel análisis (el WIP del usuario está tocando README/scrubber) | Re-pasar la ficha cuando su WIP aterrice (marcar en el checkpoint) |
## Escalan al sistema
## Escalan al sistema
@ -27,4 +27,4 @@ SYS-7 (consumidor de capa — evidencia del patrón).
## Veredictos
## Veredictos
*(pendiente — checkpoint post-barrido; re-visitar tras el WIP del usuario)*
_(pendiente — checkpoint post-barrido; re-visitar tras el WIP del usuario)_
| 3 | Coherencia semántica | ✓ declarado | family-default ESCRITO (:14,:20) — legal per guard; si el radial MERECE carácter propio (shifts por capas) es la pregunta SYS-3, pero ya no es hueco silencioso |
| 3 | Coherencia semántica | ✓ declarado | family-default ESCRITO (:14,:20) — legal per guard; si el radial MERECE carácter propio (shifts por capas) es la pregunta SYS-3, pero ya no es hueco silencioso |
| 8 | Contrato morfo | hallazgo | **F-1**: sin cita `apg:` con PASS — pagination no tiene patrón APG oficial: quinto caso "apg: none — rationale" (con stepper/cropper/image-picker/media-player), y el agujero A-1.4 del script otra vez |
| 8 | Contrato morfo | hallazgo | **F-1**: sin cita `apg:` con PASS — pagination no tiene patrón APG oficial: quinto caso "apg: none — rationale" (con stepper/cropper/image-picker/media-player), y el agujero A-1.4 del script otra vez |
| 3 | Coherencia semántica | ✓ **DOCTRINA DE REFERENCIA** | El morfo documenta con INCIDENTE por qué teclear NO es commit (`commit-input` por tecla disparaba pulse 260ms + beep por letra; alineado con editable/number/search/combobox — :40-47); `commit-toggle-visibility` = commit.toggle + affirm con cita cap. 23 (:59-72); **`signal-notify-caps-state` con `persistence: 'stateBound'`** + ciclo clearSignal razonado (el indicador seguiría 240ms y moriría con CapsLock aún activo — :73-93) |
| 3 | Coherencia semántica | ✓ **DOCTRINA DE REFERENCIA** | El morfo documenta con INCIDENTE por qué teclear NO es commit (`commit-input` por tecla disparaba pulse 260ms + beep por letra; alineado con editable/number/search/combobox — :40-47); `commit-toggle-visibility` = commit.toggle + affirm con cita cap. 23 (:59-72); **`signal-notify-caps-state` con `persistence: 'stateBound'`** + ciclo clearSignal razonado (el indicador seguiría 240ms y moriría con CapsLock aún activo — :73-93) |
| 4 | API | ✓ | min/maxLength; strength con resultado estructurado (score/label/warnings) y override zxcvbn/OWASP documentado; snippetProps completos (:17-30) |
| 4 | API | ✓ | min/maxLength; strength con resultado estructurado (score/label/warnings) y override zxcvbn/OWASP documentado; snippetProps completos (:17-30) |
| 5 | Tokens | ✓ con dup | `height-{k}` duplicada (SYS-6); strength-segment 6px = geometría del meter |
| 5 | Tokens | ✓ con dup | `height-{k}` duplicada (SYS-6); strength-segment 6px = geometría del meter |
| 6 | Sema | ✓ | Pack ✓ + el contrato de eventos más fino del catálogo |
| 6 | Sema | ✓ | Pack ✓ + el contrato de eventos más fino del catálogo |
| 7 | Animaciones | ✓ | Sin keyframes; el settle del commit lo evitó la doctrina de :40-47 |
| 7 | Animaciones | ✓ | Sin keyframes; el settle del commit lo evitó la doctrina de :40-47 |
| 8 | Contrato morfo | ✓ ejemplar | APG textbox + ARIA toggle-button del trigger — citas CORRECTAS (:38, :10-11); innovación CapsLock documentada como gap 10/10 refs (:22-26); flags visible/caps-active/focused/empty (:118-121) |
| 8 | Contrato morfo | ✓ ejemplar | APG textbox + ARIA toggle-button del trigger — citas CORRECTAS (:38, :10-11); innovación CapsLock documentada como gap 10/10 refs (:22-26); flags visible/caps-active/focused/empty (:118-121) |
| 9 | A11y | ✓ resuelto a nivel contrato | **`a11ySemantic: { requiresLiveRegion: true, reducedMotionFallback: 'text' }`** en el evento de caps (:99-102) — el anuncio para AT es parte del CONTRATO, no un extra; aria-label↔Field.Label documentado |
| 9 | A11y | ✓ resuelto a nivel contrato | **`a11ySemantic: { requiresLiveRegion: true, reducedMotionFallback: 'text' }`** en el evento de caps (:99-102) — el anuncio para AT es parte del CONTRATO, no un extra; aria-label↔Field.Label documentado |
| 10 | Theming | hallazgo | R-1.3 readonly sin estilo (patrón familia) |
| 10 | Theming | hallazgo | R-1.3 readonly sin estilo (patrón familia) |
| 11 | Composición interna | ✓ | OR-merge con Field documentado (types :38-40, :73) |
| 11 | Composición interna | ✓ | OR-merge con Field documentado (types :38-40, :73) |
| 12 | Estados | ✓ | visible/caps/focused/empty proyectados; purpose para autofill |
| 12 | Estados | ✓ | visible/caps/focused/empty proyectados; purpose para autofill |
| 13 | Runtime | ✓ | Caps vía `getModifierState` en keydown del propio input (documentado :24-26) — sin listeners globales |
| 13 | Runtime | ✓ | Caps vía `getModifierState` en keydown del propio input (documentado :24-26) — sin listeners globales |
| 14 | Docs+tests | **gap doble** | Sin README eidos (E-2.3) y **cero tests** — el contrato más rico de la familia sin suite que lo proteja |
| 14 | Docs+tests | **gap doble** | Sin README eidos (E-2.3) y **cero tests** — el contrato más rico de la familia sin suite que lo proteja |
| 15 | i18n | ✓ | texts show/hide/caps-warning localizados (:32-37); warnings del meter traducibles vía langs (documentado types :103-105) |
| 15 | i18n | ✓ | texts show/hide/caps-warning localizados (:32-37); warnings del meter traducibles vía langs (documentado types :103-105) |
| **F-1** | Cero tests para el contrato más fino de la familia (toggle+aria, caps stateBound+clearSignal, strength plugable, debounce n/a) | Suite prioritaria en fase de fixes (SYS-2): el stateBound del caps es EXACTAMENTE lo que una regresión rompería en silencio |
| **F-1**| Cero tests para el contrato más fino de la familia (toggle+aria, caps stateBound+clearSignal, strength plugable, debounce n/a) | Suite prioritaria en fase de fixes (SYS-2): el stateBound del caps es EXACTAMENTE lo que una regresión rompería en silencio |
| **F-2** | Sin README eidos | SYS-4 |
| **F-2**| Sin README eidos | SYS-4 |
| **F-3** | readonly sin estilo (R-1.3) | Tratamiento único de familia |
| **F-3**| readonly sin estilo (R-1.3) | Tratamiento único de familia |
| **Nota ✓✓** | La doctrina teclear≠commit (:40-47) y el a11ySemantic del caps son CANON exportable | Citarlos como referencia en el veredicto de field F-2 (Field.Input debe adoptar `commit-submit` al Enter, el patrón que este morfo documenta) y en la doctrina sema |
| **Nota ✓✓** | La doctrina teclear≠commit (:40-47) y el a11ySemantic del caps son CANON exportable | Citarlos como referencia en el veredicto de field F-2 (Field.Input debe adoptar `commit-submit` al Enter, el patrón que este morfo documenta) y en la doctrina sema |
## Escalan al sistema
## Escalan al sistema
SYS-2 · SYS-4 · SYS-6 · _naming (onSubmit crudo) · **la doctrina de :40-47 resuelve el field F-2 a nivel de familia** (commit-submit-en-Enter como patrón de inputs de texto).
SYS-2 · SYS-4 · SYS-6 · \_naming (onSubmit crudo) · **la doctrina de :40-47 resuelve el field F-2 a nivel de familia** (commit-submit-en-Enter como patrón de inputs de texto).
- **Vigencia re-auditoría 2026-07-07**: capa (SYS-7) — la ficha vale; el footer Accept/Cancel/Clear que registra fluye por el handle del [Picker genérico](picker.md), cuya deuda semántica declarada (picker F-1) es donde se repara la cadena
- **Vigencia re-auditoría 2026-07-07**: capa (SYS-7) — la ficha vale; el footer Accept/Cancel/Clear que registra fluye por el handle del [Picker genérico](picker.md), cuya deuda semántica declarada (picker F-1) es donde se repara la cadena