You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/process/contrast-stage2-plan-2026-0...

252 lines
15 KiB

docs(color): plan verificado de Contraste Stage 2 (generador by-construction) Plan de ejecucion para sesion aparte, VERIFICADO adversarialmente (workflow de 7 agentes contra codigo y docs) antes de escribirse — corrige 5 afirmaciones de la respuesta que lo origino: - "por delante de Radix (hex sRGB)": FALSO — el propio RFC §14 registra Radix con APCA y P3 ("Radix style"); la posicion del repo es "on par"; nuestra paleta enviada es sRGB-equivalente (oklch = siblings de hex). - "el solver satisface la tabla §40": el UNICO floor duro ratificado es text-strong·12 ≥ 4.5; text·11 quedo tier blando (decision D1); solid·9 esta anclado exacto a la semilla; el script de auditoria aun codifica la tabla PRE-veredicto (consumirla contradiria los veredictos → F0 = tabla como datos). - "los tipos ya existen en parte": ColorScaleSeed/Source son solo prosa del RFC; config-seeds imposibles hoy (solo applyColorScheme roles-only + generatePalette full-33 como bancos). - "792 hex en base.ts": exacto en total pero en DOS ficheros (288 base.ts + 504 color-scales.ts); 4 escalas ya autoradas desde semillas offline. - "nada downstream cambia": nombres si (contract.ts), pero hay pins de VALOR disenados para dispararse (hex literales, flip-set de polaridad, generated/base.css byte-exacto) + persistencia rechaza version ≠ 1. El plan: F0 tabla ratificada como datos + harness sobre output generado · F1 solver (biseccion contra sRGB gamut-mapeado, salida hex, desviacion minima del donante) · F2 validacion en bancos de semilla + navegador · F3 (gated) migracion base→seeds con re-ratificacion de pins y pase visual. 6 decisiones de usuario ABIERTAS al frente (target text·11, semantica base+ΔE, modo on-solid, colocacion/alcance, tipos Fase 0, normalizacion de la tabla). next-features §1 enlaza el plan. (El hunk ajeno de chat-list en next-features queda fuera del commit — danza clean-desde-HEAD.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
---
title: Plan — Contraste Stage 2 (generador by-construction) · verificado para ejecutar en sesión aparte
type: process
audience: human + agent
status: PLAN listo — verificado adversarialmente (7 agentes, 2026-07-19) contra código y docs; 6 decisiones de usuario ABIERTAS antes de tocar código
date: 2026-07-19
related: docs/next-features.md §1 · docs/theming/reference.md §40 · docs/theming/changelog.md §44 · docs/rfcs/rfc-color-engine.md §6/§8/§9/§10/§11
---
# Plan — Contraste Stage 2: el generador by-construction
## Objetivo en una frase
Que las escalas **generadas desde semilla** cumplan el contrato de contraste
ratificado (§40) **por construcción** — resolviendo la luminancia de los steps
de texto — en vez de heredarlo del donante por morph; y, **solo si el usuario
lo aprueba** (gate), extenderlo al tema base migrándolo a semillas.
## ⚡ Empieza aquí (sesión nueva)
1. Lee este doc ENTERO + `reference.md §40` (la doctrina ratificada) +
`rfc-color-engine.md §6/§8/§9/§10/§11`.
2. **NO toques código hasta resolver las 6 decisiones del usuario (§Decisiones).**
La primera (target de `text·11`) define qué significa "resolver" — nada se
puede especificar antes.
3. El terreno está VERIFICADO (2026-07-19, workflow de 7 agentes contra el
código real) — los anclajes de §Terreno son fiables; aun así, re-verifica
los `file:line` que uses (el árbol es compartido y se mueve).
---
## Decisiones del usuario (ABIERTAS — por orden de consecuencia)
**D1 · Target de `text·11` para escalas GENERADAS.** (a) blando ≈APCA 60 —
paridad visual con las 33 escalas enviadas (el contrato Radix que Stage 1
ratificó); (b) duro WCAG 4.5 — más accesible pero visiblemente más oscuro en
tonos turbios, rompe la paridad recién ratificada. Si (a): ¿solo suelo, o
banda (tope superior para que `text·11` siga leyéndose "secundario" vs
`text-strong`)? *Define qué resuelve el solver; bloquea todo lo demás.*
**D2 · Semántica de cobertura del BASE + tolerancia ΔE.** (a) medido-pasa: el
base queda VERBATIM como ground-truth (`rfc §12`: "the 33 hex are ground-truth,
not deleted") y su garantía es por MEDICIÓN (hoy pasa; el harness la vigila);
(b) construido-pasa: regenerar el base desde semillas aceptando drift ΔE
(por-step ΔE2000, `rfc §11` Fase 3 — **fijar el número de tolerancia**, el RFC
no lo da) + re-ratificar TODOS los pins de valor (§Guards). *Esto ES la
decisión del gate; (b) cambia los píxeles del look.*
**D3 · Modo on-solid.** (a) flip-only (default actual: light-ink-first, cae a
tinta oscura solo si falla AMBOS floors — `on-solid.ts`; el RFC RECHAZÓ en
canonización la alternativa que cambiaba el look, rfc §8:487-494); (b) añadir
un modo opt-in "oscurece el solid hasta que la tinta blanca pase" — sacrifica
la fidelidad de marca en semillas claras (clase amber/yellow/lime) porque
`solid·9` está anclado EXACTO a la semilla (`generate.ts:89`). ¿Debe existir
siquiera? *Recomendación: (a); el flip YA es el mecanismo by-construction.*
**D4 · Colocación y alcance del solver.** (a) dentro de `generateScale` — lo
heredan TODOS los consumidores, incluido el output vivo de
`applyColorScheme` (Fase 4-bis SHIPPED → **los temas white-label existentes
cambian de píxeles el día que aterrice**: cambio visible, no refactor
interno); (b) post-pass opt-in compuesto por `buildScheme` + `generatePalette`
(+ la futura expansión config). Y alcance: ¿solo la capa de 9 roles de
`buildScheme`, o también el full-33 de `generatePalette`? *Ojo además al
mandato de calibración del RFC (§6:257-261): el generador debe reproducir las
escalas curadas dentro de tolerancia — reproducir-ground-truth y
resolver-contraste son objetivos OPUESTOS; un opt-in evita el choque.*
**D5 · Tipos de Fase 0 ahora o después.** `ColorScaleSeed`/`ColorScaleSource`
NO existen (solo prosa del RFC §5/§11; `ThemeColorSet.scales` sigue siendo
12-hex — `config-types.ts:1015`). (a) Stage 2 runtime-first contra los dos
caminos de semilla que EXISTEN (`applyColorScheme` + `generatePalette`), tipos
después; (b) aterrizar Fase 0 primero (autorar semillas en config) — arrastra
la decisión de persistencia: `persistence.ts:48-52` RECHAZA versión ≠ 1 sin
lector de migración; ¿unión-mantiene-v1 o bump+lector-v1 (que no existe)?
**D6 · Normalización de la tabla de pares.** `scripts/contrast-audit.ts:38-46`
aún codifica la propuesta PRE-veredicto (4.5 duro en `text·11`) — un solver que
la consuma tal cual CONTRADICE los veredictos del 2026-07-19. ¿Re-codificar §40
como MÓDULO DE DATOS compartido (solver + auditoría consumen UNA fuente) ahora,
o junto con Stage 2? *Recomendación: ahora — es F0 del plan y no depende de D1.*
---
## El terreno (VERIFICADO 2026-07-19 — qué existe de verdad)
**El generador** (`src/arts/color/generate.ts`): template morph puro. `pickNearestTemplate`
elige donante por proximidad del solid (L ponderada ×2 + hue; ignora chroma);
`generateScale` copia la curva-L del donante VERBATIM en 11 de 12 steps,
re-matiza el hue conservando el drift relativo, rescala chroma
multiplicativamente (`seedC/tplSolidC`), y devuelve la semilla EXACTA en el
solid (`:89 — "exact brand anchor"`). **Cero contraste en la generación**; el
contraste entra solo en el pick post-hoc `pickOnSolid` (APCA≥60 ∧ WCAG≥3,
light-ink-first) + `onSolidWcagRatio` (cross-check) + `alphaOverBackground`
(inversa de compositing en sRGB gamma). `scheme.ts:11-12` lo dice explícito:
"HCT's tone→contrast is NOT ported".
**Caminos de semilla HOY (los bancos de validación):**
- `ActiveEidos.applyColorScheme(seed)` (§26; Fase 4-bis ✅) → `buildScheme`
regenera SOLO la capa de binding de roles (`--primitive-{role}-*`, ≤9 roles)
como bloque override gestionado; las 33 `--scale-*` NO se re-emiten (actúan
de donantes).
- `generatePalette` (full 33 desde anclas+carácter) → demos `/temas/paleta` y
`/temas/estudio`. **Este es el banco del solver full-palette.**
- Config-authored seeds: **IMPOSIBLE hoy** (tipos de Fase 0 sin implementar; el
stub `src/uix/eidos/COLOR_ENGINE_RFC.md:4` SOBREAFIRMA que existen — ignóralo,
el RFC real es `docs/rfcs/rfc-color-engine.md`).
- El path build/estático NO ejecuta el generador (emite hex verbatim;
rfc §6.1:280 lo admite). "Isomorfo" es propiedad del módulo, no comportamiento
actual del build.
**El base** (792 hex = 33 escalas × 12 × 2 modos, en DOS ficheros):
`themes/base.ts:47-217/219-389` (12 escalas/modo, 288 hex) +
`themes/color-scales.ts:33-81` (`RADIX_EXTRA_*`, 21 escalas/modo, 504 hex,
spread en base.ts:216/388). La paleta es NUESTRA (sembrada de Radix v3;
**gold/bronze/steel/fuchsia ya se autoraron DESDE SEMILLAS offline con
`generateScale`** — sus semillas están documentadas en color-scales.ts:13-15;
las Radix-seeded no tienen semilla registrada → ¿derivar-inversa, pin como hex,
o re-sembrar? — sub-decisión de D2). Los semantic tokens (surface/content/
border/focus) referencian `--primitive-*` → CABALGAN la migración solos, salvo
3 literales autorados: `content.onSolid` (#ffffff), `onSolidContrast` (#1c1917)
(anclas deliberadas del flip APCA) y `surface.backdrop`.
**El contrato downstream:** NOMBRES congelados (rfc §10; `contract.ts` parsea
solo nombres) → recipes/TSC/CSS de componente/purge/API `color` intactos. PERO
hay **pins de VALOR** diseñados para dispararse conscientemente si los valores
driftan (§Guards). La emisión foundation solo lleva las ~9 escalas
referenciadas por roles; el full-33 es opt-in (`generated/palette.css`).
**La matemática (restricciones del solver):**
- La inversión "Y objetivo → L OKLCH" NO es cerrada: **bisección iterativa
contra el sRGB GAMUT-MAPEADO** (todo apcaLc/wcag pasa por `oklchToGammaRgb`;
el mapping reduce chroma a L fija, `convert.ts:91-103`). Sondeo empírico: Y
post-map es no-monótona en L cerca de la frontera de gamut (~2% de dip);
monótona en el rango útil de texto — la bisección converge (~40 evals/step).
- **El solver debe emitir HEX**: `alphaColorOverBackground` devuelve null con
no-hex y la rampa alfa degrada EN SILENCIO al fallback color-mix
(`render-css.ts:2608-2656`). Mover la L de un step cambia todas las alfas
derivadas — se re-ejecutan, no se pinnean.
- **Color de medición (decisión técnica a fijar):** ¿contraste sobre el sRGB
mapeado (lo que mide el código hoy) o sobre el `oklch()` wide que renderiza
un display P3? Divergen tras la reducción de chroma.
**El spec REAL del solver (derivado de la doctrina ratificada, NO de la
propuesta pre-veredicto):**
- ÚNICO floor duro ratificado: **`text-strong·12` ≥ 4.5 WCAG sobre track·1 /
bg·2 / element·3, por modo** (formular como "resolver contra el extremo
vinculante"; la auditoría midió 1 y 3 — 2 queda acotado entre ambos; ambos
modos resuelven independiente, curvas-L distintas).
- `text·11`: lo que salga de D1 (blando ≈APCA 60 vs duro 4.5).
- on-solid: el flip ES el mecanismo (D3); `solid·9` NO se mueve (ancla de marca).
- Steps 1–8/10: SIN floors ratificados — no se resuelven (el carácter del
donante se conserva; "¿reemplazar la curva-L o desviación mínima?" —
restringir a desviación mínima respecto al donante para no tirar el carácter
perceptual que el morph existe para conservar).
- Bordes decorativos y foco: FUERA del solver (exentos §40 / eje de config §32).
**Nota §9 (para no repetir trabajo):** 3 de las 4 filas de limpieza del RFC §9
YA están arregladas en código (primary purple + loss plum `base.ts:23,44`;
tertiary indigo `:32`; border 6→7 `render-css.ts:102`) y el doc-drift alineado.
Lo que queda de §9 = la re-autoría a semillas en sí (rfc §11 Fase 3). Al pasar
por el RFC, marcar la tabla §9 como resuelta-en-código.
---
## Plan por fases (cada una verificable y commiteable sola)
**F0 — La tabla ratificada como datos (no depende de D1; recomendado hacerla ya).**
Módulo de datos con la tabla de pares POST-veredicto (§40): floor duro
`text-strong·12`, tier blando `text·11` (parametrizado por D1), floors on-solid,
exenciones. `scripts/contrast-audit.ts` pasa a consumirla (muere su constante
PAIRS pre-veredicto) y el futuro solver consume LA MISMA fuente. Extender la
auditoría para correr también sobre **output GENERADO** (hoy solo importa las
escalas estáticas del base) — ese es el harness de regresión de todo Stage 2.
**F1 — El solver (tras D1/D3/D4).** En `$color` (junto a `generateScale`),
como post-pass componible (o integrado, según D4): para cada modo, bisección de
la L de los steps 11/12 contra el sRGB gamut-mapeado hasta cumplir la tabla de
F0, con desviación mínima respecto a la curva del donante; salida hex.
Tests puros en `color.test.ts` (converge, respeta ancla del solid, hex out,
casos frontera de gamut: semillas alta-chroma clase amber/yellow).
**F2 — Validación en los bancos de semilla (SIN tocar el base).**
- `generatePalette` → `/temas/paleta` y `/temas/estudio`: full-33 generado con
solver, auditoría F0 en verde sobre el output, y **verificación EN NAVEGADOR**
(lección dura del track: la verificación de cascada/percepción de color exige
navegador, no lectura de código).
- `applyColorScheme`: si D4=(a), documentar el cambio visible en white-label
ANTES de aterrizar; si (b), demo con el opt-in activado.
**F3 — (GATED por D2=(b)) Migración base→seeds.** Re-autorar los DOS ficheros
(base.ts + color-scales.ts) como semillas (las 4 ya-sembradas primero — sus
semillas están documentadas); tolerancia ΔE fijada en D2; los 3 literales
autorados (onSolid/onSolidContrast/backdrop) se quedan a mano salvo decisión
contraria. **Protocolo de re-ratificación de pins** (§Guards) + pase visual en
navegador side-by-side (light+dark, `/uix/components/*` + `/temas/grafito`)
con reporte ΔE — el vehículo del "look-approval" del usuario, no un diff de texto.
---
## Guards y verificación (qué se dispara y qué no)
**NO se disparan con cambio de valores** (estructurales): `contract.ts` (solo
nombres) · `recipe-css-contract.test.ts` (30/30, 0 hex) · checks de morfo · el
parity guard §8 (`active-eidos-config.test.ts:1558-1581` — auto-consistente,
recomputa del config vivo).
**SÍ se disparan — POR DISEÑO, exigen re-ratificación consciente (no mecánica):**
- Pins de hex literal: `active-eidos-config.test.ts:1481,1485,1588-1589` ·
`active-eidos.test.ts:73,78`.
- El flip-set de polaridad pinneado: `active-eidos-config.test.ts:1525-1542`
(['amber','cyan','gold','lime','mint','orange','sky','yellow']) — su
comentario MANDA actualización consciente si un step-9 cruza el floor.
- `generated-css.test.ts:8-15`: `generated/base.css` byte-exacto → regenerar
con `npm run generate:eidos-css` es paso OBLIGATORIO de cualquier cambio de
valor (un CI rojo aquí es "falta regenerar", no "contrato roto").
**Operativa** (heredada del track de color, sigue vigente): árbol COMPARTIDO
(chat/palabras editan `base.ts` de recipes en vivo — ojo: ese es
`lib/recipes/base.ts`, distinto de `lib/themes/base.ts`, pero el stage se hace
igual con rutas explícitas y `git reset -q` antes) · `npm run check` baseline
~76-77 inestable → verificar CERO errores en tocados · contract test como gate.
---
## Posicionamiento honesto (corregido tras verificación)
Para no repetir sobreafirmaciones: el propio RFC (§14, 2026-06-04) registra a
**Radix con APCA y P3** — la posición del framework es "**on par with Radix/M3
en fidelidad de color**"; los exclusivos que el repo reivindica son **TSC +
sema**, no el output de color. Nuestra paleta ENVIADA es sRGB-equivalente (los
`oklch()` son siblings de hex sRGB); el wide-gamut real vive solo en esquemas
generados. Lo que Stage 2 añade de verdad: **garantía por construcción
(mecanismo clase-M3) decidida con APCA en vez de deltas de tono HCT** — con D2
también en el base, algo que ni Radix (curación) ni Tailwind/Chakra (sin lógica
de contraste) hacen. Las caracterizaciones de competidores citan el RFC §14
(afirmación propia del repo, fechada) — si el plan las publica fuera, re-verificar
externamente primero.
## Referencias
- Doctrina: `docs/theming/reference.md §40` · chronicle `changelog.md §44`.
- RFC: `docs/rfcs/rfc-color-engine.md` §5 (tipos) · §6 (generador+calibración) ·
§8 (on-solid) · §9 (limpieza, mayormente resuelta) · §10 (contrato congelado)
· §11 (fases) · §12 (riesgos: "los 33 hex son ground-truth").
- Código: `src/arts/color/generate.ts` · `src/uix/eidos/lib/build-scheme.ts` ·
`src/uix/eidos/lib/generate-palette.ts` · `src/uix/eidos/lib/themes/base.ts`
+ `color-scales.ts` · `src/uix/eidos/lib/on-solid.ts` ·
`scripts/contrast-audit.ts`.
- Registro: `docs/next-features.md §1` (Stage 1 DONE; Stage 2 = este plan).
- Verificación de este plan: workflow 7 agentes 2026-07-19 (transcript en la
sesión de origen; los anclajes file:line de arriba salen de ahí).

Powered by TurnKey Linux.