Observa qué contenedores y vhosts existen (docker ps + sitios de
nginx) y reconstruye un Inventory "actual" que matilda-plan diferencia
contra el deseado: detecta correctamente qué crear y qué eliminar
(huérfanos). Parseo puro y testeable; sólo discover_local toca el
sistema. 6 tests.
La CLI gana el flag --discover en plan/script/apply: reconcilia
contra el estado real de la máquina en vez de partir de vacío.
matilda: 7 crates + CLI, ~48 tests. Pendiente: matilda-app (GPUI) y
la inspección detallada (docker inspect) para detectar drift de
configuración, no sólo presencia.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
matilda-ghost: el agente que ejecuta los ApplySteps en la máquina
destino — escribe archivos, corre comandos, reporta paso a paso;
semántica set -e (se detiene en el primer error). dry_run previsualiza
sin tocar nada. 5 tests.
matilda-linker: aplica los pasos en un host remoto por SSH sobre
brahman-ssh-multiplex; produce el mismo ApplyReport que el ghost local.
apps/matilda: deja de ser una demo hardcoded — ahora es una CLI real:
matilda example | plan | script | apply (local · --dry-run · --host)
Carga el inventario de un JSON, reconcilia y aplica.
matilda: 6 crates + CLI, ~42 tests. La cadena va de la declaración
a la aplicación local/remota.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Traduce un Plan de reconciliación a ApplySteps concretos: por cada
acción, los archivos a escribir en el servidor y los comandos a
correr. Contenedores → docker rm/run; vhosts → archivo nginx +
reload; hosts → sin pasos (son destino de conexión, no algo a
aplicar). steps_to_script() emite un script bash único con heredocs.
Sigue agnóstico de transporte — ejecutar los pasos (local, SSH o vía
matilda-ghost) es la capa de I/O. La demo CLI ahora imprime el script.
6 tests; matilda llega de la declaración al script ejecutable.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Detecta patrones de comandos repetidos en el historial: ventana
deslizante sobre las firmas de binarios (sólo ventanas 100%
exitosas), abstracción de argumentos variables (cd /a vs cd /b →
cd <…>), patrones maximales, puntaje por largo × frecuencia.
10 tests, agnóstico y determinista.
El shell lo corre tras cada comando terminado y promueve el patrón
más fuerte a un grupo «✨ ...» en el panel [RUN] — la rehidratación
que convierte la repetición orgánica en una receta de un clic.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
shuma-session: el shell trabaja dentro de una WorkSession — directorio
actual (que es el identificador de aislamiento, hash estable del cwd),
historial de comandos ejecutados (CommandRun con salida y estado) y
grupos de comandos guardados y reutilizables (CommandGroup).
shuma-exec: ejecutor con salida en streaming — lanza bash -c en un
directorio y entrega stdout/stderr línea a línea por un canal, sin
esperar al final. Es la capa que sandokan (poll-based, orquesta Cards)
deliberadamente no cubre.
15 tests. Agnósticos de UI, #![forbid(unsafe_code)].
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lee /proc/stat y /proc/meminfo; calcula uso de CPU (delta entre
muestras) y de memoria; mantiene un History circular para la curva
del monitor. Parseo puro (parse_cpu_stat/parse_meminfo) separado de
la lectura de archivos → testeable sin tocar el sistema.
8 tests. #![forbid(unsafe_code)], cero deps de UI.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Análisis de la línea de comandos bash, listo para GUI o TUI:
- lexer: tokeniza + clasifica (comando vs argumento por etapa),
reconoce comillas, variables, tuberías, redirecciones, operadores.
- pipeline: descompone la línea en etapas separadas por |.
- complete: autocompletado posicional (comando / flag / ruta) con
CompletionSource inyectable; diccionario de flags por comando.
- LineState: input editable UTF-8-safe (cursor, motions, completado).
- Dialect conmutable (bash hoy; zsh/fish/python a futuro).
32 tests. #![forbid(unsafe_code)], cero deps de UI.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Rect + split (reparto exacto de píxeles), 4 modos de layout
(MasterStack, Monocle, Grid, Columns) con tile(), y Workspace:
ventanas en orden de teselado, foco cíclico, reordenado y
resolución de geometría. Determinista, agnóstico de Wayland/smithay.
22 tests. #![forbid(unsafe_code)].
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cimiento numérico del transpilador. Picture parsea la cláusula
PICTURE (9, V, S, 9(n)); Decimal es punto fijo exacto (mantissa i128
+ scale) con suma/resta/producto exactos, división con escala de
resultado fija, redondeo Truncate/HalfUp y coerce a un Picture con
detección de desbordamiento (ON SIZE ERROR).
22 tests. Determinista, sin deps de plataforma — base de Fase D.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
yachay-core: notebook como secuencia de celdas (orden de lectura) +
DAG de dependencias (orden de ejecución). Celdas markdown/código/embed
con content_hash BLAKE3; editar una propaga staleness a descendientes;
digest Merkle por celda (content_hash ‖ digests upstream) y
notebook_digest que certifica reproducibilidad. Demo CLI en apps/yachay.
14 tests. Sin kernel ni UI, #![forbid(unsafe_code)].
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
matilda-core: modelo declarativo (Host, Container, VHost, Inventory).
matilda-config: renderiza Container→docker-compose/docker run y
VHost→bloque server nginx (con TLS + redirección :80→:443).
matilda-plan: reconciliación pura actual→deseado con acciones
ordenadas por dependencia (contenedores antes que vhosts, removes
en orden inverso). Demo CLI en apps/matilda.
29 tests. Funciones puras, cero Docker/SSH/disco.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pitch MIDI (clase/octava/frecuencia ET A4=440), Scale (raíz + patrón
de semitonos: mayor, menor natural, pentatónica), Chord (7 cualidades,
voicing, nombres) y un Score multipista con tempo: ScoreNote en
pulsos, Track con inserción ordenada y transposición atómica.
24 tests. Agnóstico de síntesis y UI, #![forbid(unsafe_code)].
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CLI que siembra un cuaderno (cocina/jardín/oficina), imprime el grafo
de wiki-links (forward/backlinks, huérfanas, colgantes) y los
clústeres por gravedad semántica + vecinos + layout 2D.
cargo run -p badu.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
badu-core: modelo Note + NoteStore (etiquetas, búsqueda) + grafo de
wiki-links [[...]] derivado del cuerpo (forward/backlinks, huérfanas,
enlaces colgantes; resolución case-insensitive).
badu-gravity: SemanticField sobre vectores semánticos — afinidad
coseno, vecinos más cercanos, clústeres por umbral (union-find) y
layout 2D dirigido por fuerzas (notas afines se atraen, todas se
repelen; determinista, sin RNG).
29 tests. Cero red, #![forbid(unsafe_code)].
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CLI que recorre el caso canónico de extremo a extremo: Venezuela
atestigua la nacionalidad de Yumaira, otras identidades corroboran,
una firma manipulada se rechaza, y tres políticas negociadas dan
veredictos distintos sobre la misma evidencia. cargo run -p agorapura.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
agorapura-core: identidades fractales (persona/comunidad/alianza/
institución) sobre claves ed25519, Claims sujeto-predicado-valor y
Attestations firmadas y autoverificables (la prueba viaja con el
dato). agorapura-graph: TrustGraph guarda sólo atestaciones con firma
válida; corroboration() devuelve evidencia cruda y TrustPolicy —un
umbral negociado, no una verdad del sistema— la traduce a sí/no.
22 tests. Cero red, cero estado global, #![forbid(unsafe_code)].
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fana-editor-gpui: EdgesElement pinta los conectores de dependencia
como paths; editor_view compone bloques de átomo (divs absolutos
coloreados por coherencia) + osciloscopio del sidepane. RenderPlan
ahora lleva su LayoutConfig para que el backend sea autosuficiente.
app fana: ventana con un relato de ejemplo (rama principal + alterna),
botón «Mutar raíz» que dispara la onda de choque lógica
(propagate_mutation), «Re-validar todo», leyenda y estadísticas.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
build_plan(NarrativeGraph) → RenderPlan: AtomBlocks apilados por
profundidad topológica (una columna por rama), Edges de dependencia
(borde inferior → superior) y osciloscopio de coherencia en el
sidepane (tono + intensidad semántica normalizada). Determinista:
orden desempata por (profundidad, columna, id). 10 tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Daemon que carga un Provider una vez y lo sirve sobre socket Unix;
DaemonClient lo consume desde otro proceso implementando el trait
Provider (indistinguible de un backend local). Multi-instancia: un
daemon por modelo, cada uno en su socket. Frames postcard con
prefijo de largo. 8 tests (wire + integración real sobre socket).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
dominium-canvas-gpui: Element que pinta un RenderPlan como quads,
centrado en sus bounds (rgba→hsla, único crate que toca gpui).
app dominium: compone core→physics→iso→render-plan→canvas en una
ventana GPUI con bucle de simulación de fondo (~11 tps), panel de
estadísticas, controles play/pausa + re-sembrar, y re-siembra
automática al colapso poblacional.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
build_plan(World, IsoProjector, ZWeights, PlanConfig) → RenderPlan:
un quad por celda (color = mezcla pesada de las 5 capas, relieve =
Z compuesto) + un quad-marca por Lemming posado sobre el terreno.
Quads ordenados por profundidad de pintor (depth = x+y) + caja
envolvente para centrado. Cero deps gráficas. 10 tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Proyección calculada en CPU antes de emitir quads 2D (GPUI no maneja
matrices 3D ni mallas).
- ZWeights — pesos del Z compuesto, uno por capa; z_of() calcula el
relieve como Σ wᵢ·capaᵢ (los 5 sliders del panel).
- IsoProjector — matriz iso fija: x=(x-y)·cos30, y=(x+y)·sin30 − Z·zf.
cos/sin de 30° vía libm → proyección bit-exacta cross-platform.
- project() + shadow() (Lambert plano: la sombra cae en z=0 desplazada
por la dirección de luz, larga en proporción a la altura).
6 tests verdes (origen, eje del rombo, Z eleva, Z compuesto lineal,
determinismo, sombra de punto en el suelo). cargo check verde.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Layout agnóstico del grafo de intenciones del shell:
- layout(SessionGraph) → CanvasPlan: cada comando %cN es un NodeBox
ubicado en una columna por su profundidad de dependencia
(longest-path); cada ref %pN/%cN que consume genera una Edge hacia
el comando que la produjo. Nodos colapsados se dibujan retraídos.
- paint(plan, canvas) → render directo contra pineal-render: aristas
al fondo, cajas con borde coloreado por estado (ámbar/verde/rojo).
4 tests verdes (columnas por dependencia, aristas de buffer, comandos
independientes en col 0, paint emite draw calls). cargo check verde.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- diffuse — ecuación de fluidos discreta sobre los 3 campos dinámicos
(materia/psique/poder): cada celda intercambia con sus 4 vecinas +
entropía. Buffer de lectura separado (lee estado viejo). oro y
degradacion no difunden.
- tick — un paso completo: difusión → transiciones (agente exhausto se
fuerza a Pelear) → acciones de los agentes → envejecimiento + cosecha
(la energía del muerto vuelve como materia/fertilidad). run() corre N.
Determinista bit-exacto: aritmética f32 en orden fijo, sin HashMap ni
reducciones paralelas. Test `run_is_deterministic` verifica que mismo
input → mismo estado bit a bit.
7 tests verdes. cargo check --workspace verde. dominium ya CORRE
(core + physics = simulación funcional).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Desbloqueado por verbo. fana-semantic embebe los átomos y mide su
afinidad a un conjunto de conceptos.
- ConceptSet — embebe el texto de referencia de cada concepto como su
vector ancla (vía cualquier verbo Provider).
- SemanticScorer — embebe el contenido de un NarrativeAtom y llena
atom.semantic_vectors con la similitud coseno concepto→intensidad.
Limpia el scoring previo en cada pasada.
Agnóstico del backend (verbo_core::Provider). 3 tests verdes con
verbo-mock — incluye: texto idéntico al ancla puntúa coseno ≈ 1.
cargo check --workspace verde.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Backend sin modelo real: FNV-1a del texto siembra un LCG que genera el
vector. Mismo texto → mismo vector siempre; textos distintos → vectores
distintos. Dimensión configurable (default 384d, típica de modelos
ligeros).
Desbloquea desarrollar y testear los consumidores de verbo
(fana-semantic, badu, chasqui) sin descargar modelos ONNX ni pegarle a
Cohere. Los backends reales (cohere/bge/fastembed) son swaps de config.
4 tests verdes (determinismo, distinción, dimensión, batch).
cargo check --workspace verde.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Primer crate de verbo (provider de embeddings compartido; desbloquea
fana-semantic, badu y la búsqueda de chasqui).
- ModelId — identidad de modelo (nombre + dimensión). Vectores de
distinto ModelId no son comparables.
- EmbeddingVector — vector + su ModelId; new() valida la dimensión,
cosine() rechaza comparar modelos distintos (error tipado, no
sinsentido silencioso), norm() euclidiana.
- EmbedError — ModelMismatch / BadDimension / Backend.
- trait Provider — model_id + embed + embed_batch (default secuencial).
Lo cumplen los backends concretos (cohere / bge / fastembed).
5 tests verdes (cosine idéntico/ortogonal/cross-model/zero, validación
de dimensión). cargo check --workspace verde.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
shuma-intent: el corazón agnóstico del shell shuma.
- parse — Intention: una línea del prompt parseada en etapas separadas
por pipe. Ref (%cN comando / %pN buffer) + Stage (Exec | Inject).
Parsea el ejemplo de la spec: `ssh nodo 'cat data.json' | %p1 | sort`.
- graph — SessionGraph: el grafo de contexto de la sesión. record()
registra una intención (%cN), complete() le asigna buffer de salida
(%pN) + estado, resolve() resuelve referencias, dangling_refs()
valida una intención antes de ejecutar (la validación previa del
prompt), collapse_succeeded() retrae nodos OK (quietud visual).
Todo puro y serializable (sesiones exportables). El front-end GPUI
(zonas RUN/SENS + lienzo central) lo rehidrata; la ejecución la hace
sandokan. 8 tests verdes. cargo check --workspace verde.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- fana-core: NarrativeAtom + CoherenceState ahora Serialize/Deserialize
(serde con feature rc para el Arc<String>; uuid con feature serde).
- fana-graph: + atoms() iterator + from_atoms() constructor.
- fana-store: GraphStore sobre sled. put/get/remove_atom por Uuid,
serialización bincode. save_graph persiste átomo por átomo;
load_graph reconstruye el grafo (la adjacency se re-cablea desde las
dependencies de cada átomo).
7 tests verdes (roundtrip put/get/remove + save/load_graph preserva
estructura). cargo check --workspace verde.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Primer paso de fana (prioridad alta entre las apps Fase C).
- fana-core — NarrativeAtom: id + content_hash SHA-256 + content
Arc<String> (structural sharing: ramificar es O(1)) + semantic_vectors
+ dependencies + branch_id + CoherenceState (Valid/InConflict/
PendingEvaluation). Invariante hash↔content verificable; set_content
re-hashea y marca PendingEvaluation.
- fana-graph — NarrativeGraph: DAG de átomos + adjacency
dependencia→dependientes. propagate_mutation: BFS que marca
PendingEvaluation en cascada a todo descendiente (la "onda de choque
lógica" de la spec), agnóstico de UI — devuelve los ids afectados.
topological_order con detección de ciclo.
10 tests verdes. cargo check --workspace verde.
Pendiente fana: semantic (cliente verbo), store (sled), llm, render-plan,
editor-gpui.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Opción B: RemoteEngine orquesta en un host remoto tunelando el wire
del daemon sobre un canal SSH direct-streamlocal hacia el sandokan.sock
remoto. El protocolo es idéntico al de DaemonEngine (postcard
length-prefixed) — sólo cambia el transporte, así que read_frame/
write_frame se reusan tal cual.
- brahman-ssh-multiplex: + SshSession::forward_unix — abre un canal
direct-streamlocal y devuelve su ChannelStream (AsyncRead+AsyncWrite).
- sandokan-daemon: protocol ahora pub, exporta read_frame/write_frame.
- sandokan-remote: RemoteEngine { SshSession + remote_socket }.
connect() o with_session(); cada operación abre un canal nuevo
(multiplexado sobre la conexión maestra).
- sandokan umbrella re-exporta RemoteEngine.
Completa Fase B: sandokan tiene Local + Daemon + Remote + auto().
cargo check --workspace verde. RemoteEngine necesita un host remoto
con `sandokan daemon` para validación runtime (sin unit test).
Opción A (text-parse del CLI por compat) queda pendiente por decisión
del usuario.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Envuelve russh 0.54 con una API mínima: una SshSession mantiene el
Handle maestro; cada exec() concurrente abre su propio canal en
paralelo sobre la misma conexión TCP (SSH multiplexa canales por
diseño del protocolo).
- SshConfig (host/port/user/auth/keepalive) + SshAuth (Password | Key).
- SshSession::connect — config russh + keepalive + auth password o
clave privada en disco; verificación de host key TOFU por default.
- SshSession::exec — corre un comando en un canal nuevo, junta
stdout/stderr/exit_code.
- SshSession es Clone barato (comparte el Handle).
Base de sandokan RemoteEngine y del Linker SSH de matilda.
Compila contra russh 0.54. El test de conexión real requiere un
servidor SSH (fuera del unit test).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- index — CardIndex: índice en memoria con filtros (by_label
case-insensitive substring, by_kind, providing por Capability, by_id).
- registry — scan_dir: carga toda Card *.json de un directorio,
saltando ruido y archivos rotos.
- discovery — CardDiscovery: une el índice local con la malla P2P;
announce_all publica las Cards locales al DHT, find_remote busca
proveedores. Modo local-only sin DHT también soportado.
Lo consumen el card-browser de nahual-shell y agorapura.
7 tests verdes. cargo check --workspace verde.
settings.local.json: defaultMode bypassPermissions (sesión desatendida).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
brahman-net corre un único Kademlia para todo el ecosistema.
brahman-dht le pone arriba un esquema de claves namespaced para que
distintos dominios coexistan sin colisión en la misma malla.
- key — RecordKind (Code/Card/Persona/Service/Custom) + DhtKey.
Wire: [kind_tag] ++ blake3(id) = 33 bytes longitud fija. Custom(n)
usa 0x80|n: nunca choca con los kinds estándar.
- Dht — wrapper sobre BrahmanNet: announce/withdraw/find (modelo de
provider records).
Consumidores: minga (Code), brahman-card-discovery (Card), agorapura
(Persona). 5 tests verdes (incl. smoke async sobre un nodo libp2p real).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
apps/sandokan (binario `sandokan`): CLI para probar el orquestador.
Subcomandos: daemon, run <exec> [args], list, status, telemetry, stop.
Fix: Intent serializaba Card directo, pero Card tiene un campo
`#[serde(flatten)] extensions` incompatible con postcard ("sequence
length must be known"). Intent::card ahora usa #[serde(with)] que
proyecta Card↔WireCard en el límite de serialización (las extensions
locales se descartan al cruzar el wire — comportamiento correcto).
Smoke test verificado end-to-end: daemon + run /bin/sleep + list +
status Running + telemetry + stop + status Killed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Crate sandokan (umbrella): re-exporta core/local/daemon y provee la
selección de transporte.
- auto(socket) — patrón "el primero que arranca gana": prueba si hay
un daemon escuchando; si lo hay devuelve DaemonEngine, si no
LocalEngine. Box<dyn Engine> (el trait es object-safe vía async_trait).
- auto_default() — auto() con default_socket_path().
- default_socket_path() — $XDG_RUNTIME_DIR/sandokan.sock o
/run/brahman/sandokan.sock.
3 tests: fallback a Local sin daemon, pick Daemon con serve() activo,
default path absoluto. cargo check --workspace verde.
sandokan ya es usable end-to-end en modo local y daemon. Falta
RemoteEngine (B1.4, depende de brahman-ssh-multiplex).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
DaemonEngine: implementación del trait Engine que delega a otro proceso
vía Unix socket. Materializa el patrón horizontal de sandokan (el
binario que arranca primero expone el engine; los demás se le suman).
- protocol.rs — DaemonRequest/DaemonResponse (espejan los métodos de
Engine) + framing postcard length-prefixed (u32 LE + bytes), con
MAX_FRAME 16 MiB defensivo.
- client.rs — DaemonEngine: stateless, un round-trip por llamada;
is_reachable() para el probe de auto().
- server.rs — serve(engine, socket): envuelve cualquier Engine, una
task por conexión, multi-request por conexión.
EngineError ahora es Serialize/Deserialize (viaja por el wire);
NotFound se propaga tipado a través del socket.
1 test de integración: roundtrip real DaemonEngine ↔ serve ↔ LocalEngine
(list vacío + NotFound propagado). cargo check --workspace verde.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Primer crate de la Fase B. Define SOLO el contrato del orquestador
sandokan (library horizontal embebible, no daemon supremo):
- Intent / ExecContext / IsolationLevel — qué orquestar
- ExecHandle — referencia a una entidad encarnada
- LifecycleEvent / TelemetryFrame — observabilidad (wire types)
- EngineError — taxonomía de fallas
- trait Engine — run/stop/list/status/telemetry (poll-based, sin
streams sobre trait objects, para que las 3 impls lo cumplan
uniformemente)
Las impls concretas (LocalEngine, DaemonEngine, RemoteEngine) vendrán
en crates separados (sandokan-local, sandokan-daemon, sandokan-remote).
3 tests verdes. cargo check --workspace verde.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Nuevo crate runtime/sandokan-lifecycle: lógica pura reutilizable por
cualquier supervisor de procesos (shuma, matilda Ghost, charka-shadow,
mirada). Sin syscalls, sin proceso, sin UI.
Módulos:
- backoff — Backoff exponencial con tope
- ttl — Ttl anclado a Instant
- quota — ResourceQuota + check_quota + Breach + QuotaAction
- restart — RestartPolicy + RestartTracker (conteo + backoff)
- state — LifecycleState (Pending/Running/Exited/Failed/Killed)
15 tests verdes. cargo check --workspace verde.
Variante segura de A4: se crea la library limpia sin tocar shuma-core
(módulo maduro). La migración de WorkspaceManager a consumir estas
primitivas queda registrada como A4.2 (refactor diferido, no urgente).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
DAG de dependencias limpio (modularidad horizontal):
- arje-brain-rules — rules + engine + dispatch (motor determinista)
- arje-brain-cognitive — observer + crystallize (estadística)
- arje-brain-audit — audit chain → CAS (accountability)
- arje-brain — umbrella de integración (introspect +
autopromote + metrics + loader)
Habilitador clave: TimedEvent movido de observer.rs a rules.rs
(engine lo necesitaba, era el único acoplo que rompía el DAG).
arje-brain re-exporta la API de los 3 sub-crates: arje-zero y chasqui
(consumidores) no requieren cambios. cargo check --workspace verde.
24 tests del brain pasan (4 rules + 6 cognitive + 5 audit + 9 umbrella).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cierra el requerimiento del módulo web. El cliente puede correr en
modo WASM (render local, scrubbing instantáneo, sin round-trip) o
caer al SSR (server compone el SVG) si el bundle WASM no está
desplegado. Switch automático sin configuración.
cosmobiologia-web (crate nuevo, cdylib + rlib):
- `lib.rs` con un único export wasm-bindgen
`render_model_to_svg(json, size, rot_offset_deg) -> String` que
deserializa un `RenderModel`, llama `compose_wheel` +
`draw_commands_to_svg` de cosmobiologia-render, y devuelve el
SVG inline listo para `wheel.innerHTML = svg`.
- Cargo.toml con `wasm-bindgen` + `getrandom` con feature
`wasm_js` solo bajo `target_arch = "wasm32"` (en nativo no se
arrastran).
- `.cargo/config.toml` con `--cfg getrandom_backend="wasm_js"`
para que la transitividad
`uuid → cosmobiologia-model → cosmobiologia-render` compile a
wasm32-unknown-unknown.
- `cargo check -p cosmobiologia-web` pasa en nativo (valida la
signature). Build WASM real lo dispara el usuario con
`wasm-pack build --target web --out-dir ../../../apps/
cosmobiologia-server/static/wasm` — comando documentado en
DEPLOY.md y en doc del crate.
cosmobiologia-server — soporte cliente WASM:
- Nuevo flag `--static-wasm <dir>` (default = static/wasm relativo
al cwd). Si el directorio existe, los archivos WASM se sirven
en `/static/wasm/*`. Si no existe, devuelve 404 y el cliente
cae al SSR.
- ServeDir de `tower-http` para fileserver simple.
index.html:
- Nueva función `tryLoadWasm()` que hace `import dinámico` del
módulo WASM al boot. Si carga OK, `wasm` global queda set; si
falla (archivo no existe o error de WASM), se loguea info y
sigue.
- `refreshSelected()` ahora hace fetch del RenderModel JSON
(`/api/sky` o `/api/charts/:id/render`); si hay WASM, llama
`wasm.render_model_to_svg(json)` localmente; si no hay WASM o
el render WASM falla, hace fetch del SVG SSR como fallback.
- Info row muestra "WASM" o "SSR" según el modo activo —
visualmente claro qué pipeline está corriendo.
cosmobiologia-server/DEPLOY.md (nuevo):
- Build del binario + build del WASM (con wasm-pack).
- systemd service template (sandboxing básico: ProtectSystem
strict, ProtectHome, PrivateTmp, NoNewPrivileges).
- Caddyfile y nginx para reverse proxy con TLS.
- DNS: A records para cosmobiologia.gioser.net + api.*.
- CORS: warnings sobre permissive vs producción multi-usuario.
- Separación demo público (DB vacía en VPS) vs desktop personal
(DB compartida en `~/.local/share/cosmobiologia/`).
- Backup con SQLite `.backup`.
- Smoke test post-deploy con curl.
- Tabla de referencia de TODOS los endpoints.
Tests: 10 verdes (cosmobiologia-render::math). El cliente WASM
no agrega tests propios — la lógica testeable vive en render.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Crate nuevo `cosmobiologia-server` (binario axum, nativo) que
monta `cosmobiologia-engine` + `cosmobiologia-store` y expone
la rueda + el CRUD del tree por HTTP.
Endpoints v1:
- `GET /api/health`
- `GET /api/tree` tree completo anidado
- `POST /api/groups` crear grupo
- `PATCH /api/groups/:id` renombrar
- `DELETE /api/groups/:id` borrar
- `POST /api/contacts` crear contacto
- `PATCH /api/contacts/:id` renombrar
- `DELETE /api/contacts/:id` borrar
- `POST /api/charts` crear carta
- `GET /api/charts/:id` chart JSON
- `PATCH /api/charts/:id` editar (label/birth/config)
- `DELETE /api/charts/:id` borrar
- `GET /api/charts/:id/render` RenderModel JSON
- `GET /api/charts/:id/svg` SVG inline (reusa
svg_export del engine)
- `GET /api/sky` "Cielo ahora" — RenderModel
UTC actual sin chart_id real
Query params del render para activar overlays sin POST:
- `offset_min=<i64>` time scrubbing
- `transit=1` overlay de tránsito al now
- `prog_age=<f64>` progresión secundaria
- `sa_age=<f64>` solar arc
- `pd_age=<f64>` primary directions (Naibod)
Decisiones:
- Single-user, sin auth. Bind por default a `127.0.0.1:8787` —
el server NO debe exponerse a la red pública en esta fase.
- DB por default = misma del desktop (`$XDG_DATA_HOME/cosmobiologia/
charts.db`). `--db` permite override.
- CORS permissive (es localhost, single-user, sin auth).
- `ApiError` con mapeo a HTTP status: 404 NotFound,
400 BadRequest, 500 todo lo demás. Body JSON `{ "error": "..." }`.
Smoke test:
cargo run -p cosmobiologia-server -- --port 18787
curl /api/health → {"status":"ok",...}
curl POST /api/groups → {"id":"01KRYVP...","name":"Familia",...}
curl POST /api/contacts → {"id":"01KRYVP...","group_id":...}
curl /api/tree → árbol anidado
curl /api/sky → RenderModel con VSOP real
Pendiente (fase 3): cliente `cosmobiologia-web` (cdylib WASM)
que consuma estos endpoints y pinte SVG/Canvas2D.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Rename clean del proyecto astrológico antes de empezar el módulo
web (fase 2 = server axum, fase 3 = cliente WASM). Hacerlo ahora
ahorra refactor de URLs, package.json, paths de assets HTML y
deploy configs que aparecerían con el nombre en cuanto exista el
server.
Mecánica:
- `git mv` de los 10 crates de módulo + 2 apps:
* `crates/modules/tahuantinsuyu/` → `cosmobiologia/`
* `crates/modules/tahuantinsuyu/tahuantinsuyu-*` →
`cosmobiologia/cosmobiologia-*`
* `crates/apps/tahuantinsuyu` y `tahuantinsuyu-cli` análogos.
- Sed sobre todos los `.rs` y `.toml`: `tahuantinsuyu` →
`cosmobiologia` (cubre crate names, deps paths, use
statements, ProjectDirs literals, binary names).
- Workspace `Cargo.toml`: members con paths nuevos.
- Memoria del proyecto (`~/.claude/.../memory/project_*.md`)
actualizada.
Cero leftovers: `grep -rn tahuantinsuyu --include="*.rs"
--include="*.toml" crates/` devuelve vacío.
DB & XDG: clean slate. La nueva app arranca con DB vacía en
`$XDG_DATA_HOME/cosmobiologia/charts.db`. Si tenías cartas
guardadas, viven todavía en `~/.local/share/tahuantinsuyu/` —
las podés migrar manualmente con un `cp`.
IDs UI inalterados: el prefijo `tts-` de gpui ElementIds queda
igual (cosmético, no afecta funcionalidad). Cambiarlo a `cb-`
ahora sería 3-4 líneas más de sed pero ningún beneficio
operativo.
Tests: 20 verdes (10 shell + 10 render math). Compila full:
`cargo check -p cosmobiologia` OK.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fase 1 de "módulo web": extracción del modelo y la matemática
agnóstica de surface a un crate separado, sin dependencia de
gpui ni de eternal. Es la base sobre la que el cliente WASM y
el canvas nativo van a converger.
Crate nuevo `tahuantinsuyu-render`:
- Tipos del RenderModel migrados desde `tahuantinsuyu-engine`:
`RenderModel`, `Layer`, `LayerKind`, `Geometry`, `LineSeg`,
`PointMark`, `Glyph`, `OverlayMeta`, `UranianGroup`,
`AspectSummary`, `OUTER_RING_MODULES`. El engine los
reexporta — ningún call site del shell/canvas/modules/tree/
panel cambia su `use`.
- Módulo `math` con la geometría canónica del wheel migrada
desde `tahuantinsuyu-canvas`:
* `Radii` con los aros A/B/C/D/E + helpers `body_ring` y
`aspect_endpoints`
* `polar_to_screen` (Asc a las 9 del reloj)
* `spread_angles` (anti-solapamiento con damping + clamp por
glyph)
* `find_clusters` (con wrap-around)
* `format_coord_compact` ("DD°MM'{signo}")
- 10 tests del math (5 spread + 4 coord + 1 polar) viajaron con
las implementaciones. El canvas se queda solo con los tests
de UI.
Por qué un crate aparte:
- `tahuantinsuyu-engine` arrastra `eternal-sky` (VSOP2013 +
I/O de tablas) que NO compila a WASM sin empaquetar 30+ MB
de efemérides. Los tipos del modelo son serde puro y sí
compilan a WASM — extraerlos libera al cliente web futuro
de la dependencia transitiva.
- Cuando llegue la fase 2 (`tahuantinsuyu-server` axum) y la
fase 3 (`tahuantinsuyu-web` cdylib WASM), ambos consumen
`tahuantinsuyu-render` con la misma fuente de verdad sobre
el layout, evitando duplicar la lógica entre desktop y web.
Pendiente: `tahuantinsuyu-model` arrastra `uuid → getrandom`
que falla a WASM sin `wasm_js` feature flag. Lo resuelvo en la
fase del cliente WASM (necesita su propio Cargo.toml con la
config getrandom + .cargo/config con RUSTFLAGS).
Tests: 20 verdes (10 shell + 10 render math). Compilación
nativa OK; canvas sin cambios visuales (mismo código,
diferente origen).
Cierra el círculo del Card: el flow.input "chart-request" y output
"chart-result" declarados desde fase 1 ahora tienen data plane real.
Otros módulos brahman (incluyendo el CLI nuevo) pueden conectar al
Unix socket de Tahuantinsuyu y pedir cómputos de cartas natales sin
abrir la GUI.
## Protocolo y server
Nuevo módulo `tahuantinsuyu_card::service` con:
- `ComputeRequest { Ping, Natal { birth, config, offset_minutes,
label } }` — postcard-serializable
- `ComputeResponse { Pong, Render { render }, Error { message } }`
- `serve(socket_path)` — async loop sobre tokio::UnixListener,
spawn por conexión, frame `u32 length` LE + postcard payload
(mismo molde que brahman-handshake). Cap defensivo a 1 MiB por
frame.
- `request(&socket, &req) -> Response` — cliente async one-shot
(abre, envía, recibe, cierra)
- `spawn_service_thread(path)` — thread dedicado con tokio
current_thread runtime; loggea warn si bind falla, la app sigue
standalone.
La Card ahora declara `service_socket: Some(default_service_socket())`
— el broker brahman puede revelar este path a consumidores que
matcheen el flow `chart-request`. Path canónico:
`$XDG_CACHE_HOME/tahuantinsuyu/service.sock` (con fallback a
/tmp).
## CLI nuevo
`crates/apps/tahuantinsuyu-cli` — binario standalone que usa el
helper cliente. Comandos:
- `tahuantinsuyu-cli ping` — health check
- `tahuantinsuyu-cli natal --year ... --month ... --day ... --hour
... --minute ... --tz-min ... --lat ... --lon ... [--alt ...]
[--label "..."] [--offset-minutes N]` — pide compute y emite
RenderModel como JSON pretty-print en stdout
Útil para:
- Smoke tests del data plane (CI puede levantar la app + ping)
- Scripts batch (computar 100 cartas y exportar JSON)
- Integraciones con otros tools del fractal brahman vía broker
## Cambios accesorios
- apps/tahuantinsuyu/main.rs: spawn_service_thread al boot junto al
sidecar. Loggea el path del socket a stderr para debug.
- Cargo workspace: agrega tahuantinsuyu-cli como member.
- tahuantinsuyu-card Cargo: agrega deps (engine, model, postcard,
tokio, tracing, directories, thiserror) para soportar el server.
Lo que falta para integración brahman 100%:
- Suscripción al broker como "consumer-aware" para detectar cuando
otros módulos publican `chart-request`s
- Publishing de eventos al broker cuando se crean/borran cartas
- Ambos requieren protocolo handshake bidireccional sobre el Init
socket (no service_socket) — fase posterior.
cargo check verde, 8 tests engine + 1 modules verdes. CLI compila;
prueba end-to-end (ping + natal) queda a manos del usuario que
levante la GUI.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Módulo nuevo `modules/tahuantinsuyu/` con 9 crates reusables + app
`apps/tahuantinsuyu` ejecutable que abre la ventana del explorador y
coordina los widgets:
- tahuantinsuyu-card: Card Brahman + spawn_sidecar (flows
chart-request/chart-result).
- tahuantinsuyu-model: tipos agnósticos (Group/Contact/Chart,
StoredBirthData, StoredChartConfig, ChartKind, TreeSelection).
- tahuantinsuyu-store: persistencia SQLite (rusqlite) con migración v1,
CRUD por entidad y descenso recursivo `charts_under_group`.
- tahuantinsuyu-engine: bridge agnóstico al canvas vía `RenderModel`
(Layer/Glyph/Geometry). Feature `eternal-bridge` (off por default)
reservada para enchufar eternal-astrology desde ~/eternal.
- tahuantinsuyu-modules: registry de módulos pluggables (Module trait
+ Control schema) con `NatalModule` placeholder.
- tahuantinsuyu-theme: AstroPalette (elementos / modos / planetas /
aspectos) con variantes dark + light sobre yahweh-theme.
- tahuantinsuyu-canvas: widget GPUI con CanvasState (Empty / Wheel /
Thumbnails). Render placeholder hasta cablear la rueda real.
- tahuantinsuyu-tree: explorador izquierdo sobre yahweh-widget-tree,
prefijos g:/c:/h: para Group/Contact/Chart.
- tahuantinsuyu-panel: control panel inferior que lee Controls de los
módulos del registry y los pinta.
- apps/tahuantinsuyu: binario `tahuantinsuyu` (launch_app-style) con
Shell coordinador (tree↔canvas↔panel), DB en $XDG_DATA_HOME.
Workspace Cargo.toml actualizado con los 10 miembros. `cargo check`
verde, tests unitarios verdes (model/store/engine/modules/theme/card).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>