feat(brahman-cards): brazo unificado V1 — readers JSON + estructura canónica
Pivote arquitectónico: Brahman maneja varios formatos legítimos de
"Card" (cada uno en su crate origen, shape preservado), y un único
brazo los lee y proyecta a UNA estructura interna canónica que
consumen UI runtime / storage / DHT / wire. Agregar formato nuevo
= agregar reader, sin tocar consumers.
Crate nuevo `crates/core/brahman-cards/`:
- Card { id, schema_version, lineage, label, extensions, body }:
wrapper común con identidad legible. PartialEq omitido porque
MonadManifest y nakui_ui_schema::Module no lo implementan.
- CardBody enum tagged: Ente(brahman_card::Card), Monad(MonadManifest),
UiModule(nakui_ui_schema::Module). Convención: agregar variant +
reader; consumers hacen `match { Ente(..) => ..., _ => skip }`.
- trait CardReader { name, can_read(&Value), read(Value) }.
- 3 readers: EnteJsonReader (payload+supervision), MonadJsonReader
(members+cardinality), UiModuleJsonReader (entities+views+menu).
- Entry points load_card / load_card_with. Errores tipados.
13 tests integration: detection x3, dispatch+projection x3,
negative cases x2, sanity de orden, e2e desde disco, unsupported
extension, custom reader set, documented invariant.
13/13 verdes. Workspace build verde.
V1 NO hace (explícito): Nickel reader, templates, migración de
consumers, yahweh refactor, KCL→Nickel — todos en commits siguientes
para mantener este aislado.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,93 @@ ratio/diff ver `git show <sha>`.
|
||||
|
||||
## 2026-05-09
|
||||
|
||||
### feat(brahman-cards): brazo unificado V1 — readers JSON + estructura canónica
|
||||
**Pivote arquitectónico** decidido en charla: Brahman maneja varios
|
||||
formatos legítimos de "Card" (cada formato vive en su crate origen y
|
||||
conserva su shape público), y un **único brazo** los lee, completa
|
||||
desde templates si vienen simplificados, y los proyecta a UNA sola
|
||||
estructura interna canónica que consumen UI runtime / storage / DHT /
|
||||
wire. Agregar un formato nuevo = agregar un reader, sin tocar
|
||||
consumers.
|
||||
|
||||
**V1 en este commit**: estructura canónica + readers para los 3
|
||||
formatos JSON existentes en el monorepo. Sin Nickel todavía (aislado
|
||||
para próximo commit).
|
||||
|
||||
Crate nuevo `crates/core/brahman-cards/`:
|
||||
- **`Card { id, schema_version, lineage, label, extensions, body }`**:
|
||||
wrapper común con identidad legible + extensiones forward-compat.
|
||||
`id` como String (no `Ulid`) porque cada body variant usa un tipo
|
||||
de id distinto (Ulid para Ente/Monad, slug human-friendly para
|
||||
UiModule). PartialEq omitido del derive porque `MonadManifest` y
|
||||
`nakui_ui_schema::Module` no lo implementan en sus crates origen.
|
||||
- **`CardBody`** enum etiquetado `kind`:
|
||||
- `Ente(brahman_card::Card)` — entidad runtime con
|
||||
payload/soma/supervision.
|
||||
- `Monad(nouser_card::MonadManifest)` — agrupación semántica de
|
||||
archivos.
|
||||
- `UiModule(nakui_ui_schema::Module)` — descriptor de UI con
|
||||
entities/views/menu.
|
||||
- Convención: agregar variant nuevo + reader; los consumers que
|
||||
sólo manejen algunos hacen `match { Ente(..) => ..., _ => skip }`.
|
||||
- **`trait CardReader`**: `name()` + `can_read(&Value) -> bool` +
|
||||
`read(Value) -> Result<Card>`. El dispatcher prueba en orden y
|
||||
delega al primero que matchee.
|
||||
- **3 readers concretos** (en `readers.rs`):
|
||||
- `EnteJsonReader` — heurística: `payload` Y `supervision`
|
||||
presentes simultáneamente.
|
||||
- `MonadJsonReader` — heurística: `members` Y `cardinality`.
|
||||
- `UiModuleJsonReader` — heurística: `entities` Y `views` Y
|
||||
`menu`. El más específico, va primero en `default_readers()`.
|
||||
- **Entry points**:
|
||||
- `load_card(path)` — abre archivo, dispatcha por extensión, dentro
|
||||
de JSON prueba los readers default.
|
||||
- `load_card_with(path, readers)` — variante con set custom para
|
||||
apps que quieren restringir formatos.
|
||||
- **Errores tipados** vía `CardLoadError`: `Io`, `JsonParse`,
|
||||
`NoMatchingReader`, `ReaderFailed { reader, message }`,
|
||||
`UnsupportedExtension { ext, supported }`.
|
||||
|
||||
13 tests integration:
|
||||
- 3 detection tests (cada reader matchea sólo su shape, rechaza los
|
||||
otros 2 + non-object).
|
||||
- 3 dispatch+projection tests (cada formato JSON cargado produce el
|
||||
variant esperado con campos del wrapper bien derivados).
|
||||
- 2 negative cases (NoMatchingReader, non-object input).
|
||||
- 1 sanity de orden (UiModule gana cuando el shape acepta múltiples
|
||||
readers — defiende el contrato de orden documentado).
|
||||
- 1 e2e desde disco con `load_card_with`.
|
||||
- 1 unsupported extension.
|
||||
- 1 custom reader set (restringir a sólo Ente).
|
||||
- 1 documented invariant (extensions vacío en V1; si cambia, este
|
||||
test se rompe como signal).
|
||||
|
||||
13/13 verdes. Workspace build verde tras agregar el crate al
|
||||
`members[]` del workspace Cargo.toml.
|
||||
|
||||
**Lo que NO hace V1** (explícito):
|
||||
- No carga Nickel — próximo commit. La dep `nickel-lang-core` queda
|
||||
aislada para no inflar este commit.
|
||||
- No define templates — los templates Nickel se diseñan junto al
|
||||
reader Nickel (necesitan `merge` nativo de Nickel para fusionar
|
||||
override + base).
|
||||
- No migra consumers. `nakui-ui` sigue cargando `module.json` con
|
||||
`nakui_ui_schema::load_modules_from_dir` directo. La migración a
|
||||
`brahman_cards::load_card` viene cuando V1 + Nickel + templates
|
||||
estén estables.
|
||||
- No mueve los `extensions` del input a `Card.extensions` — los crates
|
||||
origen ya tienen sus propios `extensions` internos (`#[serde(flatten)]`).
|
||||
Documentado como decisión consciente.
|
||||
|
||||
**Pendientes para próximos commits** (orden):
|
||||
1. Reader Nickel + template merge.
|
||||
2. Migrar consumers (`nakui-ui` consume `brahman_cards::load_card`).
|
||||
3. Yahweh refactor: lift del MetaUi runtime a `crates/modules/ui_engine/`
|
||||
(esperando hasta que el brazo + canónico estén estables).
|
||||
4. KCL → Nickel: kcl_wrapper reemplazado por evaluación de Nickel
|
||||
contracts; los 3 schemas .k de nakui modules pasan a .ncl.
|
||||
5. card.k eliminado (es REFERENCE ONLY documentado).
|
||||
|
||||
### feat(nakui-ui): validación cross-field del EntityRef (existence en store)
|
||||
Cierra otro pendiente. Hasta ahora `parse_field_value(EntityRef, raw)`
|
||||
sólo validaba **forma** (UUID parseable + trim de whitespace) — un
|
||||
|
||||
Reference in New Issue
Block a user