Programa de unificación del Warehouse — los 5 puntos
⚠️ PARCIALMENTE OBSOLETO (predata 2026-07-09). Estado real y vigente:
docs/INFRA.md. Correcciones: catálogo activo = Lakekeeper (type=rest); SqlCatalog RETIRADO (P2.5); el vendingvendedestá ROTO sobre R2 → ruta = remote-signing; el hop/lakehouse/plan-filesfue suprimido. Queda como registro de diseño.
Qué es esto. El plan de trabajo que deriva de
warehouse-as-narrow-waist.md:
el punto único de la plataforma es el catálogo/formato (la cintura estrecha), no un
motor; el cómputo es plural. Este documento desarrolla los 5 puntos de ese plan — qué
es cada uno, por qué, qué implica en el código de Carbon, sus dependencias y su criterio de
"hecho". El Punto 1 se detalla como entregable aparte en
warehouse-compact-unit.md.
Mapa de dependencias
(1) Warehouse = unidad compacta con UN contrato ────┐ la base: sin esto, todo lo demás
│ (el objeto Warehouse + la puerta única) │ sigue fragmentado
▼ │
(2) Defender la SPEC abierta (Iceberg REST) ────────┤ el contrato de (1) es un estándar,
│ (el catálogo como interfaz, no producto) │ no un producto interno
▼ │
(3) Migrar TODOS los puertos a la puerta ───────────┤ cada building block = cliente de (1)
│ │
├──▶ (4) LIVE-SQL como puerto + Karma detrás ──┤ el caso especial (SQL arbitrario)
│ │
└──▶ (5) Cómputo plural gobernado por (1) ─────┘ N motores, una gobernanza
(1) es el cimiento; (2) define de qué material es su contrato; (3) es el grueso del trabajo (convergencia); (4) es el sub-caso que reintroduce a Karma correctamente; (5) es la regla transversal que gobierna todos los motores.
Punto 1 · Formalizar el Warehouse como unidad compacta con UN contrato de acceso
Qué. Hacer del Warehouse un objeto de primera clase: un modelo atómico de entidades
(Catalog › Schema › Table/View/Volume, respaldadas por el tridente R2+catálogo+gobernanza) y
una sola puerta de acceso por la que pasa toda lectura y escritura. Hoy el Warehouse está
implícito (repartido entre datasets, dataset_rows, la facade, los routers, el ml-runner);
el Punto 1 lo vuelve explícito y compacto.
Por qué. Es el cimiento de todo el programa: mientras el Warehouse no sea una unidad con un contrato, cada módulo seguirá improvisando su forma de hablar con él (la fragmentación observada). Los cinco grandes (Databricks/Snowflake/Foundry/Fabric/BigQuery) empiezan aquí: el control-plane como objeto único.
En Carbon. La cintura ya existe en germen: loadItemForConsumption ("LA PUERTA",
item-consumption.ts) + la jerarquía catalogs › schemas › datasets (mig. 20261231) + el tridente. El Punto 1 = (a) atomizar la
estructura (nombrar las entidades del Warehouse y sus invariantes), (b) congelar el
contrato de la puerta (ItemRef → DatasetHandle con read/stream/aggregate/write/schemaOnly
- gobernanza), (c) cerrar los bypass estructurales para que la puerta sea la única vía.
Hecho cuando. Existe una definición versionada del objeto Warehouse + su contrato de
acceso, y ningún nuevo building block puede tocar dataset_rows/R2 sin pasar por la puerta.
→ Entregable detallado: warehouse-compact-unit.md.
Punto 2 · Defender la SPEC abierta (Iceberg + REST catalog), no un binario ni un catálogo propietario
Qué. El contrato del Punto 1 debe ser un estándar abierto (la Iceberg REST Catalog spec), no una API interna acoplada a nuestro código. La cintura es una interfaz, no un producto.
Por qué. Es la lección más sutil de la investigación: cuando centralizas en el catálogo, el peligro de lock-in se traslada del motor al catálogo. Si el catálogo es un binario propietario, cualquier motor nuevo (o un tercero, o un segundo motor que valide a Karma) queda atado a nuestra implementación. La industria lo evita estandarizando la interfaz: Databricks (UC Open APIs), Snowflake (Apache Polaris OSS), Google (BigLake Metastore expone Iceberg REST) — todos hablan la misma spec REST. Defender la spec, no el binario, es también lo que hace a Karma genuinamente sustituible (el test de robustez ASF).
En Carbon. Hoy el catálogo es un PyIceberg SqlCatalog sobre Postgres servido por el
ml-runner (no hay endpoint REST). El Punto 2 = exponer ese catálogo por la Iceberg REST
Catalog spec (provisionar Lakekeeper como fachada REST del SqlCatalog, o implementar el
subconjunto REST sobre el catálogo existente), de modo que la resolución de tablas + el
credential vending sean un contrato estándar. Karma ya trae RestResolver para consumirlo.
Dependencias. Habilita (3), (4) y (5): todos los puertos y motores resuelven por el mismo
contrato REST. Es la corrección arquitectónica central que ya señaló
compute-integration.md (retirar el hop ad-hoc plan-files).
Hecho cuando. Un motor externo (o Karma) resuelve una tabla del Warehouse por Iceberg REST
- credenciales vendidas, sin tocar código interno de Carbon.
Entregable ✅ redactado: warehouse-open-catalog.md —
arquitectura de dos capas (gobernanza Carbon/Doberman sobre catálogo técnico Iceberg REST),
Lakekeeper recomendado (tipo cloudflare-r2 → vending R2 nativo; migración = re-registrar
las 82 tablas vía register API), vending scoped al prefijo de tabla (proxied→vended), y
rollout P2.0(=D3)→P2.5. Alternativa REST-over-JDBC descartada (reusa el PG pero sin vending R2).
Punto 3 · Completar la migración puerto-a-puerto a loadItemForConsumption
Qué. Cada building block que lee o escribe datos se convierte en cliente de la puerta. Es el grueso del trabajo: no inventar, converger.
Por qué. La fragmentación es, literalmente, convergencia incompleta. Cada puerto que sigue
tocando dataset_rows (o el ml-runner, o R2) directamente es una forma más de hablar con el
Warehouse que hay que retirar. Google/Microsoft "enchufan building blocks" haciéndolos clientes
del contrato de acceso, no integraciones ad-hoc.
En Carbon. La migración ya está en marcha, canary-validada. Puertos ya cableados
(según la cabecera de la facade): datasets.browse, graph.explorer.*, export.stream,
dataspace.preview, sdk.rows (notebooks). Pendientes (el inventario exacto lo produce el
entregable del Punto 1): dashboards data/query, model manager, map/geo, y —el caso especial—
LIVE-SQL (Punto 4). El Punto 3 = terminar ese inventario y migrarlo puerto por puerto, cada
uno detrás de su reader/writer-flag con shadow→canary.
Dependencias. Se apoya en (1) (la puerta) y (2) (el contrato REST). Contiene (4) como sub-caso.
Hecho cuando. Todo acceso a datos de la plataforma pasa por loadItemForConsumption; no
queda ningún lector/escritor que toque dataset_rows/R2 fuera de la puerta.
Punto 4 · Extender la puerta a LIVE-SQL con un motor plural detrás (Karma)
Qué. Las superficies que ejecutan SQL arbitrario de usuario (SQL Editor, Dashboards
QUERY) no se expresan como handle.read() estructurado; necesitan un ejecutor detrás de la
puerta. Ese ejecutor es Karma — enchufado detrás de la facade, no como un cuarto shim.
Por qué. Es el sub-caso que reintroduce a Karma en su rol correcto: un motor de cómputo subordinado a la cintura (como Photon/DuckDB/Trino frente a Unity Catalog), no el punto único. Hoy estas superficies tienen cada una su propio traductor JSONB→SQL (tres shims paralelos): la mayor fuente de fragmentación de lectura.
En Carbon. El acople anterior (Build 5) se hizo mal (un try/catch con hop ad-hoc) y fue
suprimido. El re-acople correcto está especificado en
compute-integration.md: un Compute Gateway compartido que las 3
superficies llaman antes de su shim, resolviendo por el contrato REST del Punto 2, con el
arnés de equivalencia (shadow/diff) como gate de rollout.
Dependencias. Requiere (1), (2) y el arnés de equivalencia. Es un puerto más dentro de (3).
Hecho cuando. El SQL Editor (y luego Dashboards) sirve queries Iceberg-nativas por Karma a
través de la puerta, con equivalencia validada y fallback; el shim dialect.ts se retira por
dataset.
Punto 5 · Cómputo plural, no un motor para todo
Qué. La regla transversal: muchos motores sobre el Warehouse, una gobernanza. Karma
para SQL analítico; ml-runner para ML/feature; el read-client para lecturas estructuradas;
futuros motores (streaming, grafo/Kuzu, search) sobre el mismo Warehouse.
Por qué. Los workloads son irreconciliables en un motor (OLAP-scan ≠ point-lookup ≠ ML ≠ streaming ≠ grafo). "One size does not fit all"; forzar un motor único recrea el monolito y reintroduce lock-in. La gobernanza los unifica en el control-plane; la ejecución permanece plural — es el consenso de los cinco grandes.
En Carbon. Ya es plural de facto: la facade enruta intents distintos a cómputos distintos
(read/stream/aggregate → read-client; write → iceberg-native-write; ML → ml-runner;
grafo → Kuzu). El Punto 5 = mantener esa pluralidad como principio explícito — cada motor
nuevo se enchufa detrás de la puerta y resuelve por el contrato REST, gobernado por el
control-plane, sin convertirse en un segundo punto único.
Dependencias. Es la invariante que gobierna (4) y cualquier motor futuro; se apoya en (1) y (2).
Hecho cuando. Añadir un motor nuevo (o sustituir Karma) es un cambio detrás de la puerta, sin tocar el contrato ni la gobernanza — el test de que la cintura, y no el motor, es el eje.
Secuencia recomendada
- Punto 1 (la unidad + el contrato) — el entregable inmediato; sin él nada converge.
- Punto 2 (Iceberg REST) — en paralelo temprano; define el material del contrato.
- Punto 3 (migración de puertos) — el grueso continuo, puerto por puerto, canary.
- Punto 4 (LIVE-SQL + Karma) — cuando (1)+(2)+arnés estén listos; es un puerto de (3).
- Punto 5 (cómputo plural) — principio permanente que se aplica desde el primer motor.