Roadmap de MView Experimental
Fases, entregables y criterios para validar MView Experimental de principio a fin.
Roadmap de MView Experimental
Propósito
Este roadmap ordena la implementación y validación de la especificación Experimental. Cada fase debe probarse con artefactos ejecutables antes de congelar su contrato.
El trabajo prioriza dos resultados simultáneos:
- Robustez para runtimes: contratos completos, validables y seguros.
- Simplicidad para agentes: pocas decisiones, adapters reutilizables y ejemplos copiables.
Principios de ejecución
- Cerrar semántica antes de modificar el schema.
- Implementar primero el camino mínimo completo, no todas las capacidades.
- Probar contratos con al menos dos implementaciones independientes cuando afecten interoperabilidad.
- Tratar ejemplos y fixtures como productos de primera clase.
- No publicar una guía para agentes hasta que sus pasos puedan ejecutarse de extremo a extremo.
- No declarar estable un identificador de adapter o renderer sin pruebas de conformidad.
Fase 0: decisiones de diseño
Objetivo
Eliminar ambigüedades que cambiarían el schema o el comportamiento observable.
Decisiones
- Nombre definitivo de
source.inputosource.capability. - Identidad y versionado de adapters registrados.
- Identidad y versionado de renderers.
- Ubicación y merge de
settings. - Recursos compartidos frente a recursos por vista.
- Forma exacta de
filesystem. - Contextos mínimos entregados a adapter, transform y render.
- Política mínima del renderer HTML: scripts, CSP, sandbox y red.
- Reglas de unicidad para
views[].idy versionado del paquete.
Decisiones tomadas
| Decisión | Opción elegida | Alternativa rechazada | Motivo |
|---|---|---|---|
| Nombre del campo de capacidad | input en el manifiesto (source.input, source.files[].input, view no aplica) | source.capability | input ya era el nombre usado consistentemente en manifiesto, runtime (engine.js) y prosa; renombrar no aportaba claridad y hubiera roto la implementación existente sin beneficio. |
Forma exacta de filesystem / fuente de directorio | source.files[] explícito: { path, adapter, input, contentHash? }, sin descubrimiento implícito | source.mapping[] por extensión de archivo (lo que tenía mview.schema.json) | engine.js y viewer.js ya implementaban y probaban source.files; mapping no estaba implementado en ningún lado y, siendo dispatch implícito por extensión sobre lo que exista en el directorio, contradice directamente la garantía de seguridad "el runtime lee únicamente los archivos declarados, sin descubrimiento". El schema era el que estaba desalineado, no el runtime ni la prosa. |
contentHash agregado para directorio | Definido explícitamente: sha256(JSON.stringify(files ordenados por path, cada uno como {path, contentHash})), tal como ya lo calcula engine.js | Serialización canónica RFC 8785 | La spec Experimental prioriza simplicidad de implementación sobre interoperabilidad estricta entre lenguajes; documentar exactamente lo que el runtime de referencia ya hace evita divergencia entre spec e implementación. Puede revisarse más adelante si la interoperabilidad estricta entre lenguajes se vuelve un requisito real. |
| Identidad y versionado de adapters | IDs de string estables (csv-table, json-value, markdown-text, plain-text), sin campo de versión propio en el manifiesto; un cambio incompatible requiere nuevo ID | Versionado semántico por adapter (csv-table@2) | No hay todavía necesidad demostrada de múltiples versiones activas de un mismo adapter; agregar versionado ahora es especular. Ya documentado en la prosa ("Un cambio incompatible requiere otro identificador"). |
| Identidad de renderers | Único valor soportado en esta versión inicial: "html" (ya reflejado como const en el schema) | Enum abierto con múltiples renderers desde el inicio | Coincide con el alcance real implementado en viewer.js. Se documenta explícitamente como limitación de esta versión, no como diseño cerrado para siempre. |
| Recursos compartidos vs por vista | Solo view.resources (por vista) | mview.json-level resources compartido entre vistas | Es lo único implementado y probado en viewer.js (/resources/<viewId>/...). Recursos compartidos quedan fuera de alcance de este cierre; se puede añadir como campo opcional más adelante sin romper manifiestos existentes. |
Merge de settings | Package → view → overrides de runtime, shallow merge, sin merge recursivo | Merge profundo (deep merge) | Ya documentado y ya implementado ({ ...manifest.settings, ...view.settings } en engine.js); un merge profundo agrega ambigüedad sin caso de uso probado. |
| Contexto mínimo del transform | { source, settings, view, manifest } — se agrega manifest (id, name, description, version, source, settings, metadata) a lo ya documentado | Mantener el contexto sin manifest | engine.js ya entrega context.manifest; la prosa no lo documentaba. Se documenta el comportamiento real en vez de eliminar código funcional. |
| Política del renderer HTML | Normativa: inyección de window.mview antes de scripts inline, ejecución en iframe sandbox="allow-scripts" (sin allow-same-origin ni allow-forms), CSP connect-src 'none' en el documento renderizado, servidor de referencia enlazado solo a 127.0.0.1, refresh autenticado con token efímero de sesión | Dejar la política como no-normativa/orientativa | viewer.js ya implementa exactamente esto; convertirlo en contrato normativo evita que otras implementaciones bajen el nivel de seguridad. |
Unicidad de views[].id y source.files[].path | MUST explícito en la prosa + validación semántica obligatoria fuera de JSON Schema (JSON Schema no puede expresar unicidad por clave dentro de un array de objetos) | Confiar en uniqueItems de JSON Schema | uniqueItems compara igualdad estructural completa, no por clave; ya se necesitaba (y ya existía en engine.js) un validador semántico adicional. Se documenta como requisito explícito de cualquier implementación conforme. |
Entregables
- Registro breve de decisiones con una opción elegida y alternativas rechazadas.
- Manifiestos para archivo, directorio, una vista y varias vistas.
- Tipos TypeScript de todas las formas normalizadas y contextos.
Criterio de salida
Ninguna decisión pendiente afecta campos requeridos, formas normalizadas o límites de seguridad.
Nota sobre el entregable "manifiestos para archivo, directorio, una vista y varias vistas"
Las cuatro combinaciones existen implícitamente pero no las cuatro como ejemplos independientes:
archivo+una vista y archivo+varias vistas están en examples/csv/ (Ejemplo 1 y Ejemplo 2);
directorio+una vista está en examples/directory/. Directorio+varias vistas no tiene un ejemplo
dedicado — no hay nada en el schema o el runtime que lo restrinja (views[] no depende de
source.kind), es exactamente el mismo patrón de "agregar otra entrada a views[]" que ya
demuestra Ejemplo 2 sobre una fuente de archivo. Se considera cubierto por combinación de
ejemplos existentes, no por un caso separado.
Fase 1: contrato machine-readable
Objetivo
Mantener un único schema Experimental estricto.
Trabajo
- Mantener el modelo de una fuente y
views[]. - Usar
https://carmenlabs.com/experimental/schema.jsoncomo identidad durante toda la evaluación. - Definir condicionales para fuentes de archivo y directorio.
- Validar paths de paquete y paths relativos a directorio.
- Rechazar propiedades desconocidas.
- Añadir validaciones semánticas que JSON Schema no expresa, especialmente unicidad de
views[].idy escapes mediante symlinks. - Crear fixtures válidos e inválidos para cada regla.
Entregables
mview.schema.jsondefinitivo.- Validador semántico complementario.
- Corpus de manifests válidos e inválidos.
- Pruebas automatizadas de schema y paths.
Criterio de salida
Todos los manifests del catálogo de ejemplos validan y cada fixture inválido falla por la razón esperada.
Estado: cumplido
Los 7 manifests de ejemplo validan contra mview.schema.json (verificado ejecutando
validateManifest contra cada uno, no solo revisando a simple vista). El corpus de fixtures
inválidos vive inline en los tests en vez de como archivos .json separados —
test/directory-source.test.js y test/security-anti-examples.test.js cubren: fuente de
directorio sin files, files duplicados, views[].id duplicados, contentHash que no
coincide (agregado y por archivo), y escapes de ruta — cada uno con la aserción de por qué falla.
El validador semántico complementario es validateManifest en engine.js (unicidad de IDs,
compatibilidad adapter/input) más resolveContainedPath (escapes vía .. y symlinks).
Fase 2: contratos normativos
Objetivo
Documentar las garantías que permiten implementaciones portables y seguras sin inflar el manifiesto.
Trabajo
- Actualizar overview y referencia del schema.
- Reemplazar parser/component por transform/render donde corresponda.
- Especificar Source Adapter, registro, capacidad de salida y cache.
- Especificar serialización, determinismo y mutabilidad de entradas.
- Definir refresh y regeneración como operaciones distintas.
- Definir errores globales de fuente y errores aislados por vista.
- Completar seguridad, paths, red, symlinks y directorios.
- Sincronizar documentación en español e inglés.
Entregables
- Especificación completa en ambos idiomas.
- Matriz de requisitos
MUST,MUST NOTySHOULDpor actor. - Diagrama normativo del ciclo de refresh.
Criterio de salida
Un implementador puede construir un runtime sin depender de comportamiento implícito en los ejemplos.
Estado: parcialmente cumplido
unified-mview.md documenta parser/transform/render, Source Adapter y registro, serialización y
determinismo, refresh vs. regeneración como operaciones distintas (con diagrama del ciclo de
refresh), y seguridad/paths/symlinks/directorios completos, incluyendo una sección dedicada a
validaciones semánticas que el JSON Schema no puede expresar. La matriz MUST/MUST NOT/SHOULD
por actor no existe como tabla separada — está distribuida como listas de reglas dentro de cada
sección (Source Adapters, Transform, Render, Seguridad); se considera equivalente en contenido,
no en formato. Pendiente real: sincronizar documentación en español e inglés — se decidió
diferir la traducción a inglés hasta que el contenido esté completamente estable, para no traducir
contenido que todavía podía cambiar dos veces.
Fase 3: adapters de referencia
Objetivo
Eliminar la generación repetitiva de parsers físicos para formatos comunes.
Alcance inicial
| Adapter | Entrada | Forma | Límites explícitos |
|---|---|---|---|
csv-table | CSV UTF-8 | table | RFC 4180 básico, comillas y saltos de línea documentados |
json-value | JSON UTF-8 | value | JSON estricto, sin JSON5 |
markdown-text | Markdown UTF-8 | markdown | texto y headings, sin ejecutar HTML |
plain-text | texto UTF-8 | text | política explícita para BOM y UTF-8 inválido |
Trabajo
- Extraer adapters fuera de paquetes particulares.
- Definir metadata de registro y capacidad producida.
- Añadir fixtures pequeños y casos límite.
- Probar determinismo y errores legibles.
- Probar invalidación por cambio de bytes, opciones y versión.
Entregables
- Directorio
mview/reference/adapters/o ubicación definitiva equivalente. - Un módulo, README, fixtures y tests por adapter.
- API de registro mínima para runtimes.
Ubicación definitiva (decidido)
Los cuatro adapters viven en carmenlabs.com/public/experimental/adapters/*.mjs, servidos como
archivos estáticos en /experimental/adapters/*.mjs (los enlazan unified-mview.md y llms.txt).
mview/viewer los importa directamente desde ahí. Se descarta moverlos a
mview/reference/adapters/: duplicaría el módulo servido públicamente y crearía dos fuentes de
verdad para el mismo código. El registro mínimo de runtime (id, input, adapt) vive en
mview/viewer/src/adapters.js, y sus fixtures y tests de determinismo/errores están en
mview/viewer/test/adapters.test.js (32 casos, uno por adapter más los cuatro formatos y sus
límites explícitos de la tabla de alcance inicial).
Criterio de salida
Los cuatro formatos del alcance inicial pueden usarse sin copiar adapters al paquete ni generar código de parseo físico. ✅ Cumplido: cada adapter tiene pruebas de determinismo, encoding inválido y forma normalizada específica.
Fase 4: runtime y renderer de referencia
Objetivo
Ofrecer un camino ejecutable común para probar paquetes y renders HTML.
Trabajo
- Implementar el pipeline
validate → read → adapt → transform → render. - Compartir una adaptación entre varias vistas.
- Entregar props de forma consistente.
- Aislar fallos por vista.
- Restringir filesystem y red.
- Definir inyección segura de
window.mviewy política de contenido. - Exponer refresh sin escritura del paquete.
Entregables
- Runtime o biblioteca mínima de referencia.
- Renderer HTML de referencia.
- Reporte estructurado de errores por fase y vista.
- Pruebas de refresh, aislamiento y seguridad.
Criterio de salida
El runtime ejecuta todos los ejemplos y demuestra que una fuente se adapta una sola vez por refresh.
Estado: cumplido
mview/viewer implementa el pipeline completo (validate → read → adapt → transform → render),
comparte una única adaptación entre varias vistas (test/viewer.test.js — "adapts the source
exactly once per package refresh"), entrega props consistentes ({ data, settings, source, view, resources }), aísla fallos por vista, restringe filesystem/red en el transform (proceso
separado, permisos acotados, sin globals de Node) y en el render (iframe sandbox + CSP), inyecta
window.mview de forma segura, y expone refresh sin escribir el paquete (/api/refresh,
autenticado con token efímero). Ejecuta los 7 ejemplos y los ejemplos de directorio confirman
además que un archivo no seleccionado nunca se lee.
Fase 5: paquetes de ejemplo
Objetivo
Dar a agentes patrones completos que puedan copiar con cambios locales mínimos.
Ejemplos mínimos
- CSV con una vista HTML.
- CSV con dos vistas independientes.
- JSON con una vista HTML.
- Markdown con tabla de contenidos y resumen.
- Texto con métricas simples.
- Directorio con selección explícita y entradas heterogéneas.
- Vista que falla sin impedir otra vista válida.
Requisitos por ejemplo
- Fuente real pequeña.
mview.jsonválido.- Transform determinista.
- Render sin dependencias innecesarias.
- Snapshot o assertions del modelo derivado.
- Prueba de refresh sobre copia temporal.
- README con “qué copiar” y “qué reemplazar”.
Criterio de salida
Un agente puede crear una MView nueva seleccionando el ejemplo más cercano y modificando únicamente manifiesto, transform y presentación.
Estado: cumplido
Los 7 ejemplos mínimos están implementados bajo mview/viewer/examples/, cada uno con
README.md, fuente real, <paquete>.mview/, expected/data.json (generado ejecutando el
pipeline real, no a mano) y test.mjs:
| # | Patrón | Carpeta |
|---|---|---|
| 1 y 2 | CSV con una vista / CSV con dos vistas | examples/csv/ (pokemons-data.mview, vistas stats + pokedex) |
| 3 | JSON estructurado | examples/json/ |
| 4 | Markdown | examples/markdown/ |
| 5 | Texto plano | examples/text/ |
| 6 | Directorio explícito | examples/directory/ — además prueba que un archivo no declarado en source.files (march.csv) se ignora aunque exista físicamente |
| 7 | Vista que falla junto a otra válida | examples/failing-view/ |
test.mjs de cada ejemplo sigue el procedimiento de 8 pasos de "Qué debe contener cada ejemplo
implementado" vía el runner compartido examples/lib/example-runner.mjs (excepto
failing-view, que lo implementa a mano porque una de sus vistas falla por diseño). Construir
estos ejemplos encontró y corrigió un bug real en transform-runner.js: los errores lanzados
dentro del vm.Context del transform no son instanceof Error en el realm del host, así que el
manejo de errores caía a String(error) y anteponía "Error: " a todo mensaje de fallo de un
transform. Corregido con verificación por duck-typing (typeof error.message === "string").
Fase 6: experiencia para agentes
Objetivo
Reducir la creación normal a una guía corta y verificable.
Trabajo
- Reescribir la página canónica de creación para seleccionar adapter y ejemplo antes de generar código.
- Publicar una tabla “tipo de fuente → adapter → ejemplo”.
- Entregar templates de transform y render HTML.
- Añadir checklist automático de terminado.
- Prohibir explícitamente reimplementar adapters disponibles.
- Hacer que la instrucción inicial cree una sola vista salvo solicitud contraria.
Entregables
- Guía canónica para agentes.
- Skill opcional alineada con la misma guía.
- Prompt mínimo probado con distintos modelos.
- Evaluación de tasa de éxito y cantidad de archivos/código generado.
Métricas sugeridas
- Porcentaje de manifests válidos al primer intento.
- Porcentaje de campos inventados respecto de la fuente.
- Líneas de código generadas fuera de transform y render.
- Porcentaje de tareas que reutilizan el adapter correcto.
- Porcentaje de refreshes exitosos después de cambiar la fuente.
- Porcentaje de paquetes que respetan aislamiento de E/S.
Criterio de salida
La mayoría de los modelos evaluados crea el paquete correcto sin generar un adapter ni modificar la fuente.
Fase 7: congelamiento de Experimental
Objetivo
Congelar un contrato Experimental coherente respaldado por implementaciones y pruebas.
Checklist
- Schema, documentación y ejemplos coinciden.
- Español e inglés están sincronizados. — diferido a propósito hasta que el contenido esté estable (ver Fase 2).
- Todos los links y rutas públicas funcionan. — verificado con
curlcontra el dev server real, no solo revisando el markdown fuente. - Todos los ejemplos pasan schema, runtime y refresh. —
pnpm testenmview/viewer, 63/63. - Los adapters del alcance inicial tienen identificadores estables.
- El renderer HTML cumple la política de seguridad acordada.
- Las fuentes de directorio tienen pruebas de escapes y symlinks.
- Los fallos por vista están aislados.
- No quedan rutas, ejemplos o recursos obsoletos bajo
/experimental. — se eliminócarmenlabs.com/experimental/adapters/(directorio vacío sin referencias). Quedanapp/experimental/viewer/yapp/experimental/specification/: dos carpetas de ruta vacías (sinpage.tsxniroute.ts, devuelven 404) que no sirven contenido bajo/experimental— no rompen el checklist, pero conviene borrarlas o poblarlas; decisión pendiente del autor. -
/experimental/schema.jsonentrega el schema congelado. — verificado en vivo:directorySource.requiredincluyefiles, no existemapping.
Estado: cerrado con una excepción conocida
Experimental está congelado y coherente en todo lo que sirve /experimental, con una sola
excepción explícita y deliberada: la sincronización español/inglés. Ninguna otra casilla quedó sin
verificar contra el estado real del repo — no contra lo que debería estar, sino releyendo cada
documento, revalidando cada manifiesto de ejemplo, y confirmando cada ruta pública con una
petición HTTP real.
Criterio de salida
Existe una sola interpretación de MView Experimental y el camino recomendado para agentes está respaldado por implementaciones y pruebas reales.
Orden crítico
decisiones
|
v
schema + tipos
|
v
contratos normativos
|
+----------------+
v v
adapters runtime/renderer
| |
+-------+--------+
v
ejemplos
|
v
guía para agentes
|
v
congelar ExperimentalLos ejemplos pueden bosquejarse durante las primeras fases, pero no deben presentarse como canónicos hasta que schema, adapters y runtime los validen.
