18c0344a524cb188176cbc410c5bd24b7df83b50
10 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7fb2ad3b1e |
fix(nakui-core): schema_bundle_hash usa bytes reales del schema, no del bundle
Iter 17. Regresión surfaceada por verify_log_rejects_seed_after_schema_kcl_changes. Bug: compute_schema_bundle_hash operaba sobre los bytes del bundle compilado, que es `(import "/abs/path") & ...`. Esos bytes no cambian cuando se edita el archivo apuntado; sólo cambian si se mueve el módulo o se agregan/quitan schemas. El hash quedaba pegado y un seed firmado con schema vN se verificaba ok contra schema vN+1. Fix: nueva fn read_schema_files_concat que lee cada schema declarado y los concatena con framing `\0NCL:<name>\0`. Esos bytes alimentan los dos hashers (schema_bundle_hash y morphism_schema_hash). El bundle compilado sigue siendo imports-style (Nickel necesita los paths para resolver), sólo la fuente del hash cambia. Impacto: logs versados con el binario anterior fallan SchemaMismatch al verificarse — comportamiento correcto (re-seed). Test renombrado: verify_log_rejects_seed_after_schema_kcl_changes → _after_schema_changes (residuo de la migración KCL→Nickel). 10/10 schema_versioning verde. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
b05de24c24 |
refactor(nakui-core): KCL → Nickel — kcl_wrapper reemplazado por evaluación in-process
Cierra el plan original. El motor de validación de entities deja de shellear el binario externo `kcl` y pasa a evaluar Nickel contracts in-process via la dep nickel-lang (la misma que usa el brazo de cards). Los 3 schemas de sales/inventory/treasury migran de .k a .ncl. nakui-core: - Nueva dep nickel-lang = "2.0.0". - Borrado kcl_wrapper.rs. - Nuevo nickel_validator.rs con vet(schema_path, state, entity) que evalúa `let bundle = (import "<schema>") in (std.deserialize 'Json m%%"<json>"%%) | bundle.<entity>`. - executor.rs: KclError → NickelError, KclPre/Post/PostCreate → SchemaPre/Post/PostCreate, kcl_check → validate_entity. build_schema_bundle ahora emite `(import "X") & (import "Y") & ...` en lugar de concatenar bytes (cada .ncl es expresión completa). - manifest.rs: default schema "schema.ncl", extract_schema_names reescrito para sintaxis Nickel record (CapitalCase keys con 2-space indent). Schemas migrados: - sales/schema.ncl: Venta con std.contract.Sequence [record, from_predicate] para combinar shape + invariante cross-field (total == cantidad * precio_unitario). El patrón directo `record | from_predicate` rebota con "missing definition" porque el predicate evalúa antes de que el value populate el record; documentado en cada schema. - inventory/schema.ncl, treasury/schema.ncl: idem. - 3 schema.k viejos borrados; sales/nsmc.json paths actualizados. Tests: refs Kcl* renombradas; paths .k → .ncl; tests inline que escribían schema.k cambian a schema.ncl con sintaxis Nickel. 84 tests verdes en nakui-core. Doc-only borrados: - crates/core/ente-card/schema/card.k (REFERENCE ONLY). - crates/core/ente-brain/schema/rule.k (REFERENCE ONLY). Beneficios: sin dep externa al binario `kcl` (build CI limpio), errores Nickel en línea con caret pointing al field, mismo motor que cards (una dep para todo el repo), sin tempfile JSON intermedio. Cierra el plan original yahweh + KCL + card.k. Pendientes salen de nuevo trabajo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f5987d9cfc |
refactor(yahweh): Fase 1 — nakui-ui-schema → yahweh-meta-schema
Primer paso del refactor yahweh. El schema de UI declarativa no tiene acoplamiento real con Nakui (sólo dep en serde/thiserror) — movemos a yahweh para que cualquier app metadata-driven lo use sin pasar por nakui. Mecánico: - git mv crates/modules/nakui/ui-schema → crates/modules/ui_engine/libs/meta-schema. - Crate name: nakui-ui-schema → yahweh-meta-schema. - Workspace members[] actualizado (sección yahweh, no nakui). - Consumers actualizados: brahman-cards (Cargo.toml + lib.rs + readers.rs), nakui-ui (Cargo.toml + main.rs). - Self-test (example_modules.rs): import + path rebase (5 niveles arriba ahora). Documental: - Doc del crate ahora dice "metainterfaz (yahweh meta-schema)" + "backend-agnostic" en filosofía. - Module.nakui_module_dir documentado como "path opaco al backend"; se conserva el nombre por compat con módulos ya escritos + serde alias "backend_module_dir" para futuro rename suave. Tests: 13 yahweh-meta-schema + 26 brahman-cards + 48 nakui-ui verdes. Workspace build verde. NO hace Fase 1: mover widgets a yahweh (Fase 2), trait MetaBackend (Fase 3), renombrar nakui_module_dir. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f0c0a71860 |
feat(nakui-core,nakui-ui): FieldOp::Clear — borrar values vía form vacío
Nueva variante semántica del kernel: Clear { path } remueve la key
del record, distinta de Set { value: Null } (que deja la key con
valor literal null). Habilita "limpiar" un field optional vaciando
el input en la UI.
nakui-core:
- delta::FieldOp::Clear + simulate_on + capability_token (mismo
shape que Set: "entity.field").
- MemoryStore::apply_dry_run y apply: Set/Clear comparten
pre-condition (record existe + es objeto). Clear de field
ausente = no-op silencioso.
- SurrealStore: equivalente con `UPDATE ... UNSET <field>`.
- Executor capability check: Set/Clear comparten match.
- Conservation rules NO consideran Clear (sólo Set) — documentado
como morphism-author responsibility.
nakui-ui:
- commit_seed acumula `to_clear: Vec<String>` con optional empties
en lugar de `continue` silencioso.
- EDIT branch: nuevo helper compute_clear_fields filtra a sólo los
fields con current value non-null. Combina Set + Clear ops.
NoChange ahora requiere ambos vacíos. Log entry incluye
`cleared: [...]` sólo si non-empty. Updated.changed cuenta
sets+clears.
Tests: +7 en nakui-core (4 store + 3 delta), +3 en nakui-ui.
Suites: 34/34 nakui-core, 40/40 nakui-ui. Workspace build verde.
E2E del morphism real intacto.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
fc72726666 |
feat(nakui-ui): FieldKind::EntityRef — selector clickable de records existentes
Cierra el principal trade-off documentado del commit anterior:
"Inputs UUID a mano (no dropdown)". Los formularios pueden declarar
un campo entity_ref que apunta a una entity y el runtime renderea
una lista clickable de records existentes; click selecciona, el UUID
queda guardado para el submit.
Schema:
- Nueva variante FieldKind::EntityRef (serializa como "entity_ref").
- FieldSpec.ref_entity: Option<String> nuevo. validate() chequea que
cualquier field con kind=entity_ref tenga ref_entity set.
- Nuevo SchemaError::EntityRefMissingTarget.
Runtime:
- render_entity_ref_selector helper: lista clickable debajo del input,
cada item con etiqueta humana (heuristica: name > label > title >
sku > sku_id > UUID corto) y click handler via cx.listener que
setea el TextInput con el UUID completo. Highlight en accent color
para el seleccionado.
- parse_field_value(EntityRef) devuelve string raw — validacion como
Uuid es responsabilidad de commit_morphism downstream.
- Mensaje "(sin {entity}: crea uno antes...)" cuando lista vacia —
el user sabe que hacer.
Demo actualizado sales_engine: vender_form.stock_id_input y
caja_id_input cambian a kind=entity_ref. Flujo nuevo: click en Stock
listado bajo input, click en Caja, escribir venta_id/cantidad/precio/
timestamp, submit. Sin copiar UUIDs.
Tests: 2 nuevos schema (validate detecta EntityRef sin ref_entity y
acepta con ref_entity) + 4 nuevos runtime (parse, human_label cubre
todos los key fallbacks). 29 tests totales (16 + 8 + 5).
Pendientes: confirmacion de delete, snapshot/compaction del log,
edit delta-only, validacion estricta de params del morphism via
FieldKind del FieldSpec en lugar de infer_param_value.
|
||
|
|
932e7464d7 |
feat(nakui-ui): Action::Morphism wired al pipeline real (compute -> log -> apply)
Cierra el ultimo gran TODO de la metainterfaz Nakui: las acciones
Action::Morphism ya no son un toast informativo; despachan al
Executor cargado del manifest nakui-core (nsmc.json + schemas KCL +
scripts Rhai), pasando por el pipeline completo: compute (con
dry-run + KCL post-checks) -> log append -> store apply.
Schema nakui-ui-schema extendido:
- Module.nakui_module_dir: Option<String> nuevo. Path al modulo
nakui-core. Sin esto, Action::Morphism quedan no-op con toast.
SeedEntity sigue funcionando (alta administrativa sin manifest).
- Action::Morphism gano dos campos opcionales:
- inputs: BTreeMap<String, String> — mapeo role -> field_name.
- params: Vec<String> — fields cuyos values van al params JSON.
Si vacio, todos los fields no-input van a params.
Runtime nakui-ui:
- MetaUi.executors: BTreeMap<String, Arc<Executor>> nuevo. Carga
Executor::load_module(nakui_module_dir) en MetaUi::new.
- commit_morphism: resuelve inputs (parsea UUIDs), arma params
(Value object con tipos inferidos), llama
execute_and_log_with_recovery. Toast con count de ops o error.
- infer_param_value: heuristica i64 -> f64 -> bool -> string.
Tests: 2 nuevos. E2E morphism_pipeline_executes_real_sales_vender
carga el modulo real crates/modules/nakui/modules/sales, ejecuta
"vender" con inputs Stock+Caja y params (cantidad=5, precio=200,
venta_id, timestamp). Asserta:
- el morphism produce ops (no vacio).
- stock.cantidad: 100 -> 95.
- caja.saldo: 1_000_000 -> 1_001_000.
12 tests verdes en nakui-ui (+1). Schema extension no rompio nada
(6 unit + 5 integration siguen verdes).
Demo nuevo: examples/nakui-modules/sales_engine/module.json apunta
al sales real via nakui_module_dir. 6 vistas (list+form para Stock/
Caja/Venta + "Vender" con Action::Morphism). El user crea Stocks +
Cajas con seed_entity, copia los UUIDs a los inputs de "Vender", y
ejecuta el morphism real con KCL post-checks.
Activacion:
NAKUI_EVENT_LOG=~/.nakui/state.jsonl \\
NAKUI_MODULES_DIR=examples/nakui-modules \\
cargo run -p nakui-ui
Trade-offs:
- Inputs UUID a mano (no dropdown). Nice-to-have: FieldKind::EntityRef
que renderee selector.
- Inferencia de tipo en params es heuristica.
|
||
|
|
06c4fb9130 |
feat(nakui): metainterfaz declarativa + 6 modulos ERP estandar
Salto cualitativo: Nakui pasa de "library + demos + read-only viewer
del event log" a plataforma ERP con UI dirigida por datos. Cada
modulo de negocio se declara como un module.json (sin codigo Rust
nuevo) y el runtime GPUI lo carga dinamicamente: sidebar de menus,
listas con columnas configurables, formularios de alta.
3 entregables:
1. Crate nakui-ui-schema (datos puros): Module, View::List/Form,
FieldSpec con FieldKind {Text|Multiline|Number|Boolean|Date},
Action {OpenView|SeedEntity|Morphism}. Module::from_path,
Module::validate, load_modules_from_dir(dir). 6 tests unit + 4
integration.
2. Crate nakui-ui (binario GPUI): carga modulos desde
NAKUI_MODULES_DIR. Sidebar + main panel. List view con tabla
weighted; form view con campos labeled + submit que ejecuta
SeedEntity contra MemoryStore in-process compartido. Toast +
error banner. 6 tests unit.
3. 6 modulos demo en examples/nakui-modules/:
- customers (nombre, email, telefono, credito, notas)
- products (SKU, nombre, categoria, precio, stock)
- suppliers (razon social, ID fiscal, contacto, terminos pago)
- inventory_movements (fecha, tipo, SKU, cantidad, costo, motivo)
- sales_orders (numero, cliente, fechas, estado, totales)
- invoices (numero, cliente, fechas, totales, pagado, moneda)
Filosofia: UI como datos. Persistencia universal (MemoryStore hoy,
SurrealStore manana, sin tocar module.json). Schema primero, semantica
despues.
Activacion:
NAKUI_MODULES_DIR=examples/nakui-modules cargo run -p nakui-ui
Limitaciones conocidas (proximos iters):
- Inputs sin teclado (GPUI no lo trae nativo; integrar
yahweh-widget-text-input).
- Click handlers no propagan mutacion al estado (refactor con
cx.listener pendiente).
- Action::Morphism queda como TODO hasta cargar Manifest junto al
Module.
- Sin persistencia entre runs (wire con EventLog/SurrealStore para
cuando el daemon Nakui exista).
Tests: 16 totales nuevos. Lo que esto desbloquea: cualquiera puede
escribir un module.json para su dominio (pacientes, alumnos,
reservaciones) y aparece en la UI sin recompilar.
|
||
|
|
79d42aba28 |
chore(nakui): alinear nakui-core con [workspace.package] y deps compartidas
Cleanup de drift de convenciones: nakui-core era el unico crate del
monorepo que manteia version, edition y thiserror hardcoded, mientras
el resto heredaba del workspace y usaba thiserror v2. Eso significaba
que un bump global de version o edition se olvidaba sistematicamente
de nakui.
Cambios:
- [package]: version, edition, rust-version, license, authors, publish
-> todos *.workspace = true. Agregado description (convencion).
- Deps compartidas migradas a { workspace = true }: serde, serde_json,
thiserror (v1->v2), tokio, ulid, sha2.
- uuid migrado a { workspace = true, features = ["serde"] } — la feature
serde no esta en el workspace dep porque nakui es el unico user;
queda local opt-in en lugar de inflar el dep comun.
- Deps especificas de nakui (sin comparticion posible): rhai, petgraph,
surrealdb permanecen inline con version local.
Verificacion: cargo build -p nakui-core verde tras el bump thiserror
v1->v2 — los 14+ enums de error de nakui no requirieron ajustes
(derive backwards-compat para patrones simples). cargo test -p
nakui-core --lib: 27/27 verdes.
Bonus en este commit: discovery.rs movio el import Ulid a #[cfg(test)]
porque el refactor a Card::new lo dejo unused en module-scope.
|
||
|
|
70a7a0d46d |
feat: segundo módulo (nakui) + admin API + brahman-status
Dos cosas en una sesión, en el orden discutido:
(1) Segundo módulo brahman vivo: nakui-core
- crates/modules/nakui/core/Cargo.toml: deps brahman-card,
brahman-sidecar, ulid.
- crates/modules/nakui/core/src/bin/nakui.rs: brahman_card_for_nakui()
construye una Card como Lifecycle::Daemon, Supervision::Restart,
flow.input "command" (json) + flow.output "report" (json). El
cmd_run llama brahman_sidecar::spawn antes de levantar el server
de nakui.
(2) crates/shared/brahman-sidecar (estrena crates/shared/)
Boilerplate del sidecar extraído (DRY): el thread con tokio current
thread runtime, conexión vía Client::connect, ping loop. Yahweh y
nakui ahora consumen este crate. API:
- spawn(card) fire-and-forget
- spawn_with_handle(config) con JoinHandle
Example "presence" útil para demos: módulo dummy con label tomado
del primer arg que se queda vivo hasta SIGTERM.
(3) crates/core/brahman-admin: observabilidad del broker
Socket Unix paralelo en \$BRAHMAN_ADMIN_SOCKET (default
\$XDG_RUNTIME_DIR/brahman-admin.sock). Cada conexión recibe un
StatusSnapshot JSON line-delimited y se cierra. Compatible con nc/socat.
- StatusSnapshot { server, protocol, init_attached, sessions, matches }
- server::AdminServer
- client::query(path)
- example "brahman-status" CLI
(4) Wiring de ente-zero
En primordial_loop, junto al handshake server, ahora también levanta
AdminServer con misma política de degradación grácil.
(5) brahman-broker: BrokeredCard ahora incluye lifecycle. Endpoint y
Match derivan Serialize/Deserialize. Nuevo método cards() expone
iterador de BrokeredCard para que el admin pueda construir snapshots.
(6) brahman-card: re-export pub use ulid::* para que módulos no
necesiten depender de ulid directamente.
(7) yahweh-shell migrado al sidecar compartido. Su brahman_client.rs
pasa de 96 a 53 líneas: sólo declara la Card, delega el spawn.
Demo end-to-end:
$ ente-zero &
$ presence demo.producer &
$ presence demo.consumer &
$ brahman-status
Init: server=0.1.0 protocol=0.1.0 attached=true
Sessions (2):
01KR42TY1J... demo.producer lifecycle=Daemon priority=Normal
01KR42TY1K... demo.consumer lifecycle=Daemon priority=Normal
Matches (2):
demo.producer.in ← demo.consumer.out via Exact
demo.consumer.in ← demo.producer.out via Exact
El broker matchea bidireccional por tipo. El admin lo expone.
Tests: 27/27. cargo check --workspace: 0 errores.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
4d50bfc587 |
chore: absorbe nakui (ERP matemático) en modules/nakui
- crates/modules/nakui/core/: el crate nakui-core (4 bins, tests).
Deps directas (serde, rhai, surrealdb, petgraph, sha2, uuid, tokio,
thiserror v1) — no convertidas a workspace = true en esta pasada.
- crates/modules/nakui/modules/{inventory,sales,treasury}/: datos
declarativos del dominio (nsmc.json, schema.k, morphisms/) que el
crate consume — no son crates.
cargo check -p nakui-core: 0 errores.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|