Primera tanda del último componente de F1, con el alcance firmado por el
usuario (17 partes · submenú flotante en modo icono · sema emerge · parada
tras morfo+soma). Suelo de paridad: dossier §P4, leído del `sidebar.tsx` real
de shadcn (23 partes); nadie headless lo tiene (Radix/Base/Ark/React-Aria).
Morfo (17 partes, 2 eventos)
- DOS ejes, no uno: `data-state` (expanded|collapsed) + `data-collapsible`
(offcanvas|icon|none), este último SIEMPRE estampado (shadcn solo lo pone
colapsado, lo que obliga a cada regla a guardar por estado). Más
`data-side` y `data-mobile`.
- `panel` (no `sidebar`, que daría `data-sidebar-sidebar`) = `<aside>`
NOMBRADO; `content` = `<nav>` NOMBRADO, con header y footer FUERA: así el
landmark nombra la navegación y no el cromo. Ninguna referencia trae
landmark.
- `rail` es una parte física y un `<button>` REAL en el orden de tabulación
(shadcn lo deja en tabIndex=-1, inalcanzable por teclado).
- `aria-current="page"` sale del MISMO prop que `data-active`, en
`menu-button` y en `menu-sub-button`.
- `menu-badge` va DENTRO del control de la fila (doctrina de nav-tree: su
texto entra en el nombre accesible). `separator` e `input` NO son partes:
se componen el `Separator` y el `Field` canónicos.
Soma
- Estado controlado/no controlado con las costuras exigidas por el dossier
desde v1: `open` bindable, `defaultOpen`, `onOpenChange` y `toggle()`
público — sin ellas, persistencia y atajo global en app-land serían
rediseño posterior.
- `mobile` sale del breakpoint del SISTEMA (`uix.dom.isAtLeast`), nunca de un
`matchMedia` propio; soma solo decide el hecho y estampa `data-mobile` — la
presentación como `Drawer` la compone el eidos (y con ella su foco atrapado,
bloqueo de scroll y descarte).
- Submenú que SOBREVIVE al modo icono: `menu-sub` se vuelve superficie
flotante anclada al botón de su fila (`createFloatingShellRoot` + anchor +
content), se abre por hover/foco y se cierra por salida, blur o Escape
devolviendo el foco al botón. shadcn los OCULTA y deja ramas enteras
inalcanzables.
- Dos `partRef` del morfo se resuelven last-write-wins entre instancias, así
que soma da el id per-instancia (precedente `navigation-menu`):
`aria-labelledby` del menú → la etiqueta de SU grupo; `aria-controls` del
trigger/rail → el id del panel.
Sema: pack propio con `emerge.soft` / `emerge.exit.soft` sobre `panel`,
espejando nav-tree y collapsible. Langs: 4 textos (label, nav, trigger, rail).
Verificado: `morfo:check` carga el contrato sin invariantes rotas ·
`component:audit --only sidebar` **PASS** (0E/0W) · `svelte-check` 0 errores
en estos archivos · `contracts.test` solo con los 2 fallos ajenos conocidos
(menubar DOM-write, radio-group data-ready).
Pendiente de la siguiente tanda: eidos (raíl, anchos tokenizados, modo icono,
Drawer móvil), demo y review adversarial.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decisión del usuario. Un nodo deshabilitado conserva su fila (sigue siendo
parte del mapa) pero pierde el `href`: no hay nada que activar con clic,
Enter ni «abrir en pestaña nueva». Un `pointer-events: none` de CSS solo
habría tapado el ratón.
- morfo: la parte `link` gana `data-disabled` (propRef) y `aria-disabled`
(literal `true` bajo `prop-truthy: disabled`).
- soma: `disabled` solo aplica con `href` (un grupo sin destino lo ignora);
el toggle del grupo NUNCA se deshabilita — mostrar/ocultar es control de
vista, no destino.
- eidos: fila en `--color-content-disabled`, sin puntero y sin hover.
- demo: «Agent» va deshabilitado con badge «Soon» + filas de API y a11y.
Verificado en navegador real: `<a>` sin href, `aria-disabled="true"`,
`data-disabled`, color 0.79 frente a 0.61 de una fila normal, clic sin
efecto, fuera del orden de tabulación (44 focusables en vez de 45), badge
dentro del control. `component:audit` PASS · eidos-lint 28 morfo-backed / 0
invalid · `vitest src/uix/eidos` 353/353 · svelte-check 0 errores míos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cierra el árbol de navegación data-driven (E-1) de F1: demo canónica de 9
pestañas con el mapa real de docs (43 nodos, 3 niveles), verificación en
navegador real y review adversarial (5 dimensiones × 3 verificadores
escépticos; 22 hallazgos brutos, 10 confirmados) con todos los confirmados
arreglados.
Arreglos del review
- sema: la parte `group` —target de los eventos emerge— se registraba SIN
`ref`, así que `runtime.trigger` lanzaba `SomaRuntimeTargetError` en
silencio y el pack no sonaba nunca (cero `data-event-*` en el grupo frente
a los de collapsible). El provider posee ahora el ref del `<ul>`.
- eidos: en una fila navegable el chevron resolvía `inline-size: 100%` como
flex-basis y ocupaba media fila (101 de 231 px en «Soma»), robándole clics
al enlace. Toggle compacto con suelo de diana de 24 px (WCAG 2.5.8).
- soma: una clave duplicada podía volver cíclico `parentByKey` y colgar la
pestaña dentro de `trailKeys` (deriva en render) → clave sufijada + aviso
del logger + guarda de ciclo en el paseo.
- soma: el colapso es CONTEXTUAL (recuerda el `activeKey` bajo el que se
hizo): cerrar la sección que lees se respeta, pero caduca al navegar
DENTRO del grupo, para que la página actual nunca quede sin fila visible.
Sigue siendo query pura, sin `$effect` que escriba estado.
- soma: `child` recibe también `children` (el árbol renderizado); antes
dejaba el landmark vacío, porque un árbol data-driven no lo puede
reautorar el consumidor.
- morfo + langs: el nombre accesible del chevron se declara en el contrato y
se localiza («Alternar sección {label}»); ya no duplica el del enlace.
- eidos: RTL completo — el glyph espeja solo (bordes lógicos), lo que no
espeja es el giro, así que bajo `[dir='rtl']` las dos rotaciones se
intercambian. El Gap «dirección del chevron en RTL» queda RESUELTO.
- demo: paridad de snippet con los controles vivos; fuera el token fantasma
`--nav-tree-rail-width` del docblock del recipe.
badge en v1 (decisión del usuario, delegada)
Está en el suelo de paridad del dossier §P5 (los 6 refs lo llevan). Se
resuelve con SNIPPET, no con recursión a nivel de eidos: el morfo declara la
parte `badge`, soma renderiza el snippet recibido (sin él, el valor crudo —
sigue siendo headless) y el wrapper de eidos pasa el `Badge` canónico. Va
DENTRO del control de la fila, así su texto entra en el nombre accesible
(«TSC, New, enlace»). `disabled` se descarta en v1 (fuera del suelo, y un
enlace de navegación deshabilitado es semánticamente dudoso); ambos quedan
registrados en la tabla de Gaps.
Verificado: `component:audit` PASS · eidos-lint 26 morfo-backed / 0 invalid ·
`svelte-check` 0 errores en estos archivos · `vitest src/uix/eidos` 353/353 ·
navegador real (Playwright): trail auto-expandido, sema estampando en el
grupo, teclado nativo, foco visible, claro y oscuro, RTL, 375 px sin
desbordes, 0 errores de consola.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PLAN §5: la decisión editorial planteada con el usuario (2026-07-22) queda
firmable al retomar — (a) elevar agent.md a especificación publicable («el
contrato de delegación») con agent-check como suite de conformance y el
checklist de amenazas como anexo; (b) nombrar las dos reclamaciones de
frontera que F4/F2 ya materializan: contexto por atención y delegación como
transacción; (c) posición honesta declarada (acts secuenciales v1,
cliente-first, generalidad pendiente del 2º/3º consumidor).
continue-agente: punto 0 del orden de trabajo → firmar D-AG.12.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The soma component README was never committed with F1.6, so
contracts.test's "public Soma component modules with local README docs"
guard was failing and the eidos README's link to it was broken. Adds the
headless-contract README; both guards go green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Estado al cierre (F0/F1/F3/F4a cerradas + tabla de commits), verificación
rápida al retomar, siguiente trabajo en orden (F4b chat → F2 gated →
flecos), y la disciplina operativa de la rama compartida + gotchas de
entorno (Ollama/OpenWebUI, pane, Chrome MCP, index:0 del stream).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
La composición que pidió el usuario: el orb es el trigger central de un
OnionMenu placement="center" y las afordancias de custodia aparecen como
sectores contextuales — reviewing→Authorize/Reject, escalated→Approve/Deny,
activo→Stop — sin fila de botones. El anillo se abre SOLO mientras la
custodia espera a un humano, con re-aserción (un cierre externo sin
resolver re-abre: el efecto lee onionOpen como dependencia); por click en
el centro mientras actúa; se pliega al devolver el control.
App-land por doctrina (agent.md §7): el framework da estados + afordancias
(Aura) y componentes (OnionMenu); el app da forma a la superficie de
review. Patrón documentado en el README de aura (§Patrón).
E2E verificado en Chrome real contra qwen3-coder: la escalada real por
doc.clear abre el anillo con el orb rojo al centro → Deny en el sector →
returned(aborted, user-cancel) + «Agente: terminó — control devuelto» +
documento intacto.
check 0 propios · smoke agnt PASS.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
El aurora (banda horizontal recortada al círculo) leía estático a 32-48px
(señalado por el usuario). El render vivo pasa a ser el efecto `orb` de
$scene — la presencia esférica nativa: anillo respirando con ruido + luz
orbitando, movimiento de silueta perceptible en tamaños pequeños y
deformable por el puntero. `aurora` queda como variante para superficies
grandes (`<Aura.Indicator variant="aurora">`).
Modulación §F6 del orb: el estado fija la energía (forceHoverState +
hoverIntensity como gain + rotateOnHover en acting/escalated) y el rol el
matiz como delta de hue OKLCH sobre la identidad del shader (primary;
reviewing=risk, escalated=threat). Verificado en Chrome real, estado por
estado: idle recogido → planned anillo violeta → acting denso rotando →
escalated rojo agitado.
Demo de aura: chips de variante; scene/README: acta D4 pendiente de F3.
eidos 353/353 · check 0 propios.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
La demo usaba el adaptador sin documentar cómo se configura. Nueva sección
«Configuring the transport»: qué es el puerto (turn = un intercambio de
cable), adapters fuera del barrel, ejemplo dev-directo vs prod-proxy
(topología de credenciales), tabla completa de opciones (baseUrl · model ·
headers · request · fetch) y qué POSEE el adaptador como traducción de
dialecto (SSE→v1, sanitización de ids, re-pairing de tool_calls, split por
id en servidores que reutilizan index 0, error de cable → transport-error).
README del arte: opciones inline en la entrada del adaptador.
check 0 propios · smoke agnt PASS.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
La respuesta a «¿está pensando?» no se improvisa — es Aura, el componente
del eje construido para ello. La sección real de la página agnt bootea un
uix standalone mínimo (createActiveUix + Soma + ActiveEidos + eidos css):
legítimo porque un app compone ambos tiers, y la demo ES el app.
- Motor llama → createActiveAgent (mismas opciones bus+journal); activeRun
reactivo satisface AuraAgentSource; stream vía .engine.onEvent
- <Aura {agent}> con Indicator (orb $scene) + Status + Region + Cancel;
authorize/escalation quedan como afordancias app-level (doctrina §7)
- Verificado sobre el run real de qwen3-coder: «Inactivo» → «Propone un
plan» → announcement «Agente: terminó — control devuelto» en la Region,
doc mutado, answer en es; Cancel = Button disabled-en-idle; consola 0
check 0 propios · agent 30/30 · smoke agnt PASS.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
El bus como eje del agente a nivel de app, cerrado en tres direcciones:
observar (agent.*), reaccionar (orca) e INICIAR — hecho de dominio → acción
orca → App.agent.start({initiative:'system'}) con clamp auto→review por
construcción. Nunca command-events. Sección doctrinal + ejemplo en la página.
Adaptador real `$agent/adapters/openai-chat` (fuera del barrel, D-AG.1c):
dialecto OpenAI chat-completions (Ollama/llama.cpp/LM Studio/proxy), SSE →
protocolo v1, sanitización bidireccional de capability ids con puntos, y
re-pairing de tool_calls al reproducir el transcript (memoria por callId).
Fix cazado en vivo: Ollama reutiliza index:0 para todas las calls del
stream — una call nueva se detecta por id distinto, jamás por index (los
args concatenados producían invalid-args en motor + 400 en el replay).
Demo viva en /active/docs/agnt contra qwen3-coder:30b real: capacidades
doc.* tipadas (sium), read snapshot-por-turno, streaming, escalada
irreversible. Ciclo completo verificado en navegador: 3 acts, 2 turnos,
doc mutado, respuesta en es, rastro entero en bus (correlationId=runId).
agent 30/30 · smoke agnt PASS · check 0 propios.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Auditoría punto-a-punto contra los README de bus y orca (12/13 conforme).
La no-conformidad real: `goal` viajaba en AgentRunBusPayload, contra la
higiene documentada de ambos (bus: «pasa correlationId y que el suscriptor
resuelva el contexto sensible del módulo»; orca: «no unnecessary private
data — la acción lo resuelve del módulo al ejecutar la acción»).
- AgentRunBusPayload = SOLO metadata (runId/initiative/autonomy/state/
outcome/reason/acts/turns); el suscriptor resuelve vía App.agent.run(runId)
- spine test prueba el patrón: 'goal' in payload === false + resolución
desde el módulo con el orca REAL
- página agnt: nota de higiene + recomendación configureEvent(
'agent.run.state', { queuePolicy: ORCA_QUEUE_REPLACE_QUEUED }) para el
evento frecuente (doctrina de colas de orca)
- PLAN-agent F4a: acta de la auditoría de conformidad
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Auditoría del track (encargo): divergencias firmadas-sin-implementar
corregidas y el marco de integración INDEPENDIENTE del componente — el agente
como servicio del app, no como demo de un componente.
Fixes de decisiones firmadas (con tests):
- C3/D-AG.7: la iniciativa autónoma (system/scheduled) clampa auto→review —
un run que el usuario no pidió jamás se auto-autoriza.
- D-AG.5: presupuesto de contexto (maxContextBytes) con FIFO-trim byte-based
(el goal sobrevive; resumen semántico diferido) + log del recorte.
- F4c: tope por-arg (maxArgBytes) → invalid-args tipado.
- D-AG.6d: schema Standard-Schema del resultado (`result?`); un mismatch
vuelve como recoverable para auto-corrección y no cuenta como act.
Integración app (patrón session EXACTO):
- El arte posee el vocabulario `agent.*` (consts) + bus-helpers tipados que
publican con bus INYECTADO (type-only $libs/bus): run.opened/state/
escalated/closed + act.applied, con source='agent' y correlationId=runId —
la costura causal ligera (una delegación correlaciona de punta a punta).
- defineActiveAgent cablea core.bus (junto a timers/logger).
- Reacciones vía orca APP-LAND (el invariante intacto): probado con
EngineOrca REAL reaccionando a agent.run.closed en integration.test.
- Página del arte /active/docs/agnt (la convención que faltaba): sección
Delegation en nav; artículo + playground VIVO con los 4 motores puros
reales (bus log en vivo, journal WAL, contador orca, kill switch,
compuertas deterministas) — cero componentes, integración pura de app.
Gates: agent 26/26 · smoke agnt PASS · check 0 propios · verificado en
navegador (pane + Chrome real): ciclo completo, orca archiva runs, kill
switch reactivo.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nueva sección de estado en el handoff vivo: el barrido adversarial (18/18 arreglados,
6 refutados, 7 commits verificados por test/Playwright), la infra Menubar.Panel que se
commiteó (bd5eb1925) y reparó (ef85b9223) tras leerla erróneamente como ajena, el #6
(getOrderedValues por orden DOM, 92d060a4f), la verificación en barra de menús, y la
lección: uncommitted ≠ ajeno tras un context-summary. Doc de la API Menubar.Panel ya
en el README de soma/menubar (con la infra).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adversarial-review outcome (fixes + documented v1 limits) and the two
piggybacked sticky type-fixes recorded; scoreboard corrected (prose F1.5
had also landed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
svelte-check surfaced two F1.1 type errors: the sentinel is a writable
ref bound by the wrapper, so it belongs in StateProps (not ActiveProps —
part() wants State<HTMLElement|null>); and the eidos types barrel must
import the renamed ProviderProps export, not the internal StickyProps.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Nav landmark + native anchors; IntersectionObserver band detection
(never a scroll listener + getBoundingClientRect — anti-reflow doctrine).
aria-current="location", per-depth rail indent, data-active drives the
eidos accent. Real-IO chromium test: band tracking + bottom dead-zone.
Adversarial-review hardening:
- ref-count register/unregister so duplicate hrefs / mount churn can't
drop a still-referenced target (+ test)
- tokenize the rail transition (--duration-fast/--ease-default) and the
focus outline-offset (calc(var(--focus-ring-width) * -1))
- document two v1 limits (instant-jump dead-zone, link-order == section
order) — both need a v2 observation-contract change
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cierra los dos flecos de F3 tras la corrección del usuario (la review-card
con diff era anclaje en dev-tools, sin sentido a nivel framework):
- agent.md §7 gana «The review surface»: el FRAMEWORK posee los estados del
ciclo expresados perceptualmente (reviewing/escalated con afordancias
authorize/reject/cancel — Aura ya las da) + la semántica de «propuesto por
otro actor» + los anuncios atribuidos; el APP posee el CONTENIDO de lo que
se revisa, en su propio dominio (un documento enseña sus bloques
propuestos, un calendario sus eventos pendientes). Una tarjeta-diff estilo
git es legítima en una aplicación concreta; jamás canon.
- §6 fatiga de aprobación enmendada en el mismo sentido (el cambio concreto
en el dominio del app, nunca prosa).
- PLAN: F3 CERRADA — review resuelta como doctrina sin componente nuevo;
`allowedFamilies` del primer componente actuado reubicada a F2 (su hogar
natural es el piloto). Siguiente: F4 por la vía del chat.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
El barrido de calidad de palabras confirmó que el foco por flechas (ArrowLeft/Right,
Home/End) y el hover-slide divergían del orden visual tras navegar a un elemento con
más secciones contextuales: el `{#each}` keyeado reutiliza los triggers persistentes
y los nuevos se APPENDEAN al Map después, así que el orden de inserción ≠ orden DOM.
Ahora getOrderedValues ordena por compareDocumentPosition (query estructural,
reflow-safe), corrigiendo navigateAdjacent/focusEdge/onContentKeydown/hover-slide en
un punto. 5/5 tests del menubar verdes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cierra el primer componente del eje agéntico de punta a punta (las 9 fases de
la ruta). `component:audit` PASS.
- README eidos con comparativa (5 refs adyacentes — thinking-indicators,
CopilotKit activity, Vercel useChat.status, presencia colaborativa
Figma/Docs, assistant-ui) reconociendo el paisaje escaso: nadie tiene una
semántica perceptiva de la delegación con a11y (informe §8.3). Decisiones +
Gaps con disposición.
- Fixes del audit: opacity literal `0.5` → `var(--opacity-50)` (R-4.2);
excepción R-1.5 documentada (único focusable = Cancel, que compone Button);
`apg: 'none — …'` en el morfo (A-1.4).
Gates: component:audit PASS · morfo:vocabulary limpio · eidos-lint aura 0
inválidos · recipe-contract verde.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reparan el commit anterior (era WIP con errores de svelte-check; runtime OK):
- provider: `tabindex:-1` en los props del panel estaba duplicado (focusScope.props
ya lo aporta con el mismo valor) → quitado el explícito.
- menubar-panel: el prop `style` (de PrimitiveDivAttributes = `Record<string,unknown>`)
no encajaba con el opt del provider (`StyleProperties`) → cast en el create.
- test: `props['aria-haspopup']` no es clave estática del tipo (es atributo del morfo
resuelto por renderProps en runtime) → acceso vía cast a Record.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Materializa el eje ortogonal agéntico del ecosistema: la 8ª familia semántica
`delegate` («¿quién actúa ahora?») deja de estar sin materializar. El agente es
OTRO ACTOR (LLM, macro, regla, workflow) que actúa por la MISMA API pública del
provider; la ruta de llamada no se bifurca, la concreción semántica depende del
actor.
F0 · Doctrina
- docs/architecture/agent.md — la doctrina permanente (capítulo comparativo de
referencias + bibliografía de seguridad, máquina estados=verbos, contrato de
participación, fila §0, a11y, amenazas).
- docs/process/{PLAN,TRIAGE,INFORME}-agent — plan de ejecución con todas las
decisiones firmadas (D-AG.1–11 + ⚖️1/2/3), triage de 4 revisores externos, e
informe autocontenido para revisión externa.
F1 · Motor ($libs/actor + arts/agent, 24º arte)
- EngineAgent puro (sin DOM/runes, portable a servidor): máquina D-AG.4
(suggest/review/auto · escalated con reason+timeout · returned outcome-tipado
· kill switch · autorización journaled aunque sea auto), tool-loop D-AG.5
(acts secuenciales, fallos→verbos, unknown-capability acotado, idempotencia
por callId, presupuestos acts/turns/wall-clock vía puerto de timers), techo de
autonomía por origen no confiable (F8b).
- ActiveAgent (sesión reactiva, contrato ActiveEngine) · protocolo v1 espejo
AG-UI (5 categorías + dirección tipada + reservas) · ScriptedAgentTransport
determinista (adapters/ fuera del barrel) · journal WAL + puerto de trazas
OTel · emisor sium→JSON Schema.
- Acuñación del actor (⚖️2/F6b): ActorToken opaco en $libs/actor (hoja bajo
orca/agent), registro privado WeakMap — los forjados resuelven a null;
costura `actor?` en TriggerOptions→SemanticSignal (runtime copia verbatim;
sema no resuelve). defineActiveAgent (service-factory app-level, timers del
core — cero setTimeout a pelo).
F3 · Aura — primer componente del eje (ruta 9 fases 0–6)
- El materializador canónico de `delegate` (reservado en scene §F6): morfo con
los eventos del ciclo (offer/escalate-untilAction/return + sustain-processing
stateBound) — PRIMER morfo del ecosistema que emite familia delegate; provider
soma que observa un puerto estructural (sin importar $agent — degradación
total); orb eidos = aurora $scene modulada por estado (§F6) con fallback CSS
= render de reduced-motion; live region única atribuida (WCAG 4.1.3); cancel
compone Button (asChild). Demo v2 sobre el MOTOR REAL con compuertas
deterministas. Orb-size derivado de la primitiva Avatar (32/40/48).
D4 · Promoción uix.scene (Aura llegó)
- defineEngineScene + superficie ActiveUix/ActiveEidos/contracts; el orb prefiere
el motor compartido (presupuesto de escenas global) con fallback por-superficie.
Gates: arts/agent 18/18 · aura 6/6 · arts:check 24 · eidos battery + recipe
30/30 + eidos-lint aura 0 inválidos · morfo:vocabulary + esquemas · smoke aura
PASS · verificado en navegador (ciclo completo + estampa delegate en DOM +
aurora WebGL pintando). Los tokens de recipe base.ts + CSS generado ya entraron
en HEAD vía una sesión concurrente.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Componente nuevo `menubar-panel.svelte` + soporte en el provider (FloatingContent
para anclar el panel a su opción, FocusScope, Dismissal) + types/exports/content/
README/test. Es la infra ribbon-style que el `palabras-element-inspector` (ya
commiteado en esta rama) COMPONE — cada sección del elemento = un Menubar.Panel
corto. Trabajo de esta sesión que había quedado sin commitear; sin él la rama
quedaba incompleta (el inspector referencia `Menubar.Panel`).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Las entradas del sidebar de docs para los componentes F1 sticky (grupo
Layout) y prose (grupo Typography), diferidas de sus commits por un estado
CRLF del archivo (autocrlf); ahora normalizado a LF y committeado limpio
(3 líneas). Los componentes ya estaban en 998b11d9b / 5b84749ee.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Quinto componente F1 del plan blocks (eidos-only; alcance E-2 = suelo del
dossier §P4). Estiliza HTML/markdown RENDERIZADO que el app pasa como children.
EL DIFERENCIADOR (que ninguna referencia puede): cada regla de elemento se
emite a especificidad CERO vía `:where([data-prose] EL)`. Como todo componente
del framework se estiliza por `[data-{component}]` (≥0,1,0), un <Button> /
<Callout> / <Card> embebido conserva su PROPIO estilo AUTOMÁTICAMENTE — sin
anotación `not-prose`. Verificado en navegador: un <Callout> dentro de prose
mantuvo su tinte affirm, borde-acento 3px y layout grid en claro Y oscuro; las
reglas :where de prose no lo tocaron. El donut `:not(:where([data-prose-ignore],
…))` es el opt-out explícito para subárboles HTML crudos.
- morfo: 2 partes (provider bare data-prose + ignore documenta el attr opt-out;
eidos-lint lo ve morfo-backed). Selectores de elemento = estructurales bajo la
parte (S1). 0 eventos, apg:none, passive
- recipe: cobertura de paridad (h1–h6 incl. h5/h6 que tailwind NO; p, a, strong/
em, blockquote borde-acento lógico, ul/ol + ::marker, dl, code/pre + reset kbd,
img/figure/figcaption, hr, table); ESCALA EM-relativa (un font-size raíz
re-deriva todo — no la escala 5× de tailwind); overflow de tabla estilo GitHub
sin wrapper (display:block+overflow); trim de bordes; SIN backticks en code
(doblarían sobre <Code>); dark GRATIS por roles (no el patrón -invert);
propiedades lógicas → RTL correcto
- decisión :where (no @scope): :where establecido en el codebase + soporte 2021+;
@scope diferido a Gaps (mitad de tamaño pero «Baseline newly available» sin
precedente aquí)
- demo v2 9 tabs con markdown real + <Callout>/<Button> embebidos (prueba de
auto-exclusión) + ejes size/measure; README (Comparativa 5 refs · Decisiones ·
Passive · Gaps con disposición)
Verificación: component:audit PASS 0E/0W (21 literales em con /* literal */
justificado + eidosSnippet) · eidos-lint 45 morfo-backed/0 invalid ·
recipe-css-contract 30/30 · svelte-check sin errores propios · navegador:
h1 36px/bold, h2 24px, medida 65ch (~692px), blockquote acento, tabla
display:block+overflow, code chip, AUTO-EXCLUSIÓN del Callout en claro y oscuro.
Nota: nav sidebar fuera del commit (flip CRLF ajeno); índice langs reconstruido
prose-only (entangle con aura sin commitear de sesión paralela).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
La cadena de fieldControl (inspector + panel) manejaba pills/toggle/number/slider/
length/textarea/text/spacing/border/typography/adjustments/color pero NO select ni
chip (ambos válidos en PalabrasFieldType / esquemas de diseñador) ni un else final,
así que un campo de esos tipos daba una celda vacía muerta. Añadido un {:else} que
muestra el valor actual en read-only (mapeado a la etiqueta de la opción si hay
options) — que ADEMÁS es el render correcto de `chip` (display read-only). Un control
`select` funcional queda como diseño diferido (flagged). Latente: ningún esquema
shipped lo usa.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adversarial review confirmó (minor): el recipe fijaba en el eje LÓGICO
(inset-block-start/end) mientras el rootMargin del observer es FÍSICO
(top/bottom). Coinciden en todos los modos horizontales (LTR y RTL: top/bottom
son eje de bloque, invariantes al flip inline), pero DIVERGEN en writing-modes
verticales, donde inset-block-start = izquierda/derecha física ≠ el borde que
el observer vigila → data-stuck se dispararía en la posición equivocada (o
nunca). La API `edge` es física ('top'|'bottom') y el observer es físico, así
que el CSS debe ser físico también (mi razón previa «lógico por RTL» era
incorrecta — RTL no afecta el eje de bloque).
- sticky.css: inset-block-start/end → top/bottom; margin-block-end/start del
centinela → margin-bottom/top (mismo eje de bloque, RTL-safe)
- demo: corregidas las 3 menciones «logical inset / writing-mode correct»
Verificado: component:audit PASS 0E/0W (físico top/bottom = eje bloque, no
dispara regla RTL) · test real-IntersectionObserver en chromium PASS (físico
top en horizontal = comportamiento idéntico). Nota: la lente teardown-ssr del
review falló por API stall mid-stream; esos caminos ya están cubiertos por los
tests (cleanup/disconnect aserted, disabled aserted, SSR no-op por contrato de
ActiveDom).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- #13: el filtro `kinds` (oculta un campo si el `kind` del bloque no coincide) solo
se aplicaba a los built-in de Contenido/Diseño; los campos de Avanzado y TODOS los
extras de esquema lo ignoraban (solo filtraban por `.tab`), mostrando un campo
kind-restringido para cualquier variante. Aplicado fieldForKind consistentemente en
ambos consumidores (element-sections + panel). Latente (ningún esquema shipped lo
usa) pero honra el contrato documentado.
- #18: los presets Interlineado/Tracking caían a un default hardcodeado ('1.5'/'0')
cuando el bloque no tenía override, resaltando un valor falso aunque el esquema
declarara otro (el esquema por defecto declara text.lineHeight 1.6). Ahora heredan
el valor EFECTIVO del esquema (schemeLineHeightFor/schemeLetterSpacingFor, espejo de
schemeFontFor/defaultFontSize), cableado en panel + inspector. Verificado: el párrafo
del esquema por defecto muestra Interlineado "Personalizado" (1.6, no el falso 1.5)
y Tracking "Normal" (sin regresión).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Registra sticky (998b11d9b) en el registro del plan: primer componente
behavioral, mapa de construcción por workflow, lecciones (jsdom-pragma
ignorado en browser mode → test IO-real en chromium; entangle de barrels
con sesion paralela; tension del test harness declarada). F1 = 4/8.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Primer componente F1 con comportamiento real (soma), y el que desbloquea el
block site-header (F2.1). Construido tras un fan-out de investigación (6
lectores) que fijó el mapa: template = FeedSentinelProvider, motor =
IntersectionObserver por el port bendecido de $adom, escritura de attrs por
syncAttrs (nunca dom.apply crudo). Alcance E-2 = suelo del dossier §P3.
- morfo: 2 partes (provider=box → data-sticky/data-edge/data-stuck ·
sentinel aria-hidden → data-edge); 0 eventos con justificación de COMPLEX
BEHAVIOR (apg: none); data-stuck presente/ausente + data-edge enum
espejan @container scroll-state(stuck: top/bottom) — este componente es el
polyfill cross-browser de ese contrato CSS futuro
- soma: un StickyProvider registra AMBAS partes en un runtime; el observer
vive en un $effect que RETORNA observeIntersection(...) como teardown
(mirror de feed); el callback async voltea stuck=$state; rootMargin
DERIVADO del offset (la línea de disparo del centinela ES la línea de pin
— acoplamiento matemático, offset obligatorio); root=scroll-host
(default viewport), Document coercionado a null; offset viaja como
--_sticky-offset (dato A8, no CSS visual — precedente meter)
- eidos: recipe fino que SOLO posiciona (position:sticky + inset lógico
desde --_sticky-offset + token --sticky-z-index + centinela layout-neutral
2px con margen negativo); el tratamiento del estado pegado es del
consumidor ([data-sticky][data-stuck]) — el recipe posiciona, no decora
- centinela = HERMANO de flujo del box (dos nodos raíz), no hijo (un hijo se
fija con el box y nunca detecta); orden según edge
- demo v2 9 tabs con scroll container real + readout de stuck vivo; READMEs
soma + eidos (Comparativa AntD/Mantine/Chrome-technique/headless/CSS ·
Decisiones · Passive justification · Gaps con disposición)
Verificación: component:audit PASS 0E/0W · eidos-lint 6 morfo-backed/0
invalid ([data-stuck] sin regla = intencional, consumidor decora) ·
morfo:check limpio · svelte-check sin regresión propia · 5 tests: 4 unit
(rootMargin por borde, root null-coerce, flip de stuck, disabled, cleanup) +
1 REAL-IntersectionObserver en chromium (foreground: no-stuck→scroll→
stuck→scroll-back→no-stuck — prueba end-to-end de la geometría offset↔
rootMargin, lo que el pane suspendido no puede). Navegador: box pinea a los
12px del offset, recipe correcto.
Decisión declarada (test harness): sticky-provider.svelte.test.ts usa el
installSomaHarness compartido (translator identidad) como los otros 86 tests
de provider — TÉCNICAMENTE en tensión con la regla CLAUDE.md «never fake
translators; use createActiveUix». Son unit tests del provider; la
integración real la cubren contracts.test.ts + la verificación en navegador.
Seguido el precedente universal; flaggeado para tu ratificación.
Nota: nav sidebar de sticky (web/routes/uix/+layout@.svelte) fuera del commit
— el archivo arrastra un flip CRLF de sesión paralela (2125 líneas de ruido);
la entrada viaja en el árbol de trabajo. Registros langs/soma de sticky
reconstruidos sticky-only (los índices tenían entangle con aura sin commitear).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nuevo efecto `water` en $scene/effects + registro en el pack Ambient
(import = opt-in). Superficie de mármol de vetas incandescentes sobre
negro que se ondula bajo el ratón: cada tramo de puntero engendra un
anillo que se expande y refracta la superficie (modelo de anillos de
pixel-blast). 11 params override por token (P-4). Alta en el catálogo
demo (Tier B, 33 efectos) y en el README del pack.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- #4: el guard capture-phase del BubbleMenu hacía preventDefault + restoreDomSelection
en TODO pointerdown del bubble, así que clickear el input de tamaño nunca lo
enfocaba (solo +/− servían). Ahora se salta el guard cuando el target es un control
de formulario nativo (input/textarea/select): el input se enfoca y como
syncSelectionFromDom CONGELA el modelo con foco fuera del editor, el bubble sigue
abierto y la marca aplica al rango. Los botones (marcas) no necesitan foco y siguen
con onclick; el resto de clicks siguen protegiendo el rango. Verificado Playwright:
teclear "48" + Enter aplica font-size:48px.
- #11: openLink saca el foco del editor al input de enlace; cerrarlo (aplicar O
cancelar con Escape) no lo devolvía → editor sin caret, teclado muerto hasta
reclicar. Ahora returnFocusToEditor() (api.focus + api.restoreCaret) en ambos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Find (#8): close() solo cambiaba `open`; la query/matches viven en el provider
raíz, así que cerrar dejaba los resaltados <mark data-palabras-find-match>
pintados sin UI para quitarlos (contradecía el contrato documentado). Como el
`find` del snippet no llega al close() del script exterior, muevo el Escape al
onkeydown del PANEL (dentro del snippet, con `find`) y limpio la query en ambos
cierres (✕ + Escape). Verificado Playwright: Escape cierra y borra los 7 marks.
- Find (#15): el contador podía mostrar N/M con N>M si los matches menguaban por
una edición directa (currentMatchIndex no se re-clampa). Clamp en el display.
- Slash (#9): 14 ítems, ~9 visibles; la navegación por teclado no hacía scroll al
activo → Enter insertaba algo invisible. Efecto (raf-diferido, iframe-safe) que
hace scrollIntoView del [data-active]. Verificado: ArrowUp→"Columnas" en vista.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cuatro defectos confirmados por el barrido:
- Panel (#5): tras clickear una tab (tabAuto=false), cambiar a un bloque sin
Contenido dejaba la tab en 'contenido' (trigger no renderiza) → panel en blanco
sin tab activa. El efecto de auto-landing ahora corrige una tab INVÁLIDA aunque
tabAuto=false (cae a la 1a disponible). Verificado Playwright: párrafo → Diseño.
- Inspector (#7): el draft de `src` derivaba `alt` de split('/')/split('.') →
basura base64 para data: URLs. Ahora se salta la derivación para data:.
- Spacing (#10): el override de enlace (localUnlink) se reseteaba por
JSON.stringify(value); dos bloques sin spacing serializan igual ('null') → el
override se filtraba entre ellos. Ahora resetea por identidad de bloque
(resetKey=api.activeBlockId), cableado en panel + inspector.
- Draft-field (#12): Enter llamaba confirm() y luego blur() que re-dispara
onblur=confirm antes de que `value` se propague → doble onCommit → 2 entradas
de historial. Guard de reentrada por el flag `editing`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tres defectos confirmados por el barrido en el mismo fichero:
- Click SECO (sin arrastrar) en un tirador de columna congelaba un ancho fijo
espurio: el guard comparaba liveWidth (float de getBoundingClientRect) contra
Math.round(startWidth), asi que cualquier columna sub-pixel disparaba el commit
(auto -> fijo) + entrada de historial. Ahora un flag `moved` (solo true si hubo
pointermove) hace del click un no-op real.
- Reflow forzado: onDragUp limpiaba el width inline y LEIA rects sync
(remeasure) → sync-after-write. Diferido via dom.measure (frame post-layout
coalescido), que ademas mide la geometria ya comprometida, no la transitoria.
- Sin pointercancel ni cleanup de desmontaje: una interrupcion (touch/pen cancel,
gesto) dejaba dragKey atascado (handles congeladas, remeasure off) + listeners
de window filtrados. Anadido onDragCancel (revierte sin commitear) + un $effect
cuyo cleanup libera un drag en vuelo al desmontar.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tercera pieza F1 del plan blocks (alcance E-2, dossier §P3). Banner = tira
de anuncio de pagina; Callout = el aside del documento (split deliberado).
- morfo: 4 partes (provider role=note / icon / title / content) + texts con
TITULOS DEFAULT LOCALIZADOS por intent (note/tip/warning/caution — patron
GitHub de label visible: la semantica nunca viaja solo en color/icono);
0 eventos justificados (dismissible DIFERIDO a pasada soma+sema por la
regla de admision — dismiss ES un evento real)
- el hueco IMPORTANT resuelto con el modelo Radix, sin 5o enum: `intent`
(neutral|affirm|risk|threat — mapea NOTE/TIP/WARNING/CAUTION) + `color`
override SOLO bajo neutral (doctrina §4: intent evaluativo gana);
IMPORTANT = intent neutral + color + titulo propio (preset en la demo)
- pintura sobre la maquinaria C6/THM-2 (patron badge): forwarders por color
+ slots `_palette-track/text/solid` → el generador emite la cascada de 8
roles Y el forward presence-guarded al shared layer — 33 escalas y
colores custom con CERO CSS extra
- a11y: role=note NOMBRADO via aria-labelledby → Title SOLO mientras esta
montado (registro reactivo por context local); escalacion tipada
role=status|alert|none (el role=alert estatico de shadcn = bug de
referencia que NO copiamos); Title NO es heading (protege el outline que
escaneara anchor-nav); icono decorativo = mapa doctrinal IntentIcon
- fix cazado en navegador: el `+=` del registro leia el estado dentro del
tracking del $effect del hijo → effect_update_depth silencioso (contador
a -997, attr nunca estampado) — untrack() en register/cleanup; leccion
registrada en memoria (incidente 2 de la clase)
- recipe: grid con acento logico border-inline-start (RTL-correcto),
nesting tolerado (Docusaurus); demo v2 9 tabs con preset IMPORTANT +
PalettePicker showIntent=false; README completo
Verificacion: component:audit PASS 0E/0W · eidos-lint 8 morfo-backed/0
invalid · guards 37/37 · svelte-check 76E/51W = baseline exacto ·
navegador: «Nota»/«Atencion» localizados · risk=ambar hue 45-60 ·
IMPORTANT=plum hue 326 resuelto por shared layer · labelledby=titleId ·
cero errores de consola.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dos bugs confirmados por el barrido adversarial del editor:
1) El fantasma de respaldo + borde de requerido no se pintaban en los ITEMS de
lista: renderListItem nunca pasa por renderBlock (donde se estampa el
fallback), asi que un item sin traducir salia vacio sin la referencia del
idioma origen ni el aviso de requerido — aunque documentLangHasContent SI lo
cuenta, dejando el punto de completitud incoherente. Espejado el mismo
patron de renderBlock en el <li> via ctx.decorations.i18nFallback.
2) Un write externo a `value` que cambia activeLang se revertia solo:
syncExternalState actualizaba activeLangState pero NO opts.activeLang.current,
asi que el efecto de sync leia el prop viejo y llamaba setActiveLang(idioma
viejo). Ahora sincroniza el prop (como setActiveLang y el constructor).
Tests: render (li fallback + required flag) + provider (write externo con
activeLang distinto se honra, no revierte). 35 render + provider verde.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Segunda pieza F1 del plan blocks (alcance E-2 = suelo de paridad, dossier
§P3). Comparte el esqueleto de empty-state a proposito: EmptyState describe
AUSENCIA de datos, Result reporta un RESULTADO — un lenguaje de layout, dos
contratos (duplicacion consciente registrada en el README; revisable al 3er
consumidor).
- morfo: 6 partes display (provider/media/title/description/actions/extra;
extra SIN archetype — parte genuinamente propia), scope ['eidos'],
0 eventos justificados (el resultado ya OCURRIO antes de renderizar)
- status: enum de 7 = paridad AntD CON `warning` (decision firme del
dossier) y HTTP renombrados semanticos (forbidden/not-found/server-error,
nunca '404' stringly); default 'info'; data-status = attr eidos-only
- media default: compone el mapa doctrinal IntentIcon (success→fulfill ·
error→threat · warning→risk) + Info de catalogo; HTTP = codigo mono
grande NEUTRO aria-hidden (situaciones, no fallos — AntD jamas pinta 404
de rojo); children reemplazan el default entero (context local eidos-only
con getter reactivo)
- Title default h2 (vs h3 de empty-state — Result suele SER la pagina);
Actions con label→role=group; Extra alineado a inicio (detalle que lee)
- recipe: glifo display 3× xl bundle; tokens publicos
gap/actions-gap/measures + {status}-color como forwarders de rol
retintables; sin eje size (paridad AntD, gap diferido)
- demo v2 9 tabs con copy por status + entrada nav (Status) + README
(Comparativa · Decisiones · Passive justification · Gaps con disposicion)
Verificacion: component:audit PASS 0E/0W · eidos-lint 6 morfo-backed + 5
eidos-only sancionados · recipe/api/visual-attrs 37/37 · morfo:check verde
(los 7 fallos listados son deuda foranea preexistente) · svelte-check
76E/51W = baseline exacto · navegador: success=fulfill verde 84px ·
error=threat rojo · not-found=«404» mono neutro, copy conmutando, cero
errores de consola.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
El bubble tiene sub-vistas (typoOpen/linkOpen) que viven en el componente. Al
cerrarse solo se desmonta el contenido (el {#if bubble.open}), no el estado, asi
que la SIGUIENTE invocacion reabria directa en Tipografia/Enlace. Reseteo ambos
cuando la seleccion se colapsa (el bubble solo se muestra sobre un rango): una
edicion de rango preserva el rango -> no resetea; colapsar cierra el bubble ->
resetea. Repro Playwright: seleccionar->Aa->elegir fuente->colapsar->reseleccionar
= reabre en la fila de marcas (typoLeftover 0).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Al quitar el campo Texto, el parrafo se queda sin Contenido y el efecto de
seleccion de tab podia CAER en Avanzado (carrera: Diseño vacio un instante al
montar) y quedarse ahi. Ahora un flag tabAuto mantiene la tab en la primera
no-vacia (Contenido->Diseño->Avanzado) mientras el usuario no clicke una, robusto
a que las secciones lleguen tarde. Verificado: parrafo abre en Diseño (3/3).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El bloque data-palabras-tabs fijaba una margin tight en la lista pero NO anulaba
el gap del recipe Tabs (--tabs-stack-gap, ~16px), que se sumaba encima flotando el
acordeon lejos de las pestañas. Anulado a 0 (la lista ya trae margin tight y el
contenido su hairline + padding). Hueco tabs->cabecera 28->12px. Verificado Playwright.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Firmas del usuario (2026-07-21) tras el dossier de referencia:
- E-1: componente canonico nav-tree (F1.8, data-driven — precedente Menubar)
resuelve la colision sidebar-app vs arbol-docs/B-5; fiche nueva con
fase 0 obligada contra nuestro tree-view; docs-shell compone NavTree.
- E-2: suelo de paridad del dossier = alcance v1 (regla en intro F1).
- E-3: F2 pasa de 10 a 14 (banner · team · contact · content-section);
fiches nuevas + galeria actualizada (verificada en navegador: 28 badges).
- E-4: distribucion registry/llms.txt/MCP → iniciativa propia (next-features
§9; ese archivo sigue fuera del commit por diff foraneo).
- E-5: ⌘K = listener app-land documentado (nota en F4.1).
Doctrina: promotion path de architecture/blocks.md actualizado a 8.
Verificacion: docs:check 0/0 (512).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cuando una hoja de texto está sin traducir en el idioma activo, el editor ya no la
muestra VACÍA: pinta el contenido del primer OTRO idioma con texto (en orden de
`doc.languages`) como referencia atenuada — para que el autor vea QUÉ traducir. Y
si el idioma activo es REQUERIDO, un borde de precaución (intent `risk`) marca los
bloques que faltan.
Modelo A1 (estudio firmado): DISPLAY-ONLY, el documento NO se toca — la hoja sigue
vacía (sin traducir), el fantasma es un `::before` con el texto fuente; teclear lo
reemplaza (el provider quita el atributo en cuanto la hoja tiene contenido). Así
`langFilled`/`missingRequiredLangs` siguen siendo honestos (vacío = sin traducir).
- engine/i18n.ts: `computePalabrasI18nFallback(doc, requiredLangs)` → mapa por block
id { text (fuente), required } de las hojas sin traducir (visitor read-only).
- engine/render.ts: decoración `i18nFallback`; `renderBlock` estampa central
`data-palabras-i18n-fallback` + `data-palabras-untranslated` (sin tocar los specs).
- provider: computa el mapa en el derived `blocks` + `reqSig` invalida el memo al
cambiar los requeridos.
- palabras.css: fantasma atenuado (`::before` = attr) + borde dashed `--color-risk-*`.
95 tests (provider 62 · render 33) · 0 errores. Verificado Playwright: bloque solo-ES
→ en EN muestra «Solo en español» atenuado + borde ámbar (EN requerido).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Encargo del usuario (2026-07-21): estudio de referencia para "estar al menos
a la par y cuanto menos superarlo". 6 agentes de investigación web en
paralelo sobre fuentes oficiales vivas (varias verificadas a nivel de código
fuente: sidebar.tsx del registry shadcn, styles.js de tailwind-typography,
sources de Mantine/Starlight/Docusaurus/Ark).
- docs/process/RESEARCH-blocks-references.md — el dossier: P1 catálogos de
marketing (conteos+variantes) · P2 aplicación (ProTable/ProLayout,
dashboard-01, Tremor 303, Clerk/Supabase) · P3 F1 ligeros (APIs a nivel de
prop; scroll-state(stuck) como vocabulario de plataforma; role=alert de
shadcn = bug a no copiar) · P4 prose+sidebar (not-prose/:where()+donut;
shadcn 23 partes) · P5 docs shells (Fumadocs benchmark; colision F1.7 vs
nav-tree/B-5; ruta svelte2tsx para props TS) · P6 ecosistema Svelte
(shadcn-svelte 58 blocks solo-app; hueco "sistema integrado" VACIO) +
sintesis: 5 conclusiones, enmiendas E-1..E-5 (presentadas, firmas
pendientes), 10 angulos de superacion.
- PLAN-blocks.md — regla nueva §4 (toda fase 0 contrasta contra el dossier),
puntero de colision en F1.7, registro.
Verificacion: docs:check 0/0 (512 docs).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Fuera el campo textarea «Texto»/«Código» del panel (Contenido) en heading /
paragraph / quote / code: duplica la edición inline del canvas, sin ventaja, ruido.
El párrafo queda sin campos de Contenido → se oculta la tab vacía y el panel abre
en Diseño (efecto que mantiene la tab activa en una no-vacía).
- Menubar: `align="center"` en los Panel/Content (soportado por el componente) →
submenús y paneles CENTRADOS bajo su opción de barra (verificado delta=0).
- Menubar: `sectionCols` pesa los `pills` por nº de opciones → el nivel del heading
(7: H1–H6·HC) gana un tier más ancho y cae en UNA línea (panel 328→418px, 1 fila).
Verificado Playwright: párrafo sin tab Contenido ni «Texto»; heading conserva
Contenido (level+enlace) sin «Texto»; paneles centrados; nivel en una línea. 0 errores.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tier nuevo aprobado 2026-07-21 (D-BLK firmadas; D-BLK.1 enmendada: vive en
src/uix/blocks/). Plan de ejecución completo en docs/process/PLAN-blocks.md.
- docs/architecture/blocks.md — doctrina E1 (regla de admisión canon-vs-block,
contrato B-1..B-11, promotion path); espejo de packs.md
- alias $blocks → src/uix/blocks (vite + svelte.config + tabla CLAUDE.md)
- src/uix/blocks/README.md — mapa del tier + template de README de block (B-9)
- scripts/blocks-check.ts + npm run blocks:check — guard mecánico del contrato
B con self-test de detectores (fixtures inline; 5532 archivos de
canon/arts/libs/packs escaneados para dirección de imports)
- web/routes/blocks/ — galería índice del catálogo planificado + layout de
bootstrap propio (+layout@.svelte, espejo mínimo del de /uix sin packs sema)
Verificación: docs:check 0/0 (511) · svelte-check 76E/51W = baseline exacto ·
blocks:check verde · galería comprobada en navegador (a11y-tree + estilos
computados en light y dark; captura de píxel bloqueada por panel suspendido).
Fuera del commit: fila E1 en docs/README.md y §8 en docs/next-features.md
(diff foráneo de otra sesión sin separar — ver registro del plan).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>