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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
App nueva crates/apps/charka — el binario `charka`, que vuelve usable
el pipeline COBOL->Rust desde la terminal.
- transpile <in.cob> [-o out.rs] — emite el código Rust.
- scaffold <in.cob> -o <dir> — genera un crate Rust completo
(Cargo.toml + src/main.rs) que depende de charka-runtime y compila.
- run <in.cob> — ejecuta el programa con el intérprete sombra, sin
compilar nada, y muestra su salida.
- check <in.cob> -e <esperado> — ejecuta y diferencia contra una
salida esperada; reporta las líneas que difieren.
Avisa de los verbos COBOL que aún no se transpilan. Verificado de
punta a punta contra el corpus: scaffold de 06-nomina genera un crate
que compila y produce la misma salida que el intérprete sombra — las
dos rutas de ejecución concuerdan.
4 tests; fmt + clippy limpios.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
El pipeline COBOL->Rust queda completo (7 crates) y validado de punta
a punta.
charka-shadow certifica que el transpilador preserva la semántica del
COBOL original con una ejecución sombra: un intérprete que corre el Ir
directamente sobre charka-runtime, sin compilar nada. Es una segunda
ruta de ejecución, independiente del código que emite charka-codegen
— si la sombra y el transpilado divergieran, sería un bug.
- interpret(&Ir) -> Outcome ejecuta el IR y captura las líneas de
DISPLAY; run_source(&str) corre el pipeline completo.
- Tope de pasos (Halt::StepLimit): un bucle que no termina se corta
en vez de colgarse.
- Módulos: field (datos -> campos vivos) / interp (el motor).
Corpus nuevo crates/modules/charka/corpus/ — 7 programas COBOL de
complejidad graduada (01-hola .. 07-clasificar) con sus salidas
esperadas verificadas a mano: DISPLAY, aritmética con GIVING,
IF/ELSE, PERFORM TIMES/UNTIL, grupos, COMPUTE con paréntesis,
ROUNDED, IF anidado con AND. Material de prueba del pipeline entero.
11 tests (los 7 del corpus + fuente vacío, STOP RUN, tope de pasos,
error de léxico); fmt + clippy limpios.
No hay GnuCOBOL en la máquina: la referencia v1 es el corpus; un modo
futuro diferenciará contra el compilador real.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
La etapa final del transpilador. generate(&Ir) -> String produce un
fuente Rust (un main.rs) que, compilado contra charka-runtime, ejecuta
la lógica del programa COBOL.
- struct Program con un campo Num/Text por dato elemental; new() lo
inicializa desde las cláusulas VALUE.
- Un método p_<párrafo> por párrafo del PROCEDURE; run() los encadena
en orden (el «caer» de COBOL); main() construye y corre.
- Cada Stmt -> código Rust: MOVE->.store/.fill, DISPLAY->println!,
COMPUTE y aritmética -> expresiones Decimal, IF->if/else,
PERFORM-> llamada / for / while, STOP RUN->process::exit.
- Tolerante: lo no transpilable (Stmt::Unknown, dato sin resolver, **)
se emite como comentario // charka: — el código generado compila.
- Saneado de identificadores COBOL->Rust (choques con keywords).
- Verificado de punta a punta: un programa COBOL demo transpila a Rust
que compila contra charka-runtime y produce la salida esperada.
- Módulos: emit / sym / expr / stmt. 14 tests; fmt + clippy limpios.
El pipeline COBOL->Rust corre de punta a punta. Falta sólo
charka-shadow (validador en sombra).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
El soporte que los programas COBOL transpilados enlazan. charka-codegen
emitirá Rust que llama a esta biblioteca, no Rust autónomo.
- Num: campo numérico (PIC 9(5)V99) — un Decimal conformado a su
Picture. store trunca a la escala declarada, store_rounded redondea;
al desbordar la parte entera conserva los dígitos de bajo orden (el
ON SIZE ERROR de COBOL sin cláusula). display da los dígitos con
relleno de ceros y signo.
- Text: campo alfanumérico (PIC X(n)) de longitud fija — store
justifica a la izquierda y rellena/trunca; fill mueve figurativas.
- cobol_text_cmp: comparación alfanumérica con relleno de espacios.
- Reexporta Decimal/Picture/Rounding de charka-bcd.
Construido antes que charka-codegen (la nota de orden del plan los
listaba al revés): el codegen emite contra esta API. 17 tests; fmt +
clippy limpios.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tercera etapa del transpilador: Program -> Ir. El PROCEDURE division
pasa de sentencias con tokens crudos a un árbol de instrucciones
tipadas.
- lower(&Program) -> Ir: total y tolerante, nunca falla. La DATA
division pasa tal cual y sirve de tabla de símbolos.
- Stmt cubre MOVE, DISPLAY, ACCEPT, COMPUTE, ADD, SUBTRACT, MULTIPLY,
DIVIDE, IF/ELSE/END-IF, PERFORM (fuera de línea, en línea, TIMES,
UNTIL), GO TO, STOP RUN, GOBACK, EXIT, CONTINUE.
- Expresiones de COMPUTE con precedencia y paréntesis (Pratt).
Condiciones con comparadores símbolo/palabra, AND/OR/NOT y nombres
de condición (nivel 88).
- Delimita statements por palabras frontera (COBOL no los separa con
un símbolo). Verbo no soportado -> Stmt::Unknown con tokens crudos.
- Módulos: ast / kw / cursor / expr / stmt. 17 tests; fmt + clippy
limpios.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Segunda etapa del transpilador: Vec<Token> -> Program. Alcance v1 = el
esqueleto del programa.
- parse(&[Token]) -> Result<Program, ParseError>. AST: Program
(program_id, data, paragraphs), DataItem, Paragraph, Sentence.
- Particiona el flujo en las 4 divisions por sus encabezados; extrae el
PROGRAM-ID de la IDENTIFICATION.
- DATA division -> árbol de DataItem: nivel, nombre, PICTURE
reensamblado (S9 ( 5 ) V99 -> S9(5)V99) y VALUE. Anida por número de
nivel (01/77 raíces, 88 cuelga del precedente).
- PROCEDURE division -> Vec<Paragraph> con Sentence de tokens crudos
(sin parseo de statement). Sentencias previas al primer encabezado
van a un párrafo implícito "".
- Tolerante: salta SECTION, FD/SD y cláusulas que no sean PIC/VALUE.
- 15 tests verdes; fmt + clippy limpios.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Primera etapa del transpilador COBOL→Rust (Fase D del plan macro):
texto COBOL → secuencia de Token. Lexer deliberadamente tonto (emite
Word para todo identificador, la clasificación es del parser). Tokens
Word/Number/String/Period/Symbol con línea+columna; soporta formato
fijo (tarjeta de 80 columnas) y libre; comentarios, comillas dobladas,
operadores de 1 y 2 caracteres. LexError tipado. 17 tests; clippy
limpio. Limitación v1: sin continuación de literales entre líneas.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Validación inline: al fallar un submit por campos required vacíos, el
form los marca (label destructivo + mensaje debajo), no sólo un toast.
MetaApp.form_errors + validate_required_fields. Secciones de formulario:
FieldSpec.section agrupa campos bajo encabezados; abrir_form del CRM las
usa. Campos condicionales y pulido puramente visual: scope-out conciente.
El plan docs/nakui-erp-masterplan.md queda completo (7/7 fases). Tests
verdes (meta-schema 16, meta-runtime 70, meta-form 8, nakui-ui 14);
clippy limpio en las libs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>