Commit Graph

375 Commits

Author SHA1 Message Date
Sergio b17149c528 gioser-web: add docs/ dir with frontmatter for Qdrant indexing
- 4 md files (aire, fuego, tierra, agua) with YAML frontmatter
- caminos mapped: logos, nomos, kay, uku
- original md/ unchanged
- add scripts/index-gioser-docs.py (adapted from gioserv)
2026-05-23 14:36:20 +00:00
sergio c715ee2dee fix(init): la salida de arje-zero ahora se ve en VGA Y serial
Síntoma: el screenshot del usuario en la VPS Hetzner mostraba systemd
booteando y se quedaba congelado en el último printk del kernel justo
antes del switch-root. arje-zero arrancaba bien pero su salida iba al
serial invisible.

Causa: el cmdline traía `console=tty1 console=ttyS0,115200` — y el
kernel hace que `/dev/console` apunte al ÚLTIMO `console=`, así toda la
salida de stdout/stderr de arje-zero (tracing + banner de la rescue
shell) caía en ttyS0 (serial), no en la VGA que muestra noVNC.

Dos arreglos:

- Orden de consolas invertido en el menuentry → `/dev/console` = tty1
  (lo que efectivamente se ve en la consola web del proveedor).
- arje-zero también escribe a `/dev/kmsg` (ring buffer del kernel), que
  el kernel hace eco a TODAS las consolas registradas — el mecanismo
  que usa systemd para que sus mensajes salgan tanto en VGA como en
  serial. Defense in depth: el banner de rescue y un eco temprano
  «despierta como PID 1» aparecen sí o sí en cualquier consola.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 01:16:26 +00:00
sergio d1b700eb2b fix(init): el reboot-loop de Fedora — remount rw + /run tmpfs + shell de rescate
Diagnóstico: en el VPS Fedora arje-zero caía como PID 1 y el cmdline
traía `panic=10`, así que el kernel rebooteaba cada 10 s. Tres causas
encadenadas, todas arregladas:

1) **Cmdline `ro` + sin `/run` tmpfs.** El menuentry montaba `/` como
   sólo lectura (systemd lo remonta rw temprano; arje no). Sin eso, el
   socket del bus interno se intenta crear sobre un FS de sólo lectura
   y falla con EROFS → spawn_bus devuelve Err → PID 1 sale → kernel
   panic. arje-kernel ahora remonta `/` rw en el bootstrap y monta
   `/run`, `/tmp`, `/dev/pts`, `/dev/shm` como tmpfs — superficies
   escribibles aunque la raíz quede ro.

2) **PID 1 saliendo en cualquier `?`.** Doctrina dura nueva: PID 1
   NUNCA puede salir. Cualquier error de arranque ahora cae a una
   `emergency_shell()` que imprime el diagnóstico en `/dev/console`,
   abre `/bin/sh` y, si la shell muere, la reabre — así el operador
   puede reparar en vez de mirar la máquina reiniciarse en bucle.

3) **El script no conocía grub2 (Fedora).** `install-arje-as-init.sh`
   sólo probaba `update-grub` (Debian) y `grub-mkconfig` (Arch). Ahora
   detecta `grub2-mkconfig` y resuelve el `grub.cfg` correcto
   (UEFI/BIOS, fedora/redhat/centos/almalinux/rocky). El menuentry
   también pasa de `ro` a `rw` — el remount es belt-and-suspenders.
   Mismo arreglo en `uninstall-arje.sh`.

Renaser intacto: estos cambios son Linux-side puro (arje-kernel y
arje-zero usan nix/libc/tracing); renaser sólo comparte mirada-layout y
formato, ninguno tocado.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 23:02:45 +00:00
sergio a388ab14b7 fix(compat): auditoría de stubs — los métodos que mentían dejan de mentir
Repaso de los 11 shims restantes buscando métodos que devolvían éxito
sin hacer el trabajo (como los dos setters de localed). Resultado:

timedated — tres setters arreglados de verdad:
- SetTime: aplica el reloj con clock_settime(CLOCK_REALTIME) en vez de
  sólo loggear; si falla (sin CAP_SYS_TIME) devuelve error honesto.
- SetLocalRTC: escribe la tercera línea de /etc/adjtime (UTC|LOCAL),
  conservando las dos primeras.
- SetNTP: arje no gestiona un daemon NTP — en vez de fingir éxito,
  rechaza honestamente; `CanNTP` pasa a `false` para que GNOME deje el
  toggle deshabilitado y ni llegue a llamarlo.

systemd1 — StopUnit/RestartUnit/KillUnit dejaban creer que habían
detenido la unit; ahora devuelven NotSupported honesto (como StartUnit).

Lo demás del repaso ya era honesto: resolved/machined devuelven
NotSupported de frente; polkit/tmpfiles/notify/binfmt/journald no
mienten. timer-compat queda como hueco conocido y autodocumentado (sus
timers disparan pero el spawn es un no-op a la espera del bus).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 20:45:53 +00:00
sergio 19d04a2766 feat(compat): logind — Inhibit real (fd vivo) + ListInhibitors
`Inhibit` dejó de ser un stub que devolvía NotSupported. Ahora cumple
el contrato de systemd-logind:

- Crea un pipe; el cliente recibe el extremo de escritura, el shim
  conserva el de lectura. Mientras el cliente no cierre su fd, el
  inhibidor sigue activo; al cerrarlo —o morir— el shim ve EOF y una
  tarea guardiana lo retira de la tabla.
- Tabla de inhibidores activos + método `ListInhibitors`.
- Las propiedades `BlockInhibited` / `DelayInhibited` ahora reflejan
  los inhibidores reales (tokens únicos del modo, unidos por `:`),
  en vez de devolver siempre vacío.

Es lo que GNOME/KDE usan para frenar la suspensión automática durante
una presentación o una descarga. 1 test (`inhibited_what`).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 20:41:10 +00:00
sergio 8f946b449c feat(compat): arje-compat-common — núcleo puro y testeado para los shims
El hallazgo del repaso del monorepo: la capa compat (14 shims D-Bus de
systemd) era lo más incompleto relativo a su peso — load-bearing para
correr GNOME/KDE sobre arje, y con CERO tests. Cada shim copiaba su
propio `atomic_write`, su parseo `KEY=value` y sus validadores.

Primer golpe:

- `arje-compat-common`: crate nuevo con la lógica pura compartida
  (atomic_write, parse_kv, merge_kv, conf_entries, is_valid_hostname),
  cubierta con 8 tests. Antes esa lógica vivía duplicada y sin un test.
- `arje-hostnamed-compat` y `arje-localed-compat` migrados al núcleo —
  quedan más finos y su lógica pasa a estar cubierta.
- localed: los dos setters que eran stub (sólo loggeaban) ahora
  escriben de verdad — `SetVConsoleKeymap` → /etc/vconsole.conf,
  `SetX11Keyboard` → 00-keyboard.conf. + 2 tests propios.
- Bug corregido de paso: el parser xorg de localed devolvía el NOMBRE
  de la opción en vez del valor (tomaba la 1ª comilla); ahora toma la
  2ª cadena, la correcta.

Compat: de 0 a 10 tests. Quedan 12 shims con la misma migración
mecánica pendiente; el plato fuerte real es `Inhibit` en logind.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 20:25:58 +00:00
sergio c4d1dd7bc2 feat(cosmobiologia): corpus — capa de composición por evidencia vecina
La capa de composición, resuelta con honestidad. El producto numérico
de perfiles (Hadamard y parientes) se descarta: da falsos —una
dimensión en 0 nunca «se enciende»— y, sobre todo, un perfil compuesto
es una conjetura, no evidencia.

En su lugar, `Corpus::evidencia_relacionada`: para una combinación SIN
pasaje propio, junta la evidencia VECINA —pasajes que comparten un
componente (el planeta, el signo, la casa, el tipo de aspecto)—,
agrupada por lo que comparten. No sintetiza un texto; son citas reales
de contextos parecidos para que el astrólogo componga él.

En la rueda 2D, el panel de la tajada ahora muestra, bajo los pasajes
directos, una sección «Composición» con esa evidencia vecina por cada
combinación sin texto propio.

16 tests del corpus (2 nuevos) + 2 del engine verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 20:07:35 +00:00
sergio 2035e6dfa3 feat(cosmobiologia): corpus — las tajadas sobre la rueda
La interpretación por dominio, integrada al lienzo 2D. Tres botones
(Vital / Social / Psíquico, o tecla I para ciclar): al elegir una
tajada, la rueda resalta con un anillo los cuerpos de ese dominio y un
panel a la derecha lista los pasajes citados —combinación, texto,
fuente—, o avisa de los huecos sin texto.

- El canvas carga el corpus al arrancar: corpus.ron del directorio de
  datos del usuario, o la plantilla ejemplo.ron embebida como fallback.
- El JOIN corre con corpus_inputs (engine) + interpretar_por_dominio:
  cada longitud → signo, cada casa → su tajada, los aspectos puentean.
- El resalte es una capa transparente sobre la rueda, sin tocar el
  render del wheel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 19:54:23 +00:00
sergio ac787fb3b3 feat(cosmobiologia): corpus — puente carta → pasajes de interpretación
Primer paso para conectar el cosmobiologia-corpus a la app: el engine
gana `corpus_inputs(&RenderModel)`, que deriva de una carta sus
colocaciones (planeta·signo·casa) y sus aspectos en el shape que el
corpus consume. Cada longitud se traduce a su signo; la casa viene del
glyph. El caller hace luego `Corpus::interpretar_por_dominio`.

El engine reexporta los tipos del corpus (Corpus, Pasaje, Dominio,
Colocacion, AspectoEnCarta, CombinacionId) para que el shell/canvas los
usen sin importar el crate aparte.

2 tests del engine verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 19:44:54 +00:00
sergio 6e30dc2d72 feat(cosmobiologia): esfera 3D — switch de constelaciones + luz de la Vía Láctea
- Switch de constelaciones: botón flotante «● Constelaciones» (o tecla
  B) que las enciende y apaga en la esfera 3D.
- La luminosidad se reparte: el brillo especular fijo a la pantalla se
  bajó mucho (no giraba, se sentía despegado), y en su lugar la Vía
  Láctea aporta un resplandor difuso a lo largo del plano galáctico —
  que SÍ gira con la esfera. Más intenso hacia el centro galáctico
  (Sagitario, como en el cielo real) y atenuado bajo el horizonte
  local: la franja como se ve desde la Tierra esa noche.

42 tests verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 19:40:11 +00:00
sergio cfb37af0cf feat(cosmobiologia): Tierra interior — tinte mar/continente + día/noche
La Tierra interior ahora se lee como un planeta:

- Mar y continentes teñidos distinto: el mar es un disco azul, los
  continentes son polígonos rellenos de verde. Para eso se sumó la
  primitiva DrawCommand::Polygon (relleno + trazo) — agnóstica, con su
  traductor GPUI y su emisor SVG.
- Sombreado día/noche según el Sol de la carta: el hemisferio que mira
  al Sol se ilumina (resplandor concéntrico sobre el punto subsolar,
  que se apaga si el Sol queda detrás de la Tierra), el terminador
  marca la línea día/noche, y cada continente se tiñe verde claro u
  oscuro según esté de día o de noche. El observador se atenúa si
  naci­ó de noche.

42 tests verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 19:33:46 +00:00
sergio 267e54f974 feat(cosmobiologia): esfera 3D — figuras de las 88 constelaciones
Las constelaciones de un catálogo REAL, no inventadas de memoria:
d3-celestial (dominio público), 89 figuras / 743 segmentos, en
coordenadas ecuatoriales J2000. El dataset se convirtió a un módulo
Rust generado (`constellations_data.rs`) — datos en el repo, auditables.

Cada figura: sus polilíneas unen estrellas reales del catálogo (un
punto por vértice) y el nombre va en el centroide. Capa tenue, atenuada
por profundidad — referencia, no protagonista. Se convierten al marco
eclíptico con la misma rotación por oblicuidad que el resto.

42 tests verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 19:29:50 +00:00
sergio 3454b8ba1e feat(cosmobiologia): esfera 3D — Tierra interior con continentes + topocéntricos
Tierra interior: un globo pequeño y transparente en el centro de la
esfera celeste, con los continentes esquemáticos (referenciales, no un
mapa de precisión) y el observador marcado en su lugar real. Orientada
por la longitud geográfica y el RAMC, de modo que el punto del
observador mira exactamente al cénit — y gira con la vista, así que
delata la rotación que el sombreado fijo no daba.

Topocéntricos: la capa topocéntrica del motor se dibuja como disco
hueco con un conector hasta su par geocéntrico. El LARGO del conector
es la paralaje — honesto sobre su magnitud (un cinturón aparte la
exageraría: la diferencia es sub-grado salvo la Luna).

`RenderModel` gana `geo_longitude_deg` (lo puebla el bridge). 41 tests
verdes (3 nuevos: orientación de la Tierra, observador↔cénit,
continentes).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 19:23:47 +00:00
sergio e94023d8af feat(cosmobiologia): esfera 3D — estrellas fijas notables en su lugar real
La esfera ahora dibuja las 9 estrellas fijas del motor (Sirio, Régulo,
Antares, Spica, Aldebarán, Fomalhaut, Algol, Vega, Pólux) — disco
brillante con destello de cuatro rayos y su nombre.

La longitud eclíptica —la coordenada astrológicamente viva, que
precesiona— viene intacta del motor (`build_fixed_stars_overlay`). El
módulo nuevo solo le suma la **latitud eclíptica** (valor de catálogo,
~constante con la precesión) para situar cada estrella en su lugar
real de la esfera en vez de aplastada sobre la eclíptica: Sirio cae
bien al sur, Vega bien al norte, Régulo casi sobre la eclíptica.

Se ven al activar el módulo «Estrellas fijas» en el panel. 39 tests
verdes (3 nuevos: eclip_latlon, coherencia de latitudes, render).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 19:03:27 +00:00
sergio 93856cd4d7 feat(cosmobiologia): esfera 3D — la piel real del cielo: estrellas + Vía Láctea
La «piel» de una esfera celeste no son continentes —esos van en la
Tierra— sino las estrellas y la Vía Láctea. Y a diferencia del brillo
especular (fijo a la pantalla), esta piel gira CON la esfera, así que
delata la rotación de un vistazo.

- Campo de estrellas isótropo, decorativo (no un catálogo real),
  generado con un hash determinista — no titila entre frames.
- Vía Láctea: una sobredensidad de estrellas tenues a lo largo del
  plano galáctico, ubicado con el polo galáctico real (J2000, AR
  192.859° / Dec +27.128°).
- Estrellas con brillo y tinte variables (azuladas / cálidas),
  atenuadas por profundidad. Van detrás de la rejilla, delante del
  sombreado — un fondo de planetario. Solo en tema oscuro.

36 tests verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 18:58:34 +00:00
sergio 8fc26b0c0c feat(cosmobiologia): esfera 3D batch 2 — horizonte local, cénit y relieve
Sobre el batch 1 (eclíptica + ecuador + cuerpos):

- Horizonte local: círculo máximo perpendicular al cénit, derivado de
  la latitud geográfica y el RAMC. El cénit (declinación φ, AR RAMC,
  llevado al marco eclíptico) es el «punto del observador» — marcado
  como tal, con su nadir y el meridiano local.
- Día/noche: los cuerpos bajo el horizonte se atenúan — de un vistazo
  se ve qué planetas estaban sobre la tierra en el momento de la carta.
- Marcadores de polos: eclípticos (punto dorado) y celestes (anillo +
  cruz, etiquetados PN/PS) — el ángulo entre ambos ejes ES la
  oblicuidad, ahora visible.
- Relieve de la esfera: disco base + degradado radial + brillo
  especular desplazado a la luz — volumen sin gradientes nativos.
- RenderModel gana `geo_latitude_deg` (#[serde(default)]); el bridge
  lo puebla desde birth_data.

Verificación: 2 tests nuevos fijan la construcción del cénit — está a
la colatitud del polo celeste, y cénit/polo/MC son coplanares (el
plano del meridiano), lo que ancla el RAMC. 35 tests verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 18:49:02 +00:00
sergio 6a0781c0a8 feat(cosmobiologia): esfera celeste 3D — la carta como objeto rotable
GPUI no es 3D y empotrar wgpu sería frágil; la esfera celeste es de
alambre —círculos máximos y puntos— y eso se proyecta a software con
trigonometría pura. Cada superficie ya sabe dibujar DrawCommand, así
que el módulo nuevo solo decide dónde cae cada trazo: una esfera real,
rotable, sin una línea de GPU.

- cosmobiologia-render/sphere3d.rs: marco eclíptico (z=0), proyección
  ortográfica con yaw/pitch, eclíptica + ecuador celeste inclinado por
  la oblicuidad (se cruzan en los equinoccios, como en el cielo),
  rejilla de meridianos/paralelos, signos, ángulos y cuerpos natales.
  Algoritmo del pintor + atenuación del hemisferio lejano. 5 tests.
- compose_sphere emite Vec<DrawCommand> — lo consumen igual el canvas
  gpui y el SVG del cliente web.
- cosmobiologia-canvas: modo esfera 3D en el lienzo (tecla V o el botón
  flotante «Esfera 3D»), drag para orbitar, traductor DrawCommand→GPUI.

Falta (2da capa): el horizonte local + día/noche — necesita la latitud
geográfica, que aún no viaja en el RenderModel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 18:38:21 +00:00
sergio 43e6b32e15 feat(cosmobiologia-corpus): tomografía por dominio + plantilla y guía
El corpus ya rebana la carta en tajadas vivenciales: una sola
configuración mirada plano a plano, sin promediar la contradicción.

- Colocacion / AspectoEnCarta: la posición real de un planeta en una
  carta — el puente entre el motor astronómico y las claves del JOIN.
- combinaciones_de_carta: deriva todas las CombinacionId de una carta.
- rebanar_por_dominio: la tomografía — cada planeta@cN cae en el
  dominio de su casa, cada planeta·signo hereda el de su casa, y un
  aspecto puentea apareciendo en las dos tajadas que conecta.
- Corpus::interpretar_por_dominio: el JOIN agrupado por dominio,
  entrada directa del gráfico «por tajadas».
- CombinacionId acepta el alias ASCII '/' del punto medio '·'.
- ejemplo.ron: plantilla cargable y comentada del corpus.
- GUIA.md: los pasos exactos para generar el corpus a mano.

12 tests verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 18:18:16 +00:00
sergio 121aa130af feat(cosmobiologia): cosmobiologia-corpus — esquema del corpus de interpretación
El corpus es la biblioteca de interpretación indexada: fragmentos de
texto de los libros (y del astrólogo) etiquetados por la combinación
astrológica que describen. NO calcula nada — las reglas las computa el
motor; el corpus sólo guarda evidencia citable y la sirve por JOIN.

Esquema TIPADO (la astrología tiene gramática — planeta=función,
signo=estilo, casa=dominio, aspecto=relación; no son vectores
intercambiables de un espacio plano):

- CombinacionId — la «etiqueta de código de barras», con variantes por
  tipo de combinación; el aspecto normaliza el orden de sus extremos.
- Arquetipo / TipoArquetipo — los bloques con su PerfilSemantico
  (dimensiones con nombre que define el astrólogo, no el código).
- Pasaje — texto citado + fuente + combinación.
- Dominio — el plano vivencial (Vital/Social/Psíquico) por casa.
- Corpus::interpretar — el JOIN: combinaciones de la carta → pasajes.
  Cobertura total; la SÍNTESIS es de una capa superior.

6 tests verdes. La capa de composición (deducir combinaciones no
leídas) queda explícitamente sin construir — es un problema de diseño
abierto, no un producto Hadamard ingenuo.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 18:05:10 +00:00
sergio 36d6645e7f feat(cosmobiologia): rectificador per-segundo + direcciones primarias reales
El rectificador deja la aproximación y pasa a la trigonometría exacta,
con precisión de segundo — el "microajuste argentino".

LA MATEMÁTICA. El rectificador ya NO usa el modelo simplificado
(directed_longitude, rotación uniforme de RA + convergencia GR). Ahora
usa `eternal_astrology::primary_direction::all_directions` — el método
Placidus-mundano: semi-arcos diurnos/nocturnos bajo el polo de cada
cuerpo, la trigonometría esférica de la escuela ascensional. No se
reimplementó nada: la matemática, ya probada, vive en eternal; el
engine sólo aporta la capa de optimización.

- error_de_carta: por cada evento, la distancia en años a la dirección
  primaria que perfecciona más cerca; el error total es la suma. Es la
  función de coste del microajuste — el valle es la hora real.

PRECISIÓN DE SEGUNDO. compute_natal_chart / build_eternal_inputs /
natal_cache pasan a trabajar en SEGUNDOS (compose convierte ×60). El
rectificador barre en dos pasadas: gruesa minuto a minuto sobre la
ventana (el perfil que dibuja la curva), fina segundo a segundo en
±60 s alrededor del mejor minuto.

- Rectificacion: mejor_offset_segundos; el perfil va en segundos.
- UI: panel y curva muestran «±Xm Ys · error N.NNa». Las barras siguen
  siendo clicables (scrub a esa hora candidata).

Tests verdes (engine 12, render 28). Limitación conocida: all_directions
es sólo directo — converso necesita crecer en eternal (upstream).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 16:56:06 +00:00
sergio 5fdae159f0 feat(cosmobiologia): perfil del rectificador — barras clicables
Cada barra de la curva del barrido se vuelve clicable: un clic lleva
la carta a esa hora candidata, reusando el scrub de tiempo del
jog-dial (CanvasEvent::TimeOffsetChanged, ya cableado en el shell).

Cierra el lazo del rectificador: ahora se puede inspeccionar sobre el
wheel cualquier hora del barrido, no sólo leer la ganadora.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 16:44:26 +00:00
sergio 208dc15569 feat(cosmobiologia): rectificador automático — curva del perfil del barrido
Tercer y último incremento: la visualización. El rectificador ya
muestra POR QUÉ una hora gana, no sólo cuál.

- cosmobiologia-canvas: CanvasState gana `rectificacion` +
  `set_rectificacion`. render_rectify_profile dibuja el barrido como
  un histograma en el footer — cada barra es una hora candidata, su
  altura crece cuanto menor el puntaje; la barra más alta (el valle
  del puntaje) es la hora rectificada, resaltada. Etiqueta los hitos
  (mejor, 0, extremos).
- shell: run_rectificacion publica el Rectificacion al canvas además
  del resumen textual al panel.

Con esto el rectificador automático (#67) queda completo: motor de
escaneo GR + UI de entrada + visualización del perfil.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 16:42:59 +00:00
sergio a7e9662fad feat(cosmobiologia): rectificador automático — UI de entrada y disparo
Segundo incremento: el rectificador ya es usable de punta a punta
desde el panel, sin infraestructura de UI nueva.

- cosmobiologia-panel: Control::TextInput pasa a renderizarse desde
  string_state — deja de ser un display estático y se vuelve un campo
  de sólo-lectura que el shell escribe vía set_string (resultados,
  etiquetas).
- cosmobiologia-modules: el módulo primary_directions gana 3 sliders
  «Evento N · edad» (0 = ranura sin usar), un Action «Rectificar
  hora» y un TextInput «Resultado».
- shell: run_rectificacion lee las edades de los sliders, llama a
  engine::rectificar (ventana ±15 min, paso 1) y escribe la hora
  rectificada + el puntaje en el campo Resultado del panel.

El rectificador queda funcional: activar GR → fijar edades de eventos
→ «Rectificar hora» → leer el resultado. Falta sólo la curva del
perfil del barrido como visualización (incremento opcional).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 15:53:18 +00:00
sergio 0ada1050f7 feat(cosmobiologia): rectificador automático — escaneo GR (núcleo)
Primer incremento del rectificador automático (#67): dado un conjunto
de eventos conocidos de la vida del sujeto, barre las horas de
nacimiento candidatas y devuelve la que mejor los explica vía el
Sistema GR. La killer feature pro — desbloqueada al completar el GR.

- cosmobiologia-render: `convergencia_minima` — medida CONTINUA de qué
  tan bien una carta explica un evento (suma de orbes del directo +
  converso más cerrados sobre un punto natal). 3 tests.
- cosmobiologia-engine: módulo `rectify` — `rectificar` barre la
  ventana de horas candidatas; por candidata computa la carta (una
  vez, cacheada) y mide la convergencia GR a la edad de cada evento;
  elige el puntaje mínimo. Devuelve el perfil completo del barrido
  para que la UI lo dibuje como curva. Test end-to-end con eternal.
- bridge: `compute_natal_chart`/`body_symbol`/consts GR → pub(crate).

Falta: la UI (capturar eventos conocidos, lanzar el barrido, mostrar
la curva y la hora rectificada).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 15:49:43 +00:00
sergio cb0c5c22a8 feat(arje-wasm): bump wasmi 0.40 → 1.0 — unifica el runtime WASM con renaser
brahman y renaser ya corren la misma versión de wasmi (1.0): el ABI
WASM del host es idéntico en Linux y en bare-metal. Desbloquea el
Paso 3 de la integración (converger el ABI Card/WASM).

El delta de la API resultó pequeño:
- `Linker::instantiate` + `InstancePre::start` → `instantiate_and_start`
  (wasmi 1.0 fusiona instanciación y arranque).
- Motor configurado en `CompilationMode::Eager` — traducción completa
  del módulo por adelantado, comportamiento predecible, paridad con el
  motor wasmi del kernel de renaser.

Primer test de arje-wasm: `demo_corre_en_wasmi_1` ejecuta el módulo
demo de punta a punta (WAT→wasm, instanciación, host imports
log/exit). arje-zero (PID 1, consumidor) compila sin cambios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 14:52:47 +00:00
sergio 1c6aafbc24 feat(mirada): mirada-layout no_std — primer núcleo compartible con renaser
mirada-layout (el motor de teselado del compositor) pasa a `no_std +
alloc` para poder compilarse también en bare-metal — es el primer
crate-núcleo que brahman y renaser compartirán.

- `#![cfg_attr(not(test), no_std)]` + `extern crate alloc`: usa
  `alloc::{vec, collections::BTreeMap}` en vez de `std`.
- Matemática de punto flotante vía `libm` (`sqrt`/`ceil`/`round` viven
  en `std`, no en `core`).
- `serde` pasa a feature opcional: los consumidores Linux
  (mirada-protocol/brain) la activan; un consumidor bare-metal no
  necesita (de)serializar el layout.
- Deps declaradas directas (no `workspace = true`): un núcleo que
  cruzará fronteras de workspace se mantiene autocontenido.

Verificado: `cargo build --target x86_64-unknown-none` compila;
32 tests verdes; mirada-protocol/brain sin regresión.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 14:37:03 +00:00
sergio 5770759f2e docs(cosmobiologia): marca el dial uraniano como hecho en la SDD
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 14:03:42 +00:00
sergio 11d8bcb4af feat(cosmobiologia): dial uraniano de 90° — proyección geométrica
El módulo Uranian sólo listaba las fórmulas como texto; ahora también
las muestra geométricamente.

- cosmobiologia-canvas: render_uranian_dial pinta un eje horizontal
  0-90° con cada cuerpo natal proyectado en su longitud mod 90. Ticks
  en las divisiones duras (0/22½/45/67½/90°); los cuerpos que forman
  una fórmula uraniana van resaltados, y los clusters densos se
  escalonan en filas para legibilidad. La sección del footer combina
  el dial geométrico con la lista de pills de fórmulas.
- El dial aparece siempre que el módulo Uranian está activo (antes la
  sección sólo salía si había grupos detectados).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 14:03:14 +00:00
sergio 823eff0343 docs(cosmobiologia): marca armónicos como hechos en la SDD
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 13:58:07 +00:00
sergio 54de7849c5 feat(cosmobiologia): espectro de fuerza armónica — histograma clicable
Completa la feature de armónicos: además de la carta armónica, ahora
hay un espectro que guía qué armónico mirar.

- cosmobiologia-render: harmonic_spectrum computa la fuerza de cada
  armónica 1-32 (suma de cercanía a conjunción exacta de los pares de
  cuerpos en esa armónica). apply_harmonic lo puebla + expone el
  armónico activo. Campos RenderModel.harmonic / .harmonic_spectrum.
  2 tests nuevos (el pico cae en la armónica resonante).
- cosmobiologia-canvas: render_harmonic_spectrum pinta el histograma
  en el footer; cada barra es clicable y emite HarmonicSelected — un
  clic salta a esa armónica. La barra activa va resaltada.
- shell: select_harmonic fija el slider del módulo natal y recompone.
- modules: el slider de armónico pasa de 1-20 a 1-32 (rango del
  espectro).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 13:57:54 +00:00
sergio 968255f4cd feat(cosmobiologia): carta armónica — el slider de armónico ahora pinta
El slider "Armónico" del NatalModule existía pero no hacía nada.
Ahora re-renderiza la carta en el armónico de orden N.

- cosmobiologia-render: módulo `harmonic` agnóstico — apply_harmonic
  transforma los cuerpos natales a (longitud·N) mod 360 y recomputa
  los aspectos sobre las posiciones armónicas (conjunción, oposición,
  trígono, cuadratura, sextil). Las casas se conservan como marco.
  6 tests (incluye: quintil natal → conjunción en H5).
- cosmobiologia-engine: NatalOptions.harmonic; compose lo aplica tras
  la pasada natal, antes de los overlays. Test end-to-end.
- shell: build_natal_options lee el slider del módulo natal.

El título anota "· HN". Falta: histograma de fuerza por armónico.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 13:54:02 +00:00
sergio ed4d5ffe4c docs(cosmobiologia): marca el Sistema GR como completo en la SDD
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 13:48:54 +00:00
sergio ec111a2e27 feat(cosmobiologia): GR — scrubbing live de la edad con el jog-dial
Tercer y último incremento del Sistema GR: en modo GR (direcciones
primarias activas) el jog-dial deja de rotar el wheel y pasa a
scrubear la edad en vivo.

- canvas: CanvasState::gr_active() detecta el modo; on_jog_move emite
  CanvasEvent::GrAgeDelta (años por grado de jog, sensibilidad 0.1)
  en vez de rotar; on_jog_up no aplica snap de tiempo.
- shell: scrub_gr_age acumula el delta sobre target_age_years del
  módulo primary_directions, clampa a [0,120], sincroniza el slider
  del panel y recompone — los glifos dirigidos y el HUD se mueven en
  vivo bajo el cursor.

Con esto el Sistema GR queda completo: cómputo de triggers, resaltado
de convergencias, HUD de rectificación y scrubbing live.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 13:47:57 +00:00
sergio 363f401b75 feat(cosmobiologia): GR — resaltado de eventos + HUD lateral de triggers
Segundo incremento del Sistema GR: el canvas ahora hace visible la
rectificación.

- Resaltado de convergencias: por cada punto natal donde un directo y
  un converso coinciden dentro del micro-orbe, un eje brillante cruza
  la zona del dual-ring hasta el cinturón natal, con marcador glow.
- HUD lateral: columna a la derecha del wheel cuando GR está activo,
  con los triggers ordenados por orbe. Color rojo→gris según orbe; las
  convergencias llevan ✦ y fondo resaltado.

paint_wheel recibe los gr_triggers; render_wheel monta el body como
fila wheel+HUD sólo en modo GR.

Falta: scrubbing live del jog-dial mapeado a la edad.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 13:44:14 +00:00
sergio 15e45ace9b feat(cosmobiologia): GR — cómputo de triggers y eventos de rectificación
Primer incremento del Sistema GR (García Rosas): la engine, además del
dual-ring directo/converso, ahora computa los triggers de rectificación
y detecta las convergencias directo+converso sobre un mismo punto natal.

- cosmobiologia-render: módulo `gr` agnóstico — tipos GrTrigger/GrDirection
  + compute_gr_triggers (emparejamiento puro, 7 tests). Campo gr_triggers
  en RenderModel (serde-default, back-compat).
- cosmobiologia-engine: build_primary_directions_overlay computa los
  triggers contra cuerpos natales + 4 ángulos; orbe HUD 2°, micro-orbe
  de evento 5'. Test end-to-end con eternal.

Falta: resaltado del evento en el canvas, HUD lateral, scrubbing live.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 13:40:09 +00:00
sergio e77a32f4d6 feat(minga): minga-vfs — proyecta el repo como filesystem FUSE
minga-vfs deja de ser un stub: monta el repositorio direccionado por
contenido como un filesystem FUSE de sólo lectura. roots/<hash> da el
código fuente reconstruido (formato normalizado) de cada raíz del MST;
cas/<hash> resuelve cualquier hash bajo demanda como S-expression.

Capas separadas: render (SemanticNode→texto, puro) + source (contrato
NodeSource, backends sled/memoria) + fs (único módulo con fuser).
Nuevo subcomando `minga mount <punto>`. Dep fuser 0.15 sin libfuse-dev
(default-features = false). 14 tests nuevos, sin regresión en minga-cli.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 13:23:44 +00:00
sergio 762bf95dfd feat(arje): arje-absorb — absorbe otros inits a una Semilla brahman
Nuevo crate `crates/init/arje-absorb`: lee la configuración de un init
clásico y la traduce a una Tarjeta Semilla (Card JSON) con cada
servicio como hija genesis de arje-zero. El paso «absorber» de la
migración a arje — para no perder los servicios al cambiar de init.

- Absorbers: sysvinit (/etc/inittab), runit (runsvdir o /etc/sv),
  dinit (/etc/dinit.d), openrc (/etc/runlevels). Autodetección.
- Modelo intermedio ForeignService → Card vía brahman-card (validado).
- `--with-carmen`: agrega carmen-dm (gestor de login gráfico).
- CLI: --from/--root/--output/--label/--with-carmen. 24 tests, clippy
  limpio.

`scripts/migrate-to-arje.sh`: orquesta absorber → validar → (carmen:
compila+instala mirada dinámico) → install-arje-as-init.sh. El init
viejo queda intacto; arje se elige en GRUB. --dry-run no toca nada.

systemd no se absorbe (units no son texto trivial) — para systemd
sigue la capa de shims + seeds/arje-host.card.json.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 00:40:34 +00:00
sergio 758f61f52a feat(carmen): modo greeter — mirada-compositor como DM
`mirada-compositor --greeter` arranca como gestor de login: lanza
mirada-greeter como proceso hijo, lee su stdout y, al recibir el
SessionTicket, muta de BodyMode::Greeter a BodyMode::Session sin
reiniciar el servidor Wayland — la «mutación atómica» del DM.

- BodyMode { Greeter, Session }: eje ortogonal a Brain (Embedded/Linked).
- modo greeter: sin atajos registrados, rechaza Spawn, sin autoarranque.
- traspaso (complete_greeter_handoff): registra los atajos y arranca la
  sesión — el comando del tiquet, o el autoarranque del usuario.
- privilegios: el compositor corre como root; spawn_command baja a
  setuid/setgid + grupos suplementarios del usuario autenticado.
- bandera ortogonal al backend (--greeter [--drm|--winit]); el tiquet
  llega por un canal calloop en DRM y por mpsc en winit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 00:06:59 +00:00
sergio 634a43006a feat(charka): PICTURE de edición — Z, coma de millares y punto decimal
El formateo de informes de COBOL: supresión de ceros a la izquierda,
coma de millares e inserción del punto decimal. Rebanada vertical.

- charka-lexer: el punto separador exige un espacio detrás; un punto
  pegado a un carácter (ZZ9.99) ya no es terminador, sino símbolo —
  el parser lo reensambla dentro de la cláusula PICTURE.
- charka-runtime: format_edited(valor, pic) — 9, Z, coma, punto, B.
- charka-ir: Field::edit guarda la PICTURE; el campo es texto.
- charka-codegen / charka-shadow: MOVE a un campo de edición pasa por
  format_edited antes de almacenar.
- Corpus: 19-reporte. Sombra y crate compilado dan la misma salida.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 23:00:15 +00:00
sergio b3278bdb0c feat(charka): E/S de ficheros — SELECT/FD/OPEN/READ/WRITE/CLOSE
El gran hueco que faltaba para el COBOL real: el procesamiento de
ficheros secuenciales. Una rebanada vertical por los seis crates.

- charka-parser: la ENVIRONMENT division ya no se ignora — se parsea
  FILE-CONTROL (SELECT name ASSIGN TO "ruta"); del FILE SECTION se
  asocia cada FD con su registro 01. Program::files.
- charka-runtime: tipo CobFile — un fichero «line sequential» (cada
  registro una línea). Lectura: carga a memoria. Escritura: acumula y
  vuelca al cerrar.
- charka-ir: Ir::files y los statements Open/Close/Read/Write. READ
  lleva sus bloques AT END / NOT AT END.
- charka-codegen: un campo CobFile por fichero en el struct Program;
  los verbos emiten llamadas al runtime.
- charka-shadow: el intérprete hace E/S de ficheros real.
- Corpus: programa nuevo 18-fichero — escribe tres líneas, las relee
  con READ ... AT END y las muestra. Verificado: el intérprete sombra
  y el crate compilado por scaffold dan la misma salida.

Alcance v1: organización line sequential; sin ficheros indexados ni
relativos, sin FILE STATUS.

Tests: charka-parser 17, charka-runtime 19, charka-ir 30,
charka-codegen 25, charka-shadow 23. fmt + clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:47:26 +00:00
sergio f250fd0765 feat(charka): PERFORM ... THRU como rango real de párrafos
PERFORM A THRU C ejecuta A, B y C; antes el transpilador sólo
ejecutaba A (lo marcaba como aproximado).

- charka-codegen: Symbols registra ahora los párrafos en orden con su
  nombre de método; Symbols::build toma el Ir completo.
  paragraph_range(name, thru) da los métodos del rango; emit_perform
  emite la llamada a cada uno.
- charka-shadow: run_paragraph_range ejecuta los párrafos de name a
  thru inclusive.
- Corpus: programa nuevo 17-rangopar (PERFORM PASO-A THRU PASO-C sobre
  tres párrafos). Verificado: el intérprete sombra y el crate
  compilado por scaffold dan la misma salida.

Tests: charka-codegen 24, charka-shadow 22. fmt + clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:36:53 +00:00
sergio 82ba0b7a1a feat(charka): SET ... TO TRUE — escribir nombres de condición (88)
La cara de escritura de los nombres de condición de COBOL: si
IF ES-VALIDO los lee, SET ES-VALIDO TO TRUE los escribe.

- IR: Stmt::SetTrue { conditions }.
- Parser: SET cond-1 cond-2 ... TO TRUE. Otras formas de SET
  (índices, TO FALSE) caen a Stmt::Unknown.
- Codegen y shadow: SET cond TO TRUE asigna a su dato padre el valor
  del 88 (un MOVE del valor a la variable).
- Corpus: programa nuevo 16-bandera (cambia banderas de texto y de
  número con SET). Verificado: el intérprete sombra y el crate
  compilado por scaffold dan la misma salida.

Tests: charka-ir 29, charka-codegen 23, charka-shadow 21. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:32:08 +00:00
sergio fa65f20206 feat(charka): INITIALIZE — resetear datos y grupos
El verbo de COBOL para volver un dato (o un registro entero) a su
valor por defecto.

- IR: Stmt::Initialize { targets }. El model de charka-ir registra
  ahora los grupos y sus datos elementales (DataModel::groups,
  GroupInfo { name, members }).
- Parser: INITIALIZE name-1 name-2 ...
- Codegen y shadow: cada destino, si es un grupo, se expande a sus
  miembros; cada dato elemental se pone a 0 (numérico) o a espacios
  (alfanumérico); una tabla OCCURS resetea todos sus elementos.
- Corpus: programa nuevo 15-resetear (resetea un grupo y un escalar).
  Verificado: el intérprete sombra y el crate compilado por scaffold
  dan la misma salida.

Tests: charka-ir 28, charka-codegen 22, charka-shadow 20. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:28:47 +00:00
sergio 7867d6830e feat(charka): EVALUATE TRUE y rangos WHEN ... THRU
Completa el EVALUATE con sus dos formas que faltaban.

- IR: la rama WhenBranch pasa de values: Vec<Operand> a
  tests: Vec<WhenTest>, donde WhenTest es Value (igualdad), Range
  (WHEN lo THRU hi) o Cond (EVALUATE TRUE WHEN cond).
- Parser: detecta EVALUATE TRUE y entonces cada WHEN parsea una
  condición; en modo valor reconoce WHEN lo THRU hi.
- Codegen y shadow: una prueba Range se traduce a lo <= s <= hi; una
  Cond, a la condición directa.
- Corpus: programa nuevo 14-clasifica (clasifica notas con rangos THRU
  y un EVALUATE TRUE). Verificado: intérprete sombra y crate compilado
  dan la misma salida.

Tests: charka-ir 27, charka-codegen 21, charka-shadow 19. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:22:43 +00:00
sergio 2728698f5e feat(charka): INSPECT — contar y reemplazar caracteres
El verbo de COBOL para analizar y limpiar campos de texto.

- IR: Stmt::Inspect { target, op } con InspectOp::TallyingForAll
  (cuenta apariciones y las suma a un contador) y
  InspectOp::ReplacingAll (reemplaza apariciones).
- Parser: INSPECT t TALLYING n FOR ALL lit y
  INSPECT t REPLACING ALL a BY b. Una forma no soportada cae a
  Stmt::Unknown.
- Codegen: TALLYING -> str::matches(..).count(); REPLACING ->
  str::replace.
- Shadow: el intérprete cuenta / reemplaza el texto.
- Corpus: programa nuevo 13-inspeccion. Verificado: el intérprete
  sombra y el crate compilado por scaffold dan la misma salida.

Alcance v1: TALLYING FOR ALL y REPLACING ALL; sin LEADING, FIRST,
CHARACTERS, BEFORE/AFTER.

Tests: charka-ir 26, charka-codegen 20, charka-shadow 18. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:17:47 +00:00
sergio 47c49acd47 feat(charka): STRING y UNSTRING — manejo de cadenas
Dos verbos comunes de COBOL para construir y partir cadenas.

- IR: Stmt::StringConcat { sources, into } y
  Stmt::Unstring { source, delimiter, into }.
- Parser: STRING a b DELIMITED BY SIZE INTO t END-STRING y
  UNSTRING s DELIMITED BY d INTO a b c END-UNSTRING.
- Codegen: STRING -> format! concatenado; UNSTRING -> un bloque que
  parte con str::split y reparte los trozos a los destinos.
- Shadow: el intérprete concatena / parte el texto y lo reparte.
- Corpus: programa nuevo 12-cadenas. Verificado: el intérprete sombra
  y el crate compilado por scaffold dan la misma salida.

Alcance v1: STRING con DELIMITED BY SIZE (otros delimitadores se
ignoran); sin WITH POINTER ni ON OVERFLOW.

Tests: charka-ir 25, charka-codegen 19, charka-shadow 17. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:09:10 +00:00
sergio 3902763daa feat(charka): OCCURS — tablas y referencias con subíndice
Los arrays de COBOL, que antes el transpilador descartaba en silencio.
Una rebanada vertical amplia que atraviesa el pipeline entero.

- Parser: la cláusula OCCURS n [TIMES] se captura en DataItem.
- IR: Operand::Indexed { name, index } — una referencia ELEM(I), con
  subíndice 1-based. Los destinos de los statements pasan de
  Vec<String> a Vec<Operand>, así que se puede escribir a un elemento
  de tabla (MOVE x TO ELEM(I), COMPUTE ELEM(I) = ...). model::Field
  gana occurs: Option<u32>.
- Codegen: un campo OCCURS se emite como Vec<Num>/Vec<Text>,
  inicializado con vec![..; n]; una referencia con subíndice indexa el
  vector (1-based -> 0-based).
- Shadow: en el intérprete todo campo es un vector — un escalar es de
  longitud 1, una tabla de n; las referencias se resuelven a
  (nombre, índice).
- Corpus: programa nuevo 11-tabla (llena una tabla con cuadrados y los
  suma). Verificado: el intérprete sombra y el crate compilado por
  scaffold dan ambos SUMA DE CUADRADOS = 000055.

Alcance v1: OCCURS elemental, una dimensión, subíndice de un operando.
Fuera: OCCURS de grupo, multidimensional, DEPENDING ON.

Tests: charka-parser 16, charka-ir 24, charka-codegen 18,
charka-shadow 16. fmt + clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:03:48 +00:00
sergio 28ee1ae260 feat(charka): nivel 88 + modelo de datos compartido en charka-ir
Los nombres de condición de COBOL (IF ES-VALIDO), que antes el
transpilador evaluaba siempre como false. Y, de paso, se elimina la
duplicación de la resolución del modelo de datos.

- charka-ir gana un módulo `model`: resolve_data(&[DataItem]) ->
  DataModel aplana el árbol de datos a campos elementales (Field con
  FieldKind) y a nombres de condición (ConditionName). El Ir lleva
  ahora un campo `model` — la fuente única de verdad sobre la
  clasificación de PICTURE.
- charka-codegen y charka-shadow consumen ir.model en vez de
  reimplementar cada uno la clasificación, el ancho de PICTURE y la
  normalización de VALUE. charka-codegen ya no depende de charka-bcd.
- Cond::Named (un nivel 88) se resuelve a `padre = valor`: el codegen
  emite la comparación, el intérprete sombra la evalúa.
- Corregido: un dato con hijos de nivel 88 antes se perdía como si
  fuera un grupo; ahora se reconoce como campo elemental.
- Corpus: programa nuevo 10-condicion (semáforo con 88 de texto y de
  número). Verificado: intérprete y crate compilado dan igual salida.

Tests: charka-ir 23, charka-codegen 17, charka-shadow 15. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 21:50:06 +00:00
sergio 4df7478b71 feat(charka): EVALUATE — el case de COBOL
EVALUATE atraviesa el pipeline entero — antes el parser lo guardaba
crudo como Stmt::Unknown.

- IR: Stmt::Evaluate { subject, whens, other } con
  WhenBranch { values, body }. Varios WHEN apilados comparten cuerpo;
  WHEN OTHER es el caso por defecto.
- Parser: EVALUATE subject WHEN v1 WHEN v2 ... [WHEN OTHER ...]
  END-EVALUATE.
- Codegen: lo baja a una cadena if / else if / else — una rama se
  elige si el sujeto es igual a alguno de sus valores, sin caída.
- Shadow: el intérprete evalúa el sujeto y ejecuta la primera rama
  cuyos valores casen, o el WHEN OTHER.
- Corpus: programa nuevo 09-evaluar (EVALUATE por valor anidado en un
  PERFORM VARYING, con WHEN apilados y WHEN OTHER). Verificado: el
  intérprete sombra y el crate compilado por scaffold dan la misma
  salida.

Alcance v1: EVALUATE por igualdad de valor; no la forma EVALUATE TRUE
con condiciones ni los rangos THRU.

Tests: charka-ir 19, charka-codegen 16, charka-shadow 14. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 21:37:28 +00:00
sergio 321e6f8e27 feat(charka): PERFORM VARYING — el bucle con variable de control
El bucle más usado de COBOL, que antes el parser degradaba a un
PERFORM vacío (un hueco de corrección real). Ahora atraviesa el
pipeline entero como una rebanada vertical.

- IR: PerformControl::Varying { var, from, by, until }.
- Parser: reconoce PERFORM VARYING var FROM x BY y UNTIL cond en
  línea (END-PERFORM) y fuera de línea (PERFORM párrafo VARYING ...).
- Codegen: emite var = from; while !(until) { cuerpo; var += by; }.
- Shadow: el intérprete inicializa la variable, evalúa la condición
  antes de cada vuelta e incrementa al final.
- Corpus: programa nuevo 08-varying (suma 1..10). Verificado: el
  intérprete sombra y el crate compilado por scaffold dan ambos
  SUMA 1 A 10 = 00055 — las dos rutas concuerdan.

Tests: charka-ir 18, charka-codegen 15, charka-shadow 13. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 21:33:14 +00:00