22 KiB
PLAN — Motor de escenas + pack Ambient + vía de promoción agéntica
Tipo: plan de ejecución por fases (process — efímero, no fuente de verdad). Fecha: 2026-07-11 · Estado: D1–D9 RESUELTAS · F0 ✅ + F1 ✅ + F2 ✅ + F3 ✅ + F4 ✅ + F4b ✅ (2026-07-12) — los 45 fondos aprobados (32 en el pack) migrados; queda F5 texto (plan separado, D9) y el visual píxel en pestaña visible. F4b: Tier B completo (16/16, D6) — galaxy · eter · hyperspeed · particles · pixel-blast · grid · prismatic · pillar · dither · blinds · balastro · iridiscence · liquid-metal · line-waves · floating-lines · letters. Mismo mecanismo que F4 (port verbatim + registro + chip); custom-pipeline reutilizado por 6 (particles gl.POINTS · dither 2-pases FBO · grid 2-pases+composite · eter fluidos 8-programas/7-FBOs half-float · pixel-blast cadena dither→liquid→noise · hyperspeed instancing+depth-FBO+bloom). Contrato completado con consumidores reales:
glContext.dprCap(dither exige DPR 1 — look chunky del seed; engine toma el mín) · hooks P-5 simétricosonPointerLeave(floating-lines/galaxy/eter) yonPointerUp(hyperspeed press) · bug F1 cazado: el leave del engine recentrabanx/nypero NOx/y(el hover de orb jamás decaía). Recortes declarados: gyro+snapBackDelay (grid, plataforma/chrome) · quality por navigator (pillar → high fijo; adaptación por dispositivo = futura opción del engine) · resolutionScale (veil, ya en F4) · mixBlendMode/paused/callbacks onSpeedUp (chrome del host/handle) · presets hyperspeed no exportados. Gates: packs:check 0/36 · engine 9/9 · scene+eidos 323/323 · check 0 propios · docs:check 0/0 · navegador: 32/32 efectos montan+compilan+linkan (prueba estructural canvas+data-scene; la escena interna de hyperspeed compila en su primer frame — cubierta por el pendiente visual). F4: Tier A completo (13/13, D6) — agénticos primero (silk · radar · threads · orb · waves · lumen) y después fog · grainient · veil · dots · beam · ray · snow-pixels. Cada efecto: port limpio a$scene/effects/{name}.ts(shader/física verbatim;iTime/iResolution→uTime/uResolutionestándar; colores[0,1]→hex por P-4; booleans de interacción colapsados en hooks opt-in /mouseInfluence:0;reduce: 'static-frame'los 13) + módulo de registro en el pack + chip en el catálogo. Adaptaciones de ciudadanía declaradas: waves/dots pierden overlays de chrome del seed (cursor-dot / SVG-glow→dibujado en canvas) · dots cambiasetInterval(20ms)→acumulador sobreapi.time(P-3) · veil pierderesolutionScale(el buffer es del driver; si hace falta, opción de engine) · orb deriva el hover del puntero suavizado del engine (decae al salir sin hook de leave). Extensión de contrato justificada por beam (geometría 3D real, primera del tier):WebglSceneEffect.vertexShader?()+glContext.depth?+draw?()— el driver conserva contexto/loop/resize/uniforms/teardown y solo cede geometría+draw; Tier B (particles/hyperspeed/galaxy) la reutilizará. Fix F3 arrastrado:eidos.getMode()NO existía enActiveEidos(error de check) →getThemeContext()(vía pública reactiva). Gates:packs:check0/20 · engine 9/9 · check 0 errores en scene/packs · verificado en navegador: los 16 efectos (3 pilotos + 13 Tier A) montan/compilan/linkan y el remount en cascada no deja errores (canvas presente +data-scenecorrecto; un fallo de build habría hechocanvas.remove()) · README del pack lista el catálogo completo (cierre D6). El look visual píxel-a-píxel sigue pendiente de pestaña visible (limitación de entorno documentada). F3:src/packs/ambient(<Ambient effect>genérico con registro declaration-mergingAmbientEffects·resolve-params.tsP-4 token→hex con drop-a-default · un engine por superficie DOM vía WeakMap para presupuesto global · re-tintado enganchado aeidos.getMode()) · móduloseffects/{mesh,aurora,dot-grid}(import = opt-in) · README con el contrato P ·scripts/packs-check.ts+ npmpacks:check(P-3 APIs crudas · S-1 sin<style>· R-1 registro+augment) 0/7 verde · alias$packs(vite+svelte+CLAUDE.md) · demo catálogoweb/routes/uix/packs/ambient· encapsulación probada (0 imports de$packsfuera de web/). Verificado en navegador: SSR+hidratación limpias, host/canvas/attrs correctos; bug real cazado en la pasada: propeffect+ rune$effect= store_invalid_shape (renombrado localeffectName). El PINTADO no fue verificable (pane oculto suspende rAF/RO — limitación de entorno documentada; la ruta RO→draw la cubren los 9 tests del engine): pendiente un vistazo visual en pestaña visible al retomar F4. F2:resolveTokenextendido a slots semánticos--color-{role}-{slot}(parser con unionslot,getEidosColorRoleSlotOverrideen config.ts,DEFAULT_COLOR_ROLE_SLOT_STEPSexportado — misma fuente que el CSS emitido; EXCLUIDOScontrast(pick APCA) y familiasurface(translúcida); 20/20 tests, incl. equivalencia slot↔primitive y override por rol) · 3 pilotos limpios en$scene/effects(mesh webgl1 · aurora webgl2 · dot-grid canvas2d + util color local; colores = hex concretos, la resolución de tokens es del CONSUMIDOR por P-4 — la capa arts no importa uix) · re-resolución al cambiar modo:getMode()confirmado; el cableado reactivo fino va con el componente del pack (F3). F0: motion-guide §8 revertido ·docs/architecture/packs.md(tier + regla + contrato P + vía Aura) · fila E1 en README + glosario · docs:check 0 err. F1:src/arts/scene/(types con puertoSceneDom+ contratoSceneEffectconreduceobligatoria · engine con pausa-fuera-de-vista/DPR/presupuesto/context-loss · drivers webgl(+2)/canvas2d · README) · alias$scene(vite+svelte+CLAUDE.md) · fila en arts/README · 9/9 tests verdes · check 0 errores en scene · arts:check 0/0 en 23 arts. Origen: estudio deweb/routes/demos/animations(45 efectos: 39 fondos + 6 texto, ~16.100 líneas, auto-contenidos) + decisión de arquitectura conversada: canon = superficie de contrato · pack = hoja decorativa; el indicador de agente (futuroAura) SÍ es canon (materializadelegate+sustain), las landings son pack. Este plan prepara la fase de dirección agéntica sin construirla.
Reglas de gobierno de este plan (cumplimiento total)
Las cinco del usuario (2026-07-11), literales:
- NO INVENTES.
- Ante duda o falta de datos, re-analiza el problema desde todos los ángulos.
- Toda pregunta al usuario llega con todas las opciones analizadas en profundidad.
- Ninguna decisión que viole o cree inconsistencia en el ecosistema.
- Buscar siempre la solución que suponga un upgrade del ecosistema respetando su filosofía.
Y las del propio ecosistema que aplican aquí:
- Docs-first: una decisión que contradiga doctrina vigente actualiza el doc canónico ANTES del código (aplica a motion-guide §8).
- Guard con cada sistema nuevo: el contrato de pack nace con guard mecánico el día uno.
- Nunca borrar sin instrucción explícita ("borra/elimina"): los borrados de F0 quedan gateados en D5.
- Verificación por fase: cada fase declara su criterio verify y no se cierra sin él.
- Pista independiente: este plan NO se entrelaza con la cola clean-room-fixes (F1+ de
continue-cleanroom-fixes-2026-07.md); prioridad relativa la decide el usuario.
Datos verificados que anclan el plan (no inventados)
| Dato | Evidencia |
|---|---|
45 efectos auto-contenidos (0 imports externos; solo svelte + relativos) |
grep de imports 2026-07-11 |
Sustratos: 11 WebGL2 · 16 WebGL1 standalone · 8 sobre base WebGLBackground · 4 canvas-2D (dot-grid, dots, letters, waves) · 6 texto DOM/CSS/WAAPI |
grep getContext por fichero |
| Ciudadanía: reduced-motion 9/45 · pausa-fuera-de-vista 26/45 · context-loss 0/45 · APIs crudas (rAF/RO/IO/matchMedia) 45/45 | censo grep 2026-07-11 |
Las 4 copias de la base (fog≡glass-blocks≡glass-window≡liquid-image≡mesh; nebula/plasma/lumen divergidas) comparten firma idéntica (fragmentShader/setupUniforms/updateUniforms(t,dt)/onPointerMove/loc/uploadTexture, mismas líneas 158–186) → consolidación de bajo riesgo |
diff + grep de firmas |
| Contrato por-efecto embrionario ya existe | background/mesh/Mesh.ts (params tipados + 3 métodos) |
| El mejor ciudadano actual (patrón a heredar) | aurora.svelte: reduce (frame estático), IO-pause, RO, DPR cap 2, teardown con loseContext |
ActiveDom ya expone todo lo que el motor necesita: requestFrame/cancelFrame, observeResize, observeIntersection, prefersReducedMotion, listen |
superficie confirmada (stub disabledDom en active-uix.svelte.ts:242-329 refleja la interfaz) |
⚠️ eidos.resolveToken HOY solo resuelve --scale-{n}-{step} y --primitive-{role}-{step}; devuelve null para slots semánticos (--color-primary-solid) |
active-eidos.svelte.ts:485-517 |
Alias: const único aliases en vite.config.ts:11 (espejado a svelte.config.js); $scene libre |
grep |
Precedente de factory: service-factories/motion.ts (defineEngineMotion) |
ls |
ogl en package.json:60 con 0 imports en src+web |
auditoría 2026-07-10 |
Restos: bends copy.svelte + 4 test-*.html |
inventario |
| Doctrina a revertir conscientemente: motion-guide §8 declara los fondos WebGL no-goal ("a separate axis") | docs/theming/motion-guide.md §8 |
Decisiones de usuario (gate de F0) — cada una con recomendación analizada
| ID | Decisión | Opciones analizadas | Recomendación |
|---|---|---|---|
| D1 | Nombre del art del motor | scene (genérico, sirve a fondos/agente/charts futuros) · scenic · ambient (se confunde con el pack) |
arts/scene, EngineScene/createEngineScene, alias $scene — espejo exacto del precedente motion |
| D2 | Ubicación del tier pack | (a) src/packs/ambient/ (estrato nuevo, sobre uix, documentado) · (b) src/uix/packs/ (dentro de uix — mancha la pureza de capas: el pack consume uix, no es capa) · (c) dentro de web (no es site-code) |
(a) src/packs/ + fila nueva en el mapa de estratos (docs/README.md) y en authoring.md |
| D3 | Nombres de componentes | Pack: <Ambient effect="…">. Futuro canónico: Aura (evita colisión con la capa soma Presence) |
Ambient / Aura — Aura queda solo como nombre RESERVADO documentado; no se construye en este plan |
| D4 | Acceso al motor | (a) uix.scene ya (toca superficie ActiveUix + contracts.ts + docs — invasivo antes de tener consumidor canónico) · (b) factory standalone ahora, promoción a uix.scene cuando llegue Aura |
(b) — mínima invasión = coherencia; la promoción queda declarada en la vía Aura |
| D5 | Borrados F0 (requieren tu "borra" explícito) | bends copy.svelte · 4 test-*.html · dep ogl de package.json |
Borrar los 3 grupos (la dep muerta contradice zero-dependence; los restos contaminan greps) |
| D6 | Alcance Tier A del pack v1 (tras pilotos) | Lista propuesta (sobrios/marca/agénticos): silk · fog · grainient · veil · threads · waves · dots · beam · ray · snow-pixels · radar · orb · lumen | Aprobar lista o recortarla; Tier B (galaxy, hyperspeed, eter, pixel-blast, grid, particles, pillar, prismatic, balastro, blinds, dither, floating-lines, iridiscence, line-waves, liquid-metal, letters) queda para F4b sin fecha |
| D7 | Destino de web/routes/demos/animations |
(a) conservar hasta paridad del pack y decidir entonces · (b) migrar la ruta a catálogo del pack ya | (a) — nada se borra; la demo nueva del pack nace aparte y la vieja se retira cuando tú lo digas |
| D8 | Efectos de imagen (distorsion, liquid-image, glass-blocks, glass-window) | Son tratamientos de imagen (reciben src), no fondos |
FUERA de este plan — familia futura de efectos de Image, decisión aparte |
| D9 | Los 6 de texto | (a) fase F5 de este plan · (b) plan separado | (b) plan separado — destinos heterogéneos (count→compone format; blur→dominio content de motion; gradient→eidos CSS+tokens; circular/focus/scrambled→eidos decorativos con endurecimiento SR). Mezclarlos aquí infla el alcance |
Resoluciones del usuario (2026-07-12)
- D1–D4 — ACEPTADAS las cuatro (
arts/scene·src/packs/ambient·Ambient/Aura· factory standalone). Aclaración pedida y respondida: las animaciones de texto NO entran al pack Ambient — el pack es solo fondos decorativos (aria-hidden, sin contenido); las de texto envuelven contenido real (superficie a11y = superficie de contrato) y van a su plan separado (D9). - D5 — CAMBIADA: NO se borra nada. Literal del usuario: "debes de crearlos sano, lo que hay no debe de contaminar lo nuevo, de hecho se queda a modo presencial y comparativo, nada más". Consecuencias: la colección original (
web/routes/demos/animations, incl.bends copyytest-*.html) queda intacta como referencia presencial/comparativa; la dependenciaogltambién se queda por ahora; lo nuevo se construye LIMPIO enarts/scene+src/packs/ambientsin heredar los patrones contaminados (APIs crudas, base duplicada, hex sueltos). El punto F0.4 (higiene) queda anulado. - D6 — Tier A APROBADA tal cual (13 efectos; agénticos primero).
- D7–D9 — CONFIRMADOS (demos intactas hasta paridad · imagen fuera · texto en plan separado).
Fases
F0 — Doctrina + higiene (gate: D1–D9 respondidas)
- Revertir el no-goal: editar
docs/theming/motion-guide.md§8 — los fondos ambient dejan de ser no-goal y pasan a "pack Ambient sobrearts/scene" (docs-first). - Escribir el tier pack donde corresponde: fila "packs" en la tabla de estratos de
docs/README.md+docs/authoring.md; la regla de admisión queda escrita: canon = superficie de contrato consumida por otros (eventos/ARIA/comportamiento/tokens); pack = hoja decorativa parametrizada; dependencia SOLO pack→framework; borrar el pack no rompe nada. - Declarar la vía de promoción agéntica (una sección en el mismo doc del tier): los efectos son recursos compartidos en
$scene/effects; el consumo semántico llegará con la familiadelegate+sustain(componente reservadoAura); mapeo orientativo estados→efectos (idle: mesh/fog/grainient · listening: orb/lumen · thinking: aurora/silk · acting/streaming: threads/waves · searching: radar · error puntual: ray como firma de evento). Higiene— ANULADO por D5 (2026-07-12): nada se borra; la colección original queda como referencia presencial/comparativa yoglpermanece en package.json. El mandato es construir lo nuevo limpio, no sanear lo viejo.
- Verify: docs editados enlazan y
npm run docs:checksin errores nuevos;npm run checkintacto;git statussolo con lo previsto. - No se hace: ningún código del motor; ningún borrado sin D5.
F1 — arts/scene (el motor)
- Crear
src/arts/scene/con:types.ts— puerto estructuralSceneDom(subconjunto:requestFrame/cancelFrame,observeResize,observeIntersection,prefersReducedMotion,listen;adomlo satisface — mismo patrónMotionDom) + contratoSceneEffectderivado del deMesh.tsy del patrón aurora:{ name, driver: 'webgl' | 'webgl2' | 'canvas2d', params tipados, reduce: 'static-frame' | 'hide' (OBLIGATORIA, sin default silencioso), pointer?: opt-in }; para webgl:fragmentShader/setupUniforms/updateUniforms(t,dt); para canvas2d:setup/draw(t,dt).engine-scene.ts—createEngineScene({ dom, logger? }):mount(node, effect, params) → SceneHandle { setParams, pause, resume, dispose }. Responsabilidades únicas: bucle endom.requestFramecon delta capado; pausa porobserveIntersection+ pestaña oculta; resize porobserveResize+ DPR cap configurable (default 2); reduced-motion víadom.prefersReducedMotionaplicando la policy del efecto (frame estático = dibujar 1 vez, como aurora);webglcontextlost/restored(0/45 lo manejan hoy — upgrade real); presupuesto de escenas concurrentes conlogger.warnal superarlo (los navegadores capan contextos GL); teardown completo (programa, buffers, observers,loseContext).drivers/webgl.ts— consolidación de la base: partir de la copia idéntica ×5 (fog/WebGLBackground.ts), diff completo contra nebula/plasma/lumen para incorporar lo que sus variantes corrigieron (firmas idénticas ya verificadas; la divergencia es interna) + el patrón WebGL2 de aurora como capacidad del driver (el efecto declaradriver).drivers/canvas2d.ts— extraído de dot-grid/dots/letters/waves (mismo ciclo de vida, sin GL).index.tsbarrel +README.md(contrato, policy reduce, presupuesto, la vía Aura).
- Alias
$sceneen el const devite.config.ts:11+ espejo ensvelte.config.js. - Fila en el mapa de
src/arts/README.md. - Tests (proyecto server): ciclo de vida con dom fake (mount→pause por IO→resume→dispose), policy reduce, context-loss, presupuesto.
- NO se crea
defineEngineSceneen service-factories todavía (D4b) — se anota en el README como parte de la promoción Aura.
- Verify:
npm run check0 nuevos +npx vitest run src/arts/sceneverde +npm run arts:checkverde. Ningún efecto portado aún.
F2 — Recursos piloto + costura de tokens
- Extender
eidos.resolveTokena slots semánticos (--color-{role}-{slot}): resolver rol+slot→step vía la misma fuente que el generador (DEFAULT_COLOR_ROLE_SLOT_STEPS,render-css.ts) respetando overrides deapplyColorScheme(precedencia ya implementada para primitives). Es un upgrade coherente con su diseño (config +$color, cero DOM) y cierra la limitación verificada. Test enactive-eidos.test.ts. - Helper de color del motor: los
paramsde color aceptantoken CSS custom-property | color CSS crudo; resolución víaeidos.resolveTokencuando hay contexto eidos, passthrough si no (el pack funciona sin eidos, degradando a hex — encapsulación real). - Re-resolución al cambiar modo/tema: verificar la superficie reactiva de
ActiveEidos(el mecanismo con queapplyColorScheme"sigue light/dark") y engancharse al MISMO; si no hay superficie pública, exponerla como parte de este trabajo (decisión técnica documentada en el momento, no inventada ahora). - Portar los 3 pilotos que cubren los 3 sustratos, a
src/arts/scene/effects/:mesh(webgl base, params ya tipados),aurora(webgl2, hereda su propia ciudadanía),dot-grid(canvas2d interactivo con pointer opt-in). Colores por defecto derivados de roles del tema (mesh: primary/secondary/tertiary/neutral).
- Verify: test de resolveToken semántico; los 3 efectos montan/desmontan en un harness de test; reduce policy ejercitada;
npm run check+ suite eidos verdes (306/306 — el canario THM-3 ya volteado según memoria F0 clean-room).
F3 — Pack Ambient v1
src/packs/ambient/(D2): componente de montaje<Ambient effect="…" params={…}>— registro tipado por declaration-merging (interface AmbientEffects, patrón exactoEidosMotionPresets), tree-shaking por import de efecto; canvasaria-hidden="true";pointer-events: nonesalvo efectos pointer opt-in; sin<style>scoped — clase/estilo passthrough estándar.- Instancia el motor con el dom del contexto (
ActiveEidos.require().domcuando existe; puerto explícito por prop para uso fuera del árbol uix). - Contrato P de una página (README del pack): reduce obligatoria · teardown por handle · DOM solo por puerto · colores token-aware ·
aria-hidden· presupuesto. Mini-guardscripts/packs-check.ts(+ npm scriptpacks:check): efecto registrado sinreduce= error; grep de rAF/RO/IO crudos ensrc/packs/= error. - Demo catálogo única (una ruta nueva bajo
web/routes, selector de efecto + params en vivo + toggle reduce/modo) — las demos viejas intactas (D7a).
- Verify:
packs:checkverde; demo carga los 3 pilotos; cambio de modo re-tinta un efecto token-aware en vivo (verificación en navegador); borrarsrc/packs/dejanpm run checkverde (prueba de encapsulación — en working tree, sin commitear).
F4 — Migración Tier A (D6)
Por cada efecto aprobado: extraer shader/lógica al contrato SceneEffect (queda ~⅓ del fichero original), declarar reduce, params color token-aware donde tenga sentido, registrar en AmbientEffects, añadir al catálogo demo. Orden sugerido: los agénticos primero (orb, lumen, radar, silk, threads, waves) — son los recursos que la fase Aura consumirá.
- Verify por efecto: monta/pausa/reduce/teardown en el catálogo +
packs:checkverde. Cierre de fase: Tier A completo listado en el README del pack.
F4b — Tier B (sin fecha, mismo mecanismo). F5 — texto (plan separado, D9).
F6 — Cierre
- Actualizar memoria de proyecto +
docs/process/con el estado real. - Verificación global:
npm run check·npx vitest run src/arts/scene src/uix/eidos·packs:check·docs:check. - Dejar escrito el arranque de la fase agéntica:
Aura= componente canónico por la ruta de 9 fases, morfo con estados de ciclo de agente + eventosdelegate/sustain+ intent, pack sema con modulación por intent (velocidad/amplitud/hue = el análogo visual de pitch/gain/contour), consumo de$scene/effects, promoción del motor auix.scene(D4). Nada de esto se construye en este plan.
Qué NO hace este plan (anti-scope-creep)
- No construye
Aurani toca sema/morfo/soma. - No toca la cola clean-room-fixes ni la matriz de componentes (el pack no entra en
component:audit). - No borra las demos actuales ni ningún fichero sin D5/D7.
- No añade
uix.scenea ActiveUix (D4). - No porta efectos de imagen (D8) ni texto (D9).