PDA

Ver la Versión Completa : Correcciones 1.62.0 a 1.62.129 (desde 1.61.185)


Eslopes
31 de agosto de 2026, 13:14
Lista completa de correcciones desde la 1.61.185 hasta la 1.62.129 (130 versiones de correccion, del 24 al 31 de agosto de 2026). Este ciclo estuvo dedicado por completo a la conformidad con la suite NIST CCVS85 de COBOL-85: al cierre, los 420 programas del alcance compilan (100%) y los 380 programas ejecutables terminan con 8362 aserciones PASS y 0 FAIL en los modulos NC, SQ, IX, RL, IF, IC, ST y SM. A continuacion, cada correccion con su explicacion.

=== PowerRustCOBOL 1.62.0 — 2026-08-24 ===

• La documentación se traduce por regeneración, no por parches

La política anterior exigía que cada cambio de documentación llevara el mismo delta a cinco idiomas en el mismo commit. No sobrevivió al contacto con una Developer's Guide de 319 KB: -pt, -jp y -cn acabaron siendo texto en inglés bajo un nombre de archivo traducido, -es era una traducción parcial estancada en 1.425 líneas frente a las 5.999 del inglés, y -fr nunca llegó a existir. Una traducción que en realidad es inglés es peor que una ausente, porque aparenta estar terminada.

Ahora las traducciones se descartan y se regeneran completas a partir del canónico inglés, y el ciclo se ejecuta solo en un salto de versión mayor o menor — nunca en un fix, donde el coste no se justifica. Cuando un fix toca un documento, sus cinco traducciones se borran y simplemente quedan ausentes hasta que la siguiente versión menor las reconstruye. Ningún archivo en inglés se borra jamás. La política queda registrada en CLAUDE.md (GOLDEN RULE #8) y en specs/steering/docs.md.

- Todo documento en inglés lleva ahora -en. Se renombraron once archivos (observability.md -> observability-en.md, etcétera), de modo que los seis archivos de un documento comparten por fin una única regla de nombres; README.md, los enlaces entre documentos, dos comentarios de documentación en Rust y la skill /docsync se actualizaron con ellos.
- La Ayuda del IDE no necesitó cambios en el resolvedor — docs_embed.rs::split_lang ya aceptaba ambas grafías — pero sus tests afirmaban los nombres antiguos y la vieja realidad de "faltan algunos idiomas". Las aserciones por archivo se sustituyen por every_document_ships_in_every_language, que falla si algún idioma cae de vuelta al inglés: la guardia que habría detectado la guía francesa ausente en su primera ejecución, en lugar de meses después.
- Los documentos de más de 32 KB se parten por entrada del índice en archivos de trabajo temp-*-en.md, se traducen sección a sección, se concatenan de nuevo en orden y los temporales se borran. Los cortes nunca atraviesan un párrafo, una tabla, un bloque de código cercado ni un bloque mermaid. Una ejecución interrumpida se recupera borrando temp-* y empezando de cero — nunca reanudando.
- El umbral de 32 KB no es arbitrario: todas las familias de documentos que quedaron por debajo se tradujeron de verdad, y la única familia por encima es la que se pudrió.

• END-DISPLAY y END-ACCEPT se leían como datos, no como terminadores

Ninguna de las dos palabras existía en el léxer. La tabla de terminadores de ámbito se detenía en el conjunto de COBOL-85 (END-READ ... END-UNSTRING, más END-EXEC y END-TRY), así que END-DISPLAY y END-ACCEPT caían en Token::Identifier — y is_expr_start acepta un identificador. La consecuencia no era un diagnóstico, sino un análisis silenciosamente incorrecto:


DISPLAY "A" END-DISPLAY
DISPLAY "B".


era UN solo display con tres operandos — "A", el terminador y "B" — con la segunda sentencia desaparecida. Un desarrollador que cierra cada verbo explícitamente, el hábito que trae cualquiera que llega de otro compilador, obtenía un programa que ejecutaba e imprimía lo que no era.

Post añadido a las 06:48. Post anterior a las 06:45

Ambas palabras son ahora tokens reales, consumidos por su propia sentencia (p.eat) y añadidos al conjunto de parada del parser compartido de frases de pantalla, de modo que DISPLAY X AT LINE 5 WITH HIGHLIGHT END-DISPLAY termina donde dice terminar, en vez de leer el terminador como un atributo más del display. Siguen siendo opcionales — un punto cierra la sentencia exactamente igual que antes, y un programa que nunca los escribe no se ve afectado. Tests: cuatro en test_statements — el caso de dos sentencias que fallaba, el terminador tras una frase de pantalla, las tres formas de ACCEPT (simple, FROM DATE y posicionada) y una guardia de que ambos verbos siguen analizándose sin terminador. docs/cobol85-supported-syntax.md y la Developer's Guide recogen los dos terminadores.

• El popup de autocompletado estampaba el tipo de cada nombre encima del propio nombre

Se informó contra Neumorphic Light, donde la monoespaciada grande del tema lo hacía ilegible, pero el defecto estaba en el layout y lo arrastraban todos los temas. Cada fila era un selectable_label, que se dimensiona según su rótulo — así que resp.rect.right() era el final del NOMBRE, y la pista de tipo, alineada a la derecha contra ese mismo borde, se pintaba encima del nombre al que anota. La pista de cada fila quedaba por tanto en una x distinta, allí donde terminara su nombre: la señal delatora en la captura del operador es que la pista de Panel 10 está más a la derecha que la de Panel 1.

La fila ahora crece hasta el ancho completo del popup (Button::selectable + min_size). Un átomo de texto plano no crece, así que el ancho extra cae a la derecha y el nombre queda alineado a la izquierda — y el ancla de la pista pasa a ser el borde del popup, que es lo que siempre quiso decir. El espacio vacío a la derecha de un nombre corto forma ahora parte de la fila, de modo que hacer clic en él selecciona ese elemento, como se espera que se comporte una lista de autocompletado.

Confirmado en rojo antes del arreglo, con números: dentro de un popup de 300 pt la fila medía 75.4 pt, el rótulo terminaba en 78.4 y la pista empezaba en 46.3 — 32 puntos de solape. Queda fijado por prompt_ac_row_tests (la fila abarca el popup; rótulo y pista disjuntos tanto a 14 pt como a los 24 pt que fija un tema grande). La fila se movió a prompt_ac_row para que el test ejercite el código real y no una copia.

• "Close AI Assistant" se dibujaba encima del "+" del tamaño de letra

El botón estaba en un sub-layout right_to_left al final de la fila de controles del panel de IA. Ese sub-layout toma el ancho que QUEDA y ancla su contenido al borde derecho — así que en cuanto la fila se quedaba sin sitio, el botón se dibujaba encima de los controles ya presentes, y lo que tapaba era el "+" que agranda la letra del historial. El control no estaba deshabilitado ni oculto por un bug de estado: estaba debajo.

El botón se coloca ahora en el flujo como cualquier otro botón de la fila, y la fila es horizontal_wrapped, de modo que un panel de IA estrecho pasa la cola a una segunda línea en vez de apilar controles unos sobre otros. La fila lleva ocho botones y el panel lo redimensiona el desarrollador, así que tiene que sobrevivir a ser estrecho.

=== PowerRustCOBOL 1.62.1 — 2026-08-24 ===

• Con la barra lateral plegada, los clics caían en el control equivocado

Mostrar un SideMenu plegado en el lienzo del diseñador desliza el contenido hacia la izquierda con el borde del raíl, el mismo deslizamiento que hace Run Form. Ese deslizamiento se aplicaba solo al pintado: hit_top_id seguía comprobando los rectángulos de diseño. El área clicable de cada control de contenido quedaba por tanto un ancho de raíl a la derecha del control realmente en pantalla, así que hacer clic en un control seleccionaba a su vecino izquierdo — en el formulario del informe, hacer clic en la etiqueta verde On seleccionaba CheckBox-2. La única manera de acertar en algo era apuntar a donde estaría con el raíl abierto, que es un lugar donde no se dibuja nada.

Post añadido a las 06:50. Post anterior a las 06:48

Ambas palabras son ahora tokens reales, consumidos por su propia sentencia (p.eat) y añadidos al conjunto de parada del parser compartido de frases de pantalla, de modo que DISPLAY X AT LINE 5 WITH HIGHLIGHT END-DISPLAY termina donde dice terminar, en vez de leer el terminador como un atributo más del display. Siguen siendo opcionales — un punto cierra la sentencia exactamente igual que antes, y un programa que nunca los escribe no se ve afectado. Tests: cuatro en test_statements — el caso de dos sentencias que fallaba, el terminador tras una frase de pantalla, las tres formas de ACCEPT (simple, FROM DATE y posicionada) y una guardia de que ambos verbos siguen analizándose sin terminador. docs/cobol85-supported-syntax.md y la Developer's Guide recogen los dos terminadores.

• El popup de autocompletado estampaba el tipo de cada nombre encima del propio nombre

Se informó contra Neumorphic Light, donde la monoespaciada grande del tema lo hacía ilegible, pero el defecto estaba en el layout y lo arrastraban todos los temas. Cada fila era un selectable_label, que se dimensiona según su rótulo — así que resp.rect.right() era el final del NOMBRE, y la pista de tipo, alineada a la derecha contra ese mismo borde, se pintaba encima del nombre al que anota. La pista de cada fila quedaba por tanto en una x distinta, allí donde terminara su nombre: la señal delatora en la captura del operador es que la pista de Panel 10 está más a la derecha que la de Panel 1.

La fila ahora crece hasta el ancho completo del popup (Button::selectable + min_size). Un átomo de texto plano no crece, así que el ancho extra cae a la derecha y el nombre queda alineado a la izquierda — y el ancla de la pista pasa a ser el borde del popup, que es lo que siempre quiso decir. El espacio vacío a la derecha de un nombre corto forma ahora parte de la fila, de modo que hacer clic en él selecciona ese elemento, como se espera que se comporte una lista de autocompletado.

Confirmado en rojo antes del arreglo, con números: dentro de un popup de 300 pt la fila medía 75.4 pt, el rótulo terminaba en 78.4 y la pista empezaba en 46.3 — 32 puntos de solape. Queda fijado por prompt_ac_row_tests (la fila abarca el popup; rótulo y pista disjuntos tanto a 14 pt como a los 24 pt que fija un tema grande). La fila se movió a prompt_ac_row para que el test ejercite el código real y no una copia.

• "Close AI Assistant" se dibujaba encima del "+" del tamaño de letra

El botón estaba en un sub-layout right_to_left al final de la fila de controles del panel de IA. Ese sub-layout toma el ancho que QUEDA y ancla su contenido al borde derecho — así que en cuanto la fila se quedaba sin sitio, el botón se dibujaba encima de los controles ya presentes, y lo que tapaba era el "+" que agranda la letra del historial. El control no estaba deshabilitado ni oculto por un bug de estado: estaba debajo.

El botón se coloca ahora en el flujo como cualquier otro botón de la fila, y la fila es horizontal_wrapped, de modo que un panel de IA estrecho pasa la cola a una segunda línea en vez de apilar controles unos sobre otros. La fila lleva ocho botones y el panel lo redimensiona el desarrollador, así que tiene que sobrevivir a ser estrecho.

=== PowerRustCOBOL 1.62.1 — 2026-08-24 ===

• Con la barra lateral plegada, los clics caían en el control equivocado

Mostrar un SideMenu plegado en el lienzo del diseñador desliza el contenido hacia la izquierda con el borde del raíl, el mismo deslizamiento que hace Run Form. Ese deslizamiento se aplicaba solo al pintado: hit_top_id seguía comprobando los rectángulos de diseño. El área clicable de cada control de contenido quedaba por tanto un ancho de raíl a la derecha del control realmente en pantalla, así que hacer clic en un control seleccionaba a su vecino izquierdo — en el formulario del informe, hacer clic en la etiqueta verde On seleccionaba CheckBox-2. La única manera de acertar en algo era apuntar a donde estaría con el raíl abierto, que es un lugar donde no se dibuja nada.

Post añadido a las 08:09. Post anterior a las 06:50

La prueba de impacto lee ahora la geometría que el lienzo pintó, de modo que las dos no pueden discrepar por construcción. sidebar::rail_view conserva ids, orden y longitud, así que las consultas de contenedor por índice (orden de render, visibilidad de pestañas, rectángulos de recorte) no se ven afectadas. splitter_division_at recibió el mismo tratamiento: un Splitter es contenido, así que su banda de agarre se desliza con la línea dibujada en pantalla. El diseño sigue sin tocarse jamás — el deslizamiento es una vista, el .cfrm conserva los rectángulos que dio el desarrollador y abrir el raíl los restaura.

Test: a_collapsed_rail_moves_the_clickable_area_with_the _control construye el solape que hizo visible el bug — un control diseñado en x400 pintado en x248, exactamente donde estaba diseñado su vecino — y falla con Some("Left") cuando se retira el arreglo. La Developer's Guide dice ahora que plegar el raíl desliza el contenido también en el lienzo, y que ni el deslizamiento ni el clic cambian el diseño.

=== PowerRustCOBOL 1.62.2 — 2026-08-24 ===

• Un elemento de grupo era una variable propia, no sus elementos subordinados

En COBOL-85 un grupo ES los elementos que tiene debajo, colocados uno tras otro, y es alfanumérico sean ellos lo que sean; su tamaño es la suma de los suyos. Aquí un grupo poseía una ranura de almacenamiento independiente que nada mantenía en sintonía con los hijos, así que los dos se separaban en todas las direcciones a la vez:


01 G.
05 A PIC 99.
05 B PIC 99.

MOVE "1234" TO G *> children stayed 00 / 00
DISPLAY G *> "1234" — from G's own slot
MOVE 11 TO A
DISPLAY G *> still "1234" — A is invisible from G


y un grupo cuyos hijos llevaban cláusulas VALUE se mostraba VACÍO, porque nada había escrito nunca la ranura propia del grupo.

Leer un grupo ahora concatena sus elementos subordinados y escribirlo distribuye los bytes entre ellos por anchura — la misma pareja leer-sintetizar / escribir-distribuir que ya tenía 66 RENAMES. La ranura del grupo ya no se consulta para ninguna de las dos operaciones, así que hay una sola fuente de verdad.

• FILLER desaparecía del grupo que lo contenía

ItemSym::children excluye FILLER deliberadamente: es la lista de CORRESPONDING, y un elemento sin nombre no tiene nombre por el que corresponder. Leer un grupo desde esa lista descartaba todos los separadores, así que el clásico registro de hora editada volvía como 23473536 en lugar de 23:47:35_36.

Un grupo lleva ahora una segunda lista, layout_keys — cada elemento subordinado en orden de declaración, FILLER incluido — y los elementos sin nombre reciben una clave de almacenamiento sintética (\u{2}, que no puede aparecer en una palabra COBOL) para que su VALUE se almacene de verdad. CORRESPONDING queda intacto y sigue emparejando solo por nombre. La palabra FILLER sigue siendo opcional, como permite COBOL-85: 05 PIC X VALUE ":".

• La modificación de referencia perdía los ceros iniciales de un elemento numérico

id(start:len) direcciona posiciones de carácter, así que el emisor se toma a su anchura PIC completa. Se estaba representando el valor en su lugar, lo que descarta el relleno: un PIC 9(8) que contenía 00224845 volvía como "224845", y el clásico desempaquetado MOVE T(1:2) TO HH, MOVE T(3:2) TO MM, MOVE T(5:2) TO SS, MOVE T(7:2) TO CC se corría dos posiciones a la izquierda — cada campo tomaba los dígitos de su vecino y el último se caía por el final. Informado mientras se comprobaba un reloj con ACCEPT ... FROM TIME.

Un elemento elemental NO es un grupo solo por llevar condition-names de nivel 88. ItemSym::is_group es !decl.children.is_empty(), y los 88 son hijos ahí — así que 01 WS-GRADE PIC 9(3). 88 PASSING VALUE 60 THRU 100. se leía al principio como la concatenación vacía de cero elementos de datos. Un grupo es ahora el que tiene un layout no vacío.

Post añadido a las 08:12. Post anterior a las 08:09

Tests: tres en test_hierarchy que cubren lectura/escritura de grupos, FILLER (con nombre, sin nombre y anidado) y la anchura de la modificación de referencia; toda la suite de cobolt-runtime está en verde (27 binarios, 224 tests). docs/cobol85-supported-syntax-en.md y la Developer's Guide recogen las tres reglas.

• Editar un documento no reconstruía nada, así que el IDE servía un conjunto obsoleto

docs_embed.rs incrusta el docs/ del repositorio en el binario con include_dir!, que lee el sistema de archivos mientras el crate compila. Cargo no puede ver a través de una macro, así que no tenía motivo para considerar obsoleto el crate cuando cambiaba un documento: editar, añadir, renombrar o traducir cualquier cosa bajo docs/ no reconstruía nada en absoluto, y el visor de Documentación seguía mostrando lo que hubiera quedado incrustado la última vez que algún archivo Rust cambió por casualidad.

Un script de build nombra ahora el directorio (cargo:rerun-if-changed). Medido: una compilación sin cambios termina en 1.2 s, y tocar un solo documento la convierte en 19.6 s — recompila, donde antes no lo hacía. Por esto las traducciones podían llegar al disco y el visor seguir contestando en inglés.

=== PowerRustCOBOL 1.62.3 — 2026-08-24 ===

• Un punto de interrupción puesto después de arrancar la sesión no hacía nada

Los puntos de interrupción eran una instantánea del arranque. El conjunto se leía una vez del margen del editor, se enviaba al depurado al abrirse la sesión y no se volvía a mirar: cada conmutación posterior cambiaba un punto en pantalla y nada más, y el programa pasaba de largo por la línea que el desarrollador acababa de marcar. Cuatro brechas separadas, cada una suficiente por sí sola:
- El margen de la ventana del depurador no era clicable en absoluto — se asignaba con Sense::hover(), así que solo pintaba. El único sitio donde un desarrollador está de verdad leyendo el código mientras está detenido en él era el único sitio donde no podía poner un punto de interrupción.
- RemoteDebugCmd::SetBreakpoints se enviaba desde exactamente un punto de llamada, en el arranque, así que después nada llegaba jamás al hijo rcrun run-form --debug.
- El conjunto compartido del DebugRunner interno del IDE solo se escribía en do_debug().
- DebuggerPanel::set_breakpoints() no tenía llamadores, así que hasta los puntos de la propia ventana y su pestaña Breakpoints quedaban obsoletos.

El margen acepta ahora clics (con un fantasma al pasar el cursor, para que un margen vacío muestre que se puede clicar) y los notifica como DebugAction::ToggleBreakpoint. Un clic se registra contra el archivo que la ventana del depurador está MOSTRANDO — el .cbl generado, que rara vez es la pestaña en primer plano — a través del nuevo EditorPanel::toggle_breakpoint_at. Y sync_breakpoints_to_debuggee corre en cada frame en ambos tipos de sesión, enviando el conjunto siempre que difiera de lo último que se le dijo a ese depurado, de modo que un punto puesto, movido o quitado a mitad de sesión surte efecto en la siguiente sentencia.

Alcanzar un punto de interrupción dentro de un manejador de eventos era el peor caso, y conviene decirlo sin rodeos: el manejador solo corre en la sesión externa rcrun run-form --debug. El DebugRunner interno del IDE arranca el intérprete sin anfitrión de formulario y sin canal de eventos, así que COBOL-WAIT-EVENT toma su rama de CLI, pone COBOL-QUIT de inmediato y el bucle de eventos sale antes de despachar ningún manejador. Depurar el manejador de un formulario significa depurar el formulario, no el .cbl generado desde el editor. Tests: a_breakpoint_can_be_set_on_a_file_that_is_not_in_f ront (la conmutación por ruta que necesita el margen del depurador) y the_window_reports_the_file_its_breakpoints_belong _to (el panel informa de su ruta de origen, set_breakpoints surte efecto y reset limpia la ejecución sin borrar las marcas del desarrollador).

=== PowerRustCOBOL 1.62.4 — 2026-08-24 ===

• "Only my code": el paso a paso cruza el bucle de eventos generado

Post añadido a las 08:14. Post anterior a las 08:12

El .cbl generado de un formulario es en su mayoría fontanería que el desarrollador nunca escribió, y el COBOL-EVENT-LOOP es lo peor de ella: avanzar paso a paso ponía docenas de sentencias entre el desarrollador y su propio manejador. Operador, 2026-08-24: "do not animate the cobol event loop. This is an internal construct... the focus is on the user code."

La funcionalidad entera había sido diseñada de punta a punta y dejada sin su parte central. debugger.rs ya tenía UserScope / DebugUserScope / new_user_scope, el protocolo de cable ya tenía RemoteDebugCmd::SetUserScope { user_only, user_lines }, y codegen::generate_with_user_lines ya informaba de qué líneas generadas contienen los cuerpos de manejadores y procedimientos del propio desarrollador — con un test dorado fijándolo. Pero Interpreter::set_debug_user_scope no existía, debug_check no tenía filtro, form_gui.rs aceptaba el comando como no-op documentado y nada en el IDE lo enviaba jamás. Ninguna de esas cuatro piezas era alcanzable desde otra. Ahora están unidas, y el conmutador está en la barra de herramientas del depurador — ACTIVADO por defecto, puesto que el andamiaje no es código del desarrollador. Ambos tipos de sesión llevan el ámbito, así que se comporta igual venga la sesión de un formulario o del editor.

Los puntos de interrupción deliberadamente NO se filtran. Poner uno es un acto explícito, y negarse en silencio a parar ahí sería una sorpresa peor que atravesar un bucle paso a paso. El ámbito gobierna el paso a paso, nada más. Un ámbito ausente o vacío significa "parar en cualquier sitio" — un conjunto vacío jamás debe leerse como "no parar en ningún sitio", o la sesión parece un cuelgue. Tests: test_debug_scope conduce una sesión de depuración real sentencia a sentencia y compara las líneas en que se detuvo. Con el ámbito activado se detuvo en [10, 12] — las dos líneas de usuario, con 12 líneas generadas cruzadas sin pausa; con él desactivado, [8, 9, 10, 11, 12, 13, 14], cada sentencia como antes.

=== PowerRustCOBOL 1.62.5 — 2026-08-24 ===

• Los registros de fecha y hora daban UTC, no el reloj local

COBOL-85 define ACCEPT ... FROM DATE / TIME / DAY / DAY-OF-WEEK y FUNCTION CURRENT-DATE sobre el reloj LOCAL. Todos se derivaban directamente de SystemTime::now() desde UNIX_EPOCH, que es UTC — una hora del día distinta para la mayor parte del mundo, y una FECHA distinta a ambos lados de la medianoche. Un reloj construido de la manera obvia marcaba tres horas mal en São Paulo: ACCEPT date-time FROM TIME devolvía 00224845 a las 21:22 hora local.

FUNCTION CURRENT-DATE era peor que errónea: era autocontradictoria. Sus últimos cinco caracteres son el desfase respecto a GMT, y devolvía el literal -0000 estuviera donde estuviera la máquina. El único campo cuyo trabajo entero es decir en qué zona está el programa afirmaba GMT, siempre. Los cinco registros leen ahora chrono::Local, y CURRENT-DATE informa del desfase real de la máquina. Medido en la máquina del informe: 23:18:12.76 -0300 contra un reloj de pared que marcaba 231811 -0300, donde antes contestaba 02:18.

Esto no añade nada a la compilación. chrono ya estaba compilado dentro de cobolt-runtime a través de google_maps -> chrono-tz; ahora es dependencia directa del crate que ya lo arrastraba. Ninguna toolchain de C ni biblioteca del sistema llega con la feature clock. Tests: test_local_clock encierra una ejecución real entre dos lecturas de Local::now() — así no puede fallar de forma intermitente en un límite de segundo o de medianoche — y comprueba el desfase informado contra el de la propia máquina. Falla exactamente por el desfase si los registros vuelven alguna vez a UTC, y lo dice en voz alta cuando corre en una máquina que está ella misma en GMT, donde la distinción es invisible.

=== PowerRustCOBOL 1.62.6 — 2026-08-25 ===

• Un IDE instalado no podía compilar, y lo decía en el idioma equivocado

Eslopes
31 de agosto de 2026, 13:23
Compilar una aplicación es un cargo build de verdad: el proyecto generado depende de cobolt-ast y cobolt-runtime por ruta (path), así que las fuentes Rust de la plataforma tienen que existir en la máquina. Un IDE copiado por su cuenta a cualquier sitio no lleva ninguna, y toda compilación moría en el mismo punto, con un mensaje que tenía tres defectos: nombraba BuildOptions.workspace_root, una API interna de Rust que ningún usuario puede alcanzar; no listaba ninguna carpeta en la que hubiera mirado de verdad; y sus dos estrategias no podían tener éxito en una copia instalada — una sube desde el ejecutable buscando un manifiesto [workspace], y una instalación nunca está dentro del árbol de fuentes; la otra usaba la ruta grabada al compilar el binario, que es una carpeta de la máquina que lo compiló.


Build failed: could not locate the PowerRustCOBOL workspace crates: looked via
the running executable and the build-time path, but found no 'crates/cobolt-ast'.
Pass BuildOptions.workspace_root, or run the IDE from within the PowerRustCOBOL
source tree.


El SDK de la plataforma ahora se distribuye. SDK_CRATES nombra los diez crates contra los que compila de verdad una aplicación construida — el cierre por path = de cobolt-ast, cobolt-runtime y cobolt-form-host — dejando fuera los seis que solo construyen las herramientas, y un test demuestra que el conjunto es cerrado: ningún crate distribuido puede depender de uno que se quedó atrás. Prepararlo es un solo comando, cargo run -p cobolt-compiler --example stage_sdk -- <install-dir>, y cuesta 6.0 MB. El manifiesto de workspace preparado conserva [workspace.package] y [workspace.dependencies] — cada crate de la plataforma hereda de ellos version.workspace y serde = { workspace = true }, así que una carpeta crates/ sin ese manifiesto encima falla en cuanto cargo lee el primer miembro — y recorta members a exactamente lo copiado, cosa que fija un segundo test.

- Los crates no son todo el SDK. SDK_EXTRA_PATHS distribuye además assets/images/powerrustcobol-icon.png y assets/themes. El icono no es decoración: cobolt-form-host lo incrusta con include_bytes!, así que un árbol sin él no compila NINGUNA aplicación de formularios — y falla en el primer Build del desarrollador, no al preparar el SDK. Los temas importan más silenciosamente: sin ellos, un proyecto diseñado en Cobalt Steel o Neumorphic compila igualmente y sale en Liquid Glass sin aviso. Un SDK preparado ocupa 18.9 MB.
- sdk_covers_every_compile_time_asset recorre cada crate del SDK buscando rutas de include_bytes! / include_str! que salen del crate y falla cuando alguna no se distribuye. Esa guardia existe porque la primera versión de este cambio no copiaba assets/: el árbol preparado se resolvía limpio bajo cargo metadata — que lee manifiestos sin compilar — y luego moría en el icono.
- La búsqueda mira ahora donde una instalación coloca las cosas: junto al ejecutable, en una subcarpeta sdk/, un nivel más arriba y dentro de Resources de un bundle de macOS. Una carpeta solo califica cuando contiene A LA VEZ Cargo.toml y crates/cobolt-ast; exigir solo lo segundo es lo que dejaba pasar por válido un árbol copiado a medias. Cuando no se encuentra nada, el error nombra ahora cada carpeta que probó.
- Help -> Platform SDK Location fija la carpeta a mano para un checkout que vive en otra parte. Es un ajuste de todo el IDE (ui.toml, junto al idioma y a Beautify), nunca cobolt.toml: la ruta es una propiedad de esta máquina, y un colega que abra el mismo proyecto no debe heredar una carpeta que solo existe en el disco de otro. Una entrada en blanco significa "buscar automáticamente", no "usar el directorio de trabajo". El diálogo muestra qué carpeta está en vigor y si fue encontrada o elegida, y el ajuste llega también a la sonda de conflictos de External Crates y al escaneo de System-closure, no solo a Build.

=== PowerRustCOBOL 1.62.7 — 2026-08-25 ===

• Un bloque EXEC RUST en un segundo formulario nunca se ejecutaba

Post añadido a las 08:17. Post anterior a las 08:15

Una aplicación no es un solo programa. El COBOL generado del formulario principal es uno, y cada formulario que puede abrir es otro (051): cada uno corre en su propio intérprete, y todos resuelven un bloque compilado a través del registro ÚNICO del proceso. Tres piezas de esto trataban "el programa" como el archivo de entrada a solas, y un bloque escrito en cualquier otro formulario se colaba por las tres. Nunca llegaba a un compilador: Run decide entre el intérprete y una compilación preguntando si el programa contiene un bloque — el del formulario que se ejecuta, y solo ese. Un bloque en el manejador de eventos de un formulario hijo contestaba "no", así que el IDE arrancaba rcrun run-form, que por diseño no puede ejecutar un bloque. En Run no había nada mal; el fallo llegaba después, cuando el desarrollador pulsaba el botón que alcanzaba el bloque. La pregunta cubre ahora todos los formularios que contiene el proyecto, que es exactamente el conjunto que compila la build.

Compilar tampoco lo salvaba: exec_rust::generate recorría el programa de entrada, así que el bloque de un formulario hijo nunca se emitía ni se registraba — el binario construido fallaba en el mismo clic, tras una compilación que informó de éxito. La generación toma ahora todos los programas que el binario puede ejecutar. Y los ids colisionaban: los ids de bloque salen de un contador por análisis que empieza en cero, así que el primer bloque del formulario principal y el primero de un formulario hijo eran ambos 0. Emitir los dos sin arreglar eso habría sido peor que el bug: register(0, ...) dos veces, gana el último, y un botón ejecutando en silencio el Rust de otro formulario. parse_from continúa la numeración en su lugar, y el compilador la enhebra por todos los programas de formulario — los ids que lleva el AST y los que registra el módulo generado salen de una misma secuencia.

Un error de rustc dentro de un bloque nombra ahora EL FORMULARIO en el que se escribió. Un solo módulo contiene los bloques de todos los programas, así que el informe antiguo — siempre el archivo de entrada, en la línea propia del bloque — apuntaba al archivo que la build llamara main, con toda fiabilidad el equivocado cuando el error estaba en otra parte. Los bloques a nivel de elemento de todos los formularios se emiten juntos a ámbito de módulo, que es lo que hace que una fn auxiliar escrita en un formulario sea invocable desde un bloque de otro. Es el mismo compartir a nivel de proceso que la guía ya promete para el puente de objetos; era cierto para los handles y no para el código.

=== PowerRustCOBOL 1.62.8 — 2026-08-25 ===

• El COBOL-85 clásico ya compila: --source-format=fixed

El COBOL en imagen de tarjeta no compilaba. No "una parte": nada en absoluto. Ante la suite oficial de validación NIST COBOL-85 (CCVS85 4.0, 459 programas, 28 MB), RustCOBOL analizó 0 de ellos. Faltaban dos reglas del formato de referencia. Columnas 73-80: el estándar las reserva para el área de identificación y el compilador las ignora; nosotros las leíamos como fuente, así que cada línea de CCVS85 terminaba con su sello de programa — NC1014.2 — pegado a la sentencia, y el parser no encontraba PROGRAM-ID en un solo programa. Líneas de continuación: un guion en la columna 7 continúa la línea anterior; no las uníamos en absoluto — los fragmentos se emitían como líneas separadas, así que un literal partido en dos líneas dejaba una comilla sin pareja, y la regla de cadenas del léxer seguía más allá del fin de línea buscando su comilla de cierre, tragándose cantidades arbitrarias de programa. 396 de los 459 programas contienen un literal así. El síntoma era siempre el mismo y siempre engañoso: un expected PROCEDURE DIVISION a fin de archivo, señalando lejos de la causa.


rcrun run --source-format=fixed program.cbl
rcrun check --source-format=fixed program.cbl


Post añadido a las 08:18. Post anterior a las 08:17

Ambas reglas están ahora implementadas, tras un selector explícito. fixed es el formato de referencia clásico — área de secuencia, columna indicadora, fuente en las columnas 8-72, área de identificación descartada, líneas de continuación unidas para literales y para palabras. free (el valor por defecto), fixed-relaxed y auto son los otros valores; COBOLT_SOURCE_FORMAT fija un valor por defecto. Nada del comportamiento existente cambió: la lectura fixed relajada es una variante separada y sigue ejecutando una línea hasta donde el desarrollador la escribió. Eso importa: imponer el límite de 72 columnas en todas partes es lo que rompió EXEC RUST en los fuentes de formulario generados el 2026-08-05, ya que un .cbl generado abre con un rótulo cuyo * cae en la columna 7 y el Rust embebido no tiene ninguna regla de columnas. El formato estricto nunca se elige por detección — solo pidiéndolo.

Una decisión de criterio merece constar. Un carácter en la columna 7 que no es un indicador COBOL-85 se lee como fuente ORDINARIA, no se rechaza. CCVS85 usa el área del indicador como selector, marcando líneas opcionales con Y, P, C, S y otras once letras — 4.830 líneas Y solo de esa clase. Rechazarlas haría fallar la suite; descartarlas borraría código en silencio. Medido sobre la distribución intacta, solo el front end: 0 -> 224 de 459 (48.8 %), y el cubo de causa raíz expected PROCEDURE DIVISION cayó de 48 programas a 6. Lo que queda son lagunas reales del lenguaje — literales numéricos con punto decimal inicial, comas y puntos y comas separadores, FUNCTION x(ALL), CLOSE ... WITH LOCK — cada una especificada en specs/nist/.

=== PowerRustCOBOL 1.62.9 — 2026-08-25 ===

• Toda aplicación compilaba SQLite, así que toda compilación necesitaba un compilador de C

rusqlite está fijado con su feature bundled, que compila la amalgama en C de SQLite. cobolt-runtime la tomaba incondicionalmente, y toda aplicación generada depende de cobolt-runtime — así que un programa de consola que solo hace DISPLAY seguía construyendo una biblioteca C, y seguía necesitando una toolchain de C (link.exe en Windows, cc en el resto) además de rustc. BUILDING-en.md ya documentaba las tres cosas como requisitos que el desarrollador debe instalar; una de ellas la exigían programas que no le daban ningún uso. El fallo que producía no nombraba nada que el desarrollador hubiera escrito — un error del enlazador, contra una dependencia que nunca eligió — y aterrizaba en la máquina que compila, nunca en la que ejecuta.

El puente SQL es ahora una feature. La feature sql de cobolt-runtime lleva juntos SQLite, PostgreSQL y MySQL y está activada por defecto, así que rcrun, el IDE y todos los tests conservan la superficie que tenían. El manifiesto generado es lo único que la apaga, y lo hace para un programa del que la build puede demostrar que nunca alcanza un verbo SQL. libsqlite3-sys sale entonces por completo del grafo de dependencias, y la compilación necesita solo Rust. cobolt-form-host reenvía la misma feature en lugar de poseerla: Cargo une las features a lo largo de un grafo, así que una aplicación de formularios que recortara SQL del runtime lo habría recibido de vuelta directamente del host — un test fija ambas líneas, porque ese fallo es invisible: la compilación sigue funcionando y sigue necesitando la toolchain de C.

Post añadido a las 08:19. Post anterior a las 08:18

La lectura es deliberadamente tímida, porque equivocarse aquí es peor que desperdiciar: el IDE interpreta con el runtime completo, así que un programa mal juzgado funcionaría bajo Run Form y fallaría solo una vez compilado. La duda enlaza los drivers — un CALL que nombra un verbo SQL, un CALL cuyo destino es un elemento de datos en vez de un literal (el nombre solo se conoce en ejecución) o un bloque EXEC RUST que menciona los módulos SQL. Solo un programa donde no aparece nada de eso pierde SQL. El examen cubre el programa de cada formulario, no solo el de entrada, así que una base de datos abierta por un formulario hijo sigue enlazando. Si de algún modo llega un verbo que la build no pudo ver, el runtime dice qué decisión lo produjo y qué cambiar, en vez de informar de un fallo de conexión ordinario. Advertencia: native-tls sigue llegando a OpenSSL en Linux, así que HTTP y Maps siguen siendo allí una dependencia de biblioteca del sistema. Este cambio cubre solo SQLite.

=== PowerRustCOBOL 1.62.10 — 2026-08-25 ===

• Corregido — un literal numérico puede comenzar con punto decimal

.5 es un medio. COBOL-85 establece que un literal numérico únicamente no puede terminar con un punto decimal; uno inicial está permitido de forma explícita. RustCOBOL exigía un dígito delante, de modo que .999 se leía como un punto de fin de sentencia seguido del entero 999 — y como un punto sitúa al léxico en lo que considera un comienzo de línea, .00001 salía como un número de nivel. El daño era peor dentro de una llamada a función, donde el punto extraviado cerraba antes de tiempo la lista de argumentos; así es como el módulo de funciones intrínsecas de NIST escribe todas y cada una de sus pruebas, por lo que el módulo era en gran parte inalcanzable.


COMPUTE WS-NUM = FUNCTION ACOS(.999).


Ahora se admite en todos los lugares donde cabe un literal numérico: VALUE, MOVE, COMPUTE, condiciones, argumentos de FUNCTION, WHEN, valores de nivel 88 — incluidas las formas con signo (-.5, +.5) y, bajo DECIMAL-POINT IS COMMA, la escritura ,5. Los ceros iniciales son exactos: .000000001 es una milmillonésima, no una décima. Los dígitos escritos son los que fijan la escala, así que .000000001 * 1000000000 es exactamente 1 — comprobado ejecutando el programa y leyendo su salida, no con un parseo limpio. La distinción importa aquí: VALUE .11111 tampoco producía diagnóstico alguno antes de esta corrección; simplemente almacenaba el número equivocado, en silencio.

Qué no cambió: un punto seguido de un espacio o de un salto de línea sigue siendo un terminador de sentencia, y una PICTURE numérica editada que comienza con punto decimal — PIC .9999/99999,99999,99 — queda intacta. Las dos cosas se distinguen por la ausencia de espacio, que es como el propio COBOL-85 las separa; también es la razón de que esto se resuelva cuando el literal se parsea y no cuando se lexifica, donde una picture y un literal son indistinguibles. Un fuente malformado como MOVE X TO Y.5 sigue siendo un error de compilación en lugar de reinterpretarse en silencio. Medido sobre la suite NIST CCVS85, solo front end: dentro de alcance 222 -> 237 de 434 (51.2 % -> 54.6 %). Funciones intrínsecas 21 -> 29, Nucleus 25 -> 29, Sort/Merge 27 -> 30. Nueva suite de pruebas tests/cobol/numeric/leading-decimal-point.cbl (11 casos, con resumen cuantificado).

=== PowerRustCOBOL 1.62.11 — 2026-08-25 ===

• Corregido — los párrafos descriptivos de la IDENTIFICATION DIVISION

Post añadido a las 08:22. Post anterior a las 08:19

AUTHOR, INSTALLATION, DATE-WRITTEN, DATE-COMPILED, SECURITY — y REMARKS, que COBOL-85 eliminó pero que el fuente antiguo todavía arrastra — llevan un comment-entry: texto libre que puede contener palabras reservadas y puntos, y que se extiende hasta el siguiente encabezado de párrafo o de división. Tres cosas estaban mal. Primera: DATE-COMPILED no se manejaba nunca; al ser una palabra clave no llegaba a la rama genérica de párrafos, caía a través y terminaba la división antes de tiempo, con lo que el parser exigía un encabezado de división donde había un nombre de párrafo. Segunda: el texto terminaba en el primer punto, cuando un comment-entry es prosa y los contiene de forma rutinaria; de este ejemplo de la suite NIST, de nueve líneas, solo sobrevivía la primera. Tercera: las palabras reservadas del texto se tomaban literalmente — la recolección se detenía en el DATA de la tercera línea, concluía que había empezado la DATA DIVISION, buscaba DIVISION y encontraba AND.


INSTALLATION.
GENERAL SERVICES ADMINISTRATION
AUTOMATED DATA AND TELECOMMUNICATION SERVICE.
5203 LEESBURG PIKE SUITE 1100
FALLS CHURCH VIRGINIA 22041.
DATE-WRITTEN.


Una entrada ahora termina solo en un token que comienza una línea y que además parece un encabezado — una palabra clave de párrafo, o una palabra clave de división seguida de DIVISION. Hacen falta ambas mitades: la primera permite que la prosa contenga DATA, la segunda permite que una línea empiece legítimamente con esa palabra. La misma regla se aplicó a la comprobación de divisiones duplicadas, que examina tokens crudos antes del parseo; sin ella, SECURITY. SEE THE PROCEDURE DIVISION BELOW. se habría reportado como una PROCEDURE DIVISION redeclarada. INSTALLATION, SECURITY y REMARKS se reconocen únicamente dentro de la IDENTIFICATION DIVISION y deliberadamente no se convirtieron en palabras reservadas — un item de datos llamado SECURITY sigue funcionando.

Medido sobre la suite NIST CCVS85, solo front end: dentro de alcance 237 -> 241 de 434 (54.6 % -> 55.5 %), Debug 5 -> 9. El grupo de 32 programas que esto despejó es mayor que la ganancia: 9 de ellos son programas de Communication, que siguen fuera de alcance, y la mayoría del resto tropieza de inmediato con un segundo bloqueo. También destapó un defecto que esta corrección no puede arreglar: seis programas fallan ahora por una comilla suelta dentro de la prosa de un comment-entry — THE COMPILER"S ABILITY abre un literal que corre hasta la siguiente comilla en cualquier punto del archivo. El lexificado precede al parseo, así que el léxico no puede saber que está dentro de un comment-entry; la corrección pertenece al trabajo de literales sin terminar ya registrado en specs/nist/NIST-spec-literal-continuation.md R6.

=== PowerRustCOBOL 1.62.12 — 2026-08-25 ===

• Corregido — una sola comilla suelta podía desplazar un programa entero

Un literal ahora termina al final de su línea. Antes podía continuar más allá del salto de línea, a la caza de una comilla de cierre, y eso convertía una errata en algo mucho peor que una errata. Los programas donde se encontró el problema contienen cada uno un número par de comillas — nada estaba sin terminar. Una sola comilla dentro de prosa corriente, como el comentario *> THIS PROGRAM CHECKS THE COMPILER"S ABILITY TO HANDLE EIGHT, abría un literal que se cerraba en la siguiente comilla varias líneas más abajo, tragándose lo que hubiera en medio — en un caso, un encabezado ENVIRONMENT DIVISION entero — tras lo cual cada comilla restante del archivo se emparejaba con la pareja equivocada. Un solo carácter cambiaba la paridad del programa completo, y el error afloraba a cientos de líneas de su causa, o como un "expected PROCEDURE DIVISION" a fin de archivo sin nada que señalar.

Confinar un literal a su línea hace que el daño se autocorrija en el siguiente salto de línea: se reporta la línea infractora, y la línea siguiente vuelve a emparejarse correctamente. Un literal sin cerrar ahora lo dice, allí donde está escrito:

Post añadido a las 08:23. Post anterior a las 08:22


unterminated alphanumeric literal — a literal cannot span source lines. In fixed
format, continue it on the next line with `-` in column 7 and reopen with the
same quotation mark; in free format there is no continuation, so the literal
must fit on one line.


La continuación no se ve afectada: un - en la columna 7 sigue uniendo un literal entre líneas, sigue extendiendo el fragmento continuado hasta la columna 72 con sus espacios finales y sigue reensamblándolo byte a byte — ese trabajo ocurre en el preprocesador, antes de que el léxico vea nada. Verificado ejecutando programas y leyendo su salida, no con un parseo limpio: el HYPHEN-LINE de CCVS85 se reensambla exactamente a los 54 caracteres que declara su PICTURE X(54). Los bloques EXEC RUST tampoco se ven afectados — incluidas las cadenas multilínea propias de Rust — porque un bloque se captura por corte de offsets entre RUST y END-EXEC. Medido sobre la suite NIST CCVS85, solo front end: dentro de alcance 241 -> 242 de 434. Una ganancia pequeña por un grupo de 6 programas despejado, y la razón merece decirse: un programa ahora pasa, y cuatro avanzaron hasta SORT-PARA SECTION 69. — un número de prioridad de segmento, que es una carencia distinta. Segmentation sigue marcando 0 / 13, pero su verdadero bloqueo ya es visible.

=== PowerRustCOBOL 1.62.13 — 2026-08-26 ===

• Corregido — una coma es puntuación, no un error

COBOL-85 define una coma separadora y un punto y coma separador como una coma o un punto y coma seguidos de un espacio. Son pura decoración: pueden aparecer en cualquier lugar donde pueda aparecer un espacio y significan exactamente lo que significa un espacio. Todo desarrollador COBOL los escribe, y PowerRustCOBOL los rechazaba todos:


MOVE ZERO TO DN3, DN4.
CALL "SUB" USING TABLE-01, TABLE-02, DN3.
PROCEDURE DIVISION USING TABLE-1, TABLE-2, DN1.
READ SQ-FS1 ; AT END GO TO READ-EOF.
01 WRK-AN-X-18-1, REDEFINES WRK-XN-18-1 PIC A(18).


Los cinco casos parsean ahora, junto con cualquier otro lugar donde pueda aparecer un separador — listas de ENTRY, argumentos de FUNCTION, listas de literales de VALUE, listas OCCURS ... KEY, entradas SELECT. La coma se descarta en el léxico en lugar de saltarse en cada punto de la gramática, de modo que un sitio que nadie ha escrito todavía ya es correcto. Qué conserva su coma, porque una coma no seguida de espacio nunca es un separador: 1,5 bajo DECIMAL-POINT IS COMMA sigue siendo uno y medio, y en PIC ZZ,ZZ9.99 la coma sigue siendo la coma de edición de la plantilla — ambos sin cambios.

• Corregido — subíndices separados por espacios

El único separador que COBOL-85 exige entre subíndices es un espacio. MOVE 1 TO CELL (1 2). y MOVE 1 TO CELL (1, 2). son la misma referencia, y antes solo parseaba la segunda.

• Corregido — un subíndice tras un nombre calificado

COBOL-85 coloca el subíndice después del nombre calificado completo — data-name-1 [OF data-name-2]... [(subíndice...)]. Ese orden era imposible de parsear en cualquiera de sus dos escrituras, por ejemplo MOVE W-3 TO CELL OF COLS OF ROWS (IDX-A IDX-B). El subíndice solo se leía antes de la cadena de OF, así que la lista final caía en la regla ordinaria de expresión entre paréntesis y reportaba expected RParen en el segundo subíndice. Ahora se aceptan ambos órdenes.

• Corregido — una comilla duplicada dentro de un literal

COBOL-85 no tiene escape con barra invertida. El propio delimitador de un literal se escribe dos veces, de modo que 'IT''S' es el valor de cuatro caracteres IT'S y """" es una sola comilla. El léxico no decodificaba la duplicación: 'IT''S' se convertía, en silencio, en los dos literales separados IT y S, así que DISPLAY 'IT''S WORKING'. mostraba IT ... S WORKING. El preprocesador de formato de fuente sí había implementado esta regla desde siempre al decidir si una línea de formato fijo termina dentro de un literal, con lo que las dos mitades del compilador discrepaban sobre el mismo texto.

• Corregido — un Caption con apóstrofo o con salto de línea rompía la compilación

Eslopes
31 de agosto de 2026, 14:05
Al generar un formulario, el Caption, el Text, la URL base y la cadena de conexión del desarrollador se escribían en crudo entre dos apóstrofos. Dos valores corrientes rompían el programa generado: un apóstrofo — Don't Save — cerraba el literal antes de tiempo y el resto del caption se convertía en palabras COBOL sueltas; y un salto de línea en un caption emitía un literal partido en dos líneas de fuente sin continuación, lo que no es COBOL válido. El síntoma reportado era una compilación bloqueada con expected PROCEDURE DIVISION (line 367) — un número de línea sin relación alguna con el caption que lo causaba.

Ahora los delimitadores se duplican (el escape del estándar, que el léxico decodifica de vuelta) y los caracteres de control se pliegan a un espacio. La PICTURE, además, se dimensiona a partir del valor en lugar de un PIC X(256) fijo, así que un caption de más de 256 caracteres ya no se trunca en silencio. Un PROGRAM-ID es una palabra COBOL, no un literal, de modo que un nombre de formulario que no lo sea se sanea ahí en lugar de emitirse tal cual.

• Conformidad NIST COBOL-85: 242 -> 292 de 434 programas dentro de alcance

Progresión de PASS dentro de alcance por cambio: 1.62.12 (anterior), 242; delimitador duplicado dentro de un literal, 244; coma / punto y coma separadores, 285; subíndice tras nombre calificado, 292. Tres grupos de diagnóstico completos quedan ahora vacíos: unexpected token in statement: Comma (eran 50 programas), expected RParen (23) y unexpected token in statement: Semicolon (14). Especificación: specs/nist/NIST-spec-separators.md, con el plan y el registro de tareas junto a ella.

=== PowerRustCOBOL 1.62.14 — 2026-08-26 ===

• Corregido — una tabla entera puede pasarse a una intrínseca: FUNCTION MAX(IND(ALL))

COBOL-85 pasa una tabla completa a una función estadística subindexándola con la palabra reservada ALL: un argumento escrito se convierte en un argumento por cada ocurrencia, como en COMPUTE WS-NUM = FUNCTION MAX(IND(ALL)). o COMPUTE WS-NUM = FUNCTION SUM(TBL(ALL, 2)). ALL se leía como el prefijo de constante figurativa (ALL "X"), así que el compilador exigía un literal a continuación. Quedan cubiertas las once intrínsecas de longitud variable — MAX, MIN, SUM, MEAN, MEDIAN, MIDRANGE, RANGE, VARIANCE, STANDARD-DEVIATION, ORD-MAX, ORD-MIN.

ALL puede ocupar una dimensión de una tabla multidimensional con subíndices ordinarios en las demás, y se expande en orden por filas (row-major). Una tabla OCCURS ... DEPENDING ON se expande contra su contador en el momento en que se llama a la función.

• Corregido — MOVE ALL "X" rellenaba un carácter, no el campo

Con 01 WS-T PIC X(5)., la sentencia MOVE ALL "X" TO WS-T. dejaba "X " y ahora deja "XXXXX". ALL es la única constante figurativa cuyo carácter de relleno tiene más de un byte, y por eso SPACES y ZEROS nunca mostraron el problema: el literal aterrizaba una sola vez y el resto del campo quedaba en espacios.

• Corregido — MOVE ALL ZEROS se rechazaba

COBOL-85 permite ALL delante de otra constante figurativa, donde es simplemente redundante: ALL ZEROS es ZEROS. Solo se aceptaba un literal real, así que esa escritura fallaba con "expected literal after ALL".

• Corregido — CLOSE ... WITH LOCK y las frases de carrete/unidad

Ninguna de estas escrituras parseaba: CLOSE IX-FD2 WITH LOCK., CLOSE SQ-FS1 WITH NO REWIND., CLOSE TAPE-FILE REEL FOR REMOVAL. WITH se reportaba como token inesperado y LOCK se leía como el nombre de otro archivo que cerrar. WITH LOCK se aplica de verdad en lugar de aceptarse e ignorarse: reabrir ese archivo en la misma unidad de ejecución reporta ahora file status 38, que es el código del estándar para exactamente eso. REEL / UNIT posicionan una cinta multivolumen y se aceptan como no-ops en disco.

• Corregido — un literal con signo como objeto de WHEN

En WHEN -0.000020 THRU 0.000020 el - inicial hacía que el parser leyera el objeto como el comienzo de una condición, con lo que reportaba expected comparison operator in condition y luego se atragantaba con el THRU.

• Corregido — PERFORM ... TIMES con un item de datos como contador

Post añadido a las 08:43. Post anterior a las 08:23

COBOL-85 admite un identificador como contador de repetición, no solo un literal: con 77 THREE PIC 9 VALUE 3., la sentencia PERFORM PFM-C THREE TIMES. ahora es válida.

• Corregido — una cláusula cuyo contador salta a la línea siguiente

Un número que abre una línea se toma por un número de nivel — y normalmente lo es —, así que en 10 STUFF-1 OCCURS seguido de 31 TIMES. en la línea siguiente, el 31 llegaba como número de nivel y OCCURS reportaba "expected integer after OCCURS". Donde la gramática exige un entero y un número de nivel jamás podría aparecer, ambas escrituras se leen ahora como el mismo número. Lo mismo ocurría con un contador de PERFORM ... TIMES escrito en su propia línea.

• Conformidad NIST COBOL-85: 303 -> 317 de 434 programas dentro de alcance

El módulo de Intrinsic Functions está ahora completo: 45 / 45. Progresión de PASS dentro de alcance: 1.62.13, 303; frases de CLOSE, WHEN con signo y PERFORM TIMES con identificador, 314; contadores enteros en línea de continuación, 317. Movimiento por módulos: IF 40 -> 45, IX 38 -> 40, ST 30 -> 32, SQ 50 -> 52, NC 56 -> 58, RL 31 -> 32.

=== PowerRustCOBOL 1.62.15 — 2026-08-26 ===

• Añadido — literales de bloque en formato libre (```)

Una extensión del lenguaje, no COBOL-85: el estándar no tiene literal multilínea alguno — la continuación es un mecanismo de columnas del formato fijo —, así que el fuente en formato libre no podía escribir uno, ni escribir un literal lleno de comillas sin duplicar cada una de ellas. Un literal de bloque se delimita como un bloque de código de Markdown; el texto son las líneas entre las cercas, tomadas tal cual:


MOVE
```
Hello, World!
```
TO WS-GREETING.


WS-GREETING recibe Hello, World!. El texto empieza en la línea siguiente a la cerca de apertura (lo que siga a ``` en esa línea es una etiqueta, como en Markdown), la línea de la cerca de cierre no es texto, y tampoco lo es el salto de línea anterior a ella — así que un bloque de una sola línea se comporta exactamente igual que un literal entre comillas. Los saltos de línea interiores se conservan y nada necesita escaparse, que es lo que hace legibles el JSON, el SQL y el HTML embebidos (por ejemplo {"name": "O'Brien", "tags": ["a", "b"], "ok": true} bajo una etiqueta json). Solo en formato libre: el formato fijo tiene columna indicadora y área de secuencia, así que una línea de acentos graves significa allí otra cosa y se rechaza con un diagnóstico que lo dice.

• Corregido — un nombre de FUNCTION desconocido es ahora un error de compilación

Una intrínseca que RustCOBOL no implementa antes registraba un aviso y devolvía 0, de modo que una errata producía una respuesta erróneamente segura que nada reportaba. COMPUTE WS-X = FUNCTION SQRTT(4). ahora no compila, con el mensaje 'SQRTT' is not an intrinsic function RustCOBOL implements — did you mean FUNCTION SQRT?

El conjunto implementado (53 funciones) se lista una sola vez, en cobolt-ast, y una prueba afirma en ambas direcciones que coincide con lo que implementa el intérprete — un nombre listado pero no implementado compilaría y luego fallaría en ejecución, y un nombre implementado pero no listado se rechazaría aunque funciona. Esa segunda dirección no es hipotética: VARIANCE faltaba en el primer borrador de la lista y dos programas NIST que funcionaban dejaron de compilar hasta que se escribió la prueba.

• Corregido — una palabra definida por el usuario puede empezar por un dígito

Post añadido a las 08:46. Post anterior a las 08:43

COBOL-85 forma una palabra con A-Z, 0-9 y el guion; solo un data-name debe contener al menos una letra, y un nombre de párrafo o de sección no la necesita. No parseaba ninguno de estos casos: 01 25COUNT PICTURE 99., 01 3-DEM-TBL REDEFINES 3-DIMENSION-TBL. ni 0 SECTION. 25COUNT se descomponía en el entero 25 y la palabra reservada COUNT, y por eso el diagnóstico nombraba una palabra clave que no estaba en ninguna parte del fuente. 3-DEM-TBL es el caso que merece mención: 3- podría leerse como "tres menos...", y el estándar lo resuelve a favor de la palabra porque un operador aritmético debe llevar espacios alrededor — la misma regla que ya hacía de B-C una sola palabra y de B - C una resta.

• Corregido — una línea de depuración es un comentario salvo que el programa lo pida

Una D en la columna 7 marca una línea de depuración. El valor por defecto de COBOL-85 es que sea un comentario; solo se compila bajo SOURCE-COMPUTER. XYZ WITH DEBUGGING MODE. Los dos preprocesadores de formato fijo discrepaban al respecto — la vía estricta trataba la D como comentario, la relajada la compilaba incondicionalmente —, así que el mismo texto significaba cosas distintas según cuál lo leyera. WITH DEBUGGING MODE no estaba implementado en absoluto. Solo formato fijo: el formato libre no tiene área indicadora, así que una D allí es una palabra COBOL corriente.

• Corregido — una aplicación compilada no podía leer su propio programa embebido

El fallo decía: deserialize embedded AST: Custom("invalid value: integer `10`, expected variant index 0 <= i < 10"). Una aplicación compilada lleva su programa como un AST serializado con bincode, y bincode identifica una variante de enum por su ordinal. Añadir una variante en medio de Expr renumeraba todas las variantes posteriores, de modo que un programa escrito por una build no podía ser leído por otra — y el fallo afloraba al arrancar la aplicación, mucho después de la build que lo causó.

La variante se movió al final del enum, donde toma un ordinal nuevo y deja intactos todos los existentes, y el enum lleva ahora un comentario que explica por qué eso importa. Un desajuste que aún puede darse (añadir un campo también cambia el layout) reporta ahora la causa y el remedio — rebuild the application with the current toolchain — en lugar del mensaje propio de bincode.

• Corregido — un nombre de formulario que no es una palabra COBOL

derive_paragraph_name se llama con el nombre del formulario para los eventos a nivel de formulario, y un nombre de formulario no se valida en ninguna parte. Un formulario llamado MAIN'FORM producía el manejador MAIN'FORM--ONLOAD, emitido a la vez como PROGRAM-ID anidado y como el CALL que lo alcanza — y ninguno de los dos puede contener un apóstrofo. Un nombre que ya es una palabra COBOL se devuelve sin cambios, así que cada .cfrm en disco conserva el nombre de párrafo que tiene almacenado.

• Conformidad NIST COBOL-85: 317 -> 332 de 434 programas dentro de alcance

Progresión de PASS dentro de alcance: 1.62.14, 317; FUNCTION desconocida convertida en error de compilación y palabras que empiezan por dígito, 331; VARIANCE restaurada a la lista de implementadas, 332. Segmentation 0 -> 10, Nucleus 58 -> 61, Relative 32 -> 34.

• Añadido — puntuación de ejecución, y lo que encontró

Todas las cifras de conformidad anteriores miden compilación: si el front end acepta el programa. nist_conformance run ahora ejecuta cada programa que compila y lee el informe PASS/FAIL del propio programa, que es lo que la suite CCVS85 existe para producir.

De los 434 programas dentro de alcance, 0 llegan al final de su ejecución. 101 no compilan; de los 332 que sí compilan, 170 entran en bucle hasta que se les mata por exceso de salida, 76 agotan el tiempo, 66 no imprimen informe alguno y 21 son rechazados por el runtime. Esa brecha — 332 compilan, 0 funcionan — es el estado honesto del runtime frente a la suite, y ahora está medida en lugar de sospechada.

=== PowerRustCOBOL 1.62.16 — 2026-08-27 ===

• Corregido — el AT de AT END es opcional

Post añadido a las 08:55. Post anterior a las 08:46

COBOL-85 escribe la frase como [AT] END, así que RETURN SORTFILE RECORD AT END GO TO EOF-PARA. y RETURN SORTFILE END GO TO EOF-PARA. son la misma sentencia. Solo se aceptaba la primera. La segunda dejaba la frase sin consumir, y la sentencia entonces seguía de largo y se tragaba el encabezado del párrafo siguiente — con lo que cada GO TO que apuntara a ese párrafo lo reportaba como no declarado, en una línea lejana a la causa. CCVS85 escribe ambas ortografías a propósito, una tras otra, etiquetadas "WITH ALL OPTIONAL WORDS" y "WITHOUT OPTIONAL WORDS".

NOT AT END ya aceptaba la forma sin AT; las dos mitades de la misma regla discrepaban. Este era el mayor defecto individual que quedaba en la suite: 33 programas.

• Corregido — la palabra COPY dentro de un literal no es una directiva

El preprocesador de COPY/REPLACE recorría un literal de cadena hasta la siguiente comilla en cualquier punto del archivo, con lo que una sola comilla sin pareja cambiaba la paridad de todos los literales posteriores y dejaba prosa corriente expuesta como texto de fuente. En CCVS85 eso hacía que el rótulo de copyright pareciera una directiva COPY y corrompía cuatro programas por lo demás limpios:


02 FILLER PIC X(40) VALUE
"CCVS74 NCC COPY, NOT FOR DISTRIBUTION.".


Un literal termina ahora en su línea y un delimitador duplicado es contenido — exactamente lo que el léxico hace desde 1.62.12. El preprocesador nunca tuvo ninguna de las dos reglas.

• Corregido — un literal numérico puede abrir una lista de operandos con su punto decimal

ADD y SUBTRACT delimitaban su lista de operandos emisores con Token::Period, así que en SUBTRACT SUBTR-4 SUBTR-5 .499 FROM SUBTR-2 GIVING SUBTR-11. o en ADD .3 TO PERFORM4. la sentencia terminaba en el . de .499 y 499 FROM ... se leía como una sentencia nueva — reportado como unexpected token: IntegerLiteral(499), señalando a los dígitos en lugar de al punto. La adyacencia es la regla, como en el resto del compilador: COBOL-85 exige un espacio tras el punto de fin de sentencia, así que un punto pegado a dígitos solo puede ser un punto decimal. Solo cambió el lado emisor — MOVE X TO Y.5 sigue siendo un error de compilación, según la resolución vigente, porque un receptor no puede empezar con punto decimal.

• Conformidad NIST COBOL-85: 332 -> 376 de 434 programas dentro de alcance

Progresión de PASS dentro de alcance: 1.62.15, 332; [AT] END con el AT opcional, 365; biblioteca COPY conectada al arnés más la corrección de literales anterior, 372; punto decimal inicial en lista de operandos, 376. Indexed I/O está ahora completo: 42 / 42 — el segundo módulo terminado, tras Intrinsic Functions. Sequential I/O 52 -> 77, Sort/Merge 32 -> 36, Source Text Manipulation 4 -> 13, Nucleus 57 -> 64.

• El arnés ahora expande COPY, como rcrun siempre ha hecho

CCVS85 incluye sus 51 copybooks dentro de la misma distribución, como miembros CLBRY. El arnés de conformidad tokenizaba sin ejecutar nunca el preprocesador, así que el módulo Source Text Manipulation — cuyo tema completo es COPY y REPLACE — no podía medirse en absoluto. Ahora escribe la biblioteca a disco y expande a través de cobolt_lexer::expand_copybooks, la misma vía que usa rcrun.

=== PowerRustCOBOL 1.62.17 — 2026-08-27 ===

• Añadido — LINAGE: la página impresa, y AT END-OF-PAGE

Un archivo de informe puede ahora declarar la disposición de su página, y un WRITE puede preguntar si ha alcanzado el pie de esa página:


FD PRINT-FILE
LINAGE IS 50 LINES
WITH FOOTING AT 45
LINES AT TOP 10
LINES AT BOTTOM 6.
...
WRITE PRINT-REC BEFORE ADVANCING 1 LINE
AT END-OF-PAGE PERFORM PAGE-TRAILER
NOT AT END-OF-PAGE CONTINUE
END-WRITE.


Post añadido a las 08:59. Post anterior a las 08:55

COBOL-85 divide la página en un margen superior, un cuerpo de n líneas y un margen inferior. LINAGE-COUNTER cuenta desde 1 las líneas escritas en el cuerpo; alcanzar el FOOTING levanta la condición de fin de página, que es como un informe sabe que debe imprimir su pie. ADVANCING PAGE inicia una página nueva y reinicia el contador. Omitir FOOTING lo deja por defecto en el tamaño del cuerpo, de modo que la condición espera a la página completa. AT EOP se acepta como la ortografía corta, y el AT es opcional en ambas. El parser de FD antes se saltaba la cláusula LINAGE completa y AT END-OF-PAGE era un error de parseo, así que un programa de informes no podía compilar.

Parsearlo sin más habría sido peor que dejarlo roto: la frase habría compilado y no se habría disparado nunca — una respuesta errónea en silencio en lugar de un fallo honesto —, así que el contador está implementado, no simulado. Un archivo sin cláusula LINAGE no tiene página, y su AT END-OF-PAGE, correctamente, no se levanta jamás.

• Conformidad NIST COBOL-85: 376 -> 380 de 434 programas dentro de alcance

Sequential I/O 77 -> 81. Los cuatro programas de informe de SQ que quedaban (SQ201M, SQ208M, SQ209M, SQ401M) esperaban todos esto.

=== PowerRustCOBOL 1.62.18 — 2026-08-27 ===

• Corregido — un número que abre una línea de continuación es un operando

Un número al comienzo de una línea se clasifica como número de nivel, y normalmente lo es. Pero una sentencia cuya lista de operandos salta a la línea siguiente pone un entero corriente exactamente en esa posición:


SUBTRACT DNAME-1
1 FROM ERROR-COUNTER.
WRITE PRINT-REC FROM OVERPRINTED-LINE AFTER
000000000000000001 LINE.
DISPLAY "NUMERIC LITERALS "
21 SPACE 35 I-DATA


El léxico no puede distinguir los dos casos — un número de nivel solo se reconoce por el contexto —, así que el parser acepta ahora esa escritura allí donde se espera una expresión. Eso es exacto y no laxo: la DATA DIVISION casa sus entradas antes de que se parsee expresión alguna, de modo que un número de nivel real nunca llega al parser de literales. Vale 8 programas.

• Corregido — el IS es opcional en una condición de clase o de signo

COBOL-85 escribe por igual IF X IS NUMERIC e IF X NUMERIC. Solo se aceptaba la ortografía con IS, así que la condición terminaba en el nombre del dato; casos como IF IF-D8 POSITIVE PERFORM PASS ELSE PERFORM FAIL., IF WS-FIELD NUMERIC ... o IF WS-FIELD NOT NUMERIC ... parsean ahora. Un NOT inicial se consume solo cuando de verdad le sigue una palabra de clase o de signo, así que a NOT = b sigue siendo una comparación negada.

• Corregido — una condición puede ser sujeto de un EVALUATE

EVALUATE parseaba su sujeto como una expresión simple, así que en EVALUATE WRK-XN-00001-1 NUMERIC con sus ramas WHEN TRUE y WHEN FALSE y su END-EVALUATE, se detenía en el nombre del dato y NUMERIC se leía como una sentencia — que entonces se tragaba las ramas WHEN y el END-EVALUATE.

• Corregido — un nombre de procedimiento puede escribirse solo con dígitos

COBOL-85 exige un carácter alfabético en un data-name, pero no en un nombre de párrafo o de sección: PERFORM 00., GO TO 50., el párrafo 00. y el encabezado 50 SECTION. no parseaban ni como referencias ni como encabezados. El caso del encabezado necesitaba además que el escáner de listas de sentencias reconociera un nombre numérico como frontera — sin eso, las sentencias del párrafo anterior se tragaban el encabezado.

• Conformidad NIST COBOL-85: 380 -> 391 de 434 programas dentro de alcance

Más del 90 % de la suite dentro de alcance. Nucleus 64 -> 69, Segmentation 10 -> 12, Sort/Merge 36 -> 38.

=== PowerRustCOBOL 1.62.19 — 2026-08-27 ===

Conformidad NIST COBOL-85: 391 -> 396 de 434 programas dentro de alcance (91.2 %). Nucleus 69 -> 74; ningún módulo retrocedió.

• Corregido — un item numérico editado es un item numérico

Post añadido a las 09:05. Post anterior a las 08:59

Un item numérico editado es un receptor legal para COMPUTE, ADD, SUBTRACT, MULTIPLY y DIVIDE ... GIVING — editar el resultado es la razón de declararlo. Dos defectos independientes hacían que un receptor correcto pareciera no numérico, y el analizador rechazaba código válido con "'DIV9' is not numeric; DIVIDE GIVING requires a numeric receiver":


01 DIV9 PICTURE IS ZZ,ZZZ.9.
01 WRK-NE-6 PIC $**.**CR.
DIVIDE GROSS BY 12 GIVING DIV9.
SUBTRACT TAX FROM GROSS GIVING WRK-NE-6.


Primero, el punto decimal de edición se tragaba su dígito. Es la misma ambigüedad que 1.62.18 encontró en una línea de continuación, en un lugar nuevo: un número que sigue a un punto se clasifica como número de nivel, porque ahí es donde normalmente empieza el siguiente item de datos. Dentro de una PICTURE no lo es, así que la plantilla se truncaba en el punto — ZZ,ZZZ.9 quedaba en ZZ,ZZZ —, perdiendo el dígito y, con él, la categoría numérica del item. Ahora deciden los spans, exactamente como ya lo hacen para B-C frente a B - C: solo un dígito pegado al punto forma parte de la picture, de modo que el siguiente item de datos no se traga jamás. ZZ,ZZZ.99 nunca tuvo el problema, porque 99 no es un número de nivel.

Segundo, una picture no necesita llevar ningún 9: Z, * y un $, + o - flotantes son posiciones de dígito por derecho propio, así que ZZZZ, $.** y $**.**CR son numéricos editados; antes caían en el fallback alfanumérico. El recuento de dígitos sigue ahora la misma regla con la que el runtime da formato, de modo que la clasificación y la salida editada no pueden discrepar.

=== PowerRustCOBOL 1.62.20 — 2026-08-27 ===

• Corregido: PERFORM a THRU b es un rango de párrafos, no un bloque de sentencias
PERFORM BAIL-OUT THRU BAIL-OUT-EX aplanaba los párrafos del rango en una sola lista de sentencias y ejecutaba esa lista. El aplanado pierde la identidad de los párrafos, de modo que un GO TO que nombraba un párrafo dentro del rango no podía aterrizar allí: escapaba al controlador de nivel superior, que reanudaba la ejecución secuencial más allá del rango y nunca regresaba al llamador.


PRINT-DETAIL.
...
ELSE PERFORM BAIL-OUT THRU BAIL-OUT-EX.
BAIL-OUT.
IF CORRECT-A EQUAL TO SPACE GO TO BAIL-OUT-EX.
BAIL-OUT-WRITE.
...
BAIL-OUT-EX. EXIT.


El GO TO BAIL-OUT-EX de arriba es la ruta ordinaria, no un caso límite. El control caía a través de BAIL-OUT-EX, atravesaba los párrafos que hay detrás y entraba en la propia sección de pruebas del programa — que volvía a ejecutar las pruebas, las imprimía de nuevo y no se detenía jamás. Un rango ahora se ejecuta párrafo a párrafo: un GO TO dentro del rango transfiere el control dentro de él, un GO TO que sale del rango sigue propagándose como exige COBOL, y el PERFORM retorna cuando termina el último párrafo.

Ésta era la razón sistémica por la que los programas NIST CCVS85 no se ejecutaban. La construcción está en el código común que comparten todos los programas CCVS, en la ruta que recorre cada prueba que pasa, así que cualquier programa con una prueba superada quedaba en bucle infinito. NC104A pasó de producir salida sin límite a terminar e informar 199 PASS / 30 FAIL.

=== PowerRustCOBOL 1.62.21 — 2026-08-27 ===

NIST CCVS85 Nucleus (NC): compilación 76 -> 92 de 95, ejecución 16 -> 28 de 95. Dos números separados, siempre. La compilación cuenta los programas que el front end acepta; la ejecución cuenta los programas que corren hasta el final e informan cero fallos en su propio informe CCVS — la afirmación estrictamente más fuerte, y la que significa que el programa funciona. Ningún otro módulo fue tocado (GOLDEN RULE #9).

Eslopes
31 de agosto de 2026, 14:18
• Corregido: una ocurrencia de una tabla de grupos no tenía almacenamiento propio
MOVE VALUES-1 TO GRP-1 (IN1) escribía un escalar en un slot llamado GRP-1(1), y MOVE ELEM1 (1, 1) TO TEMP leía el slot propio del hijo, que nada había escrito. Una ocurrencia de grupo es ahora las ocurrencias correspondientes de sus ítems subordinados: escribir GRP-1 (2) se distribuye entre ELEM1 (2,1) … ELEM1 (2,4), y leerla concatena exactamente ésas. Sin subíndice, el mismo recorrido entrega al registro 01 que lo engloba los bytes de todas las ocurrencias, de modo que MOVE GRP-TAB1 TO GRP-TAB2 copia una tabla entera en vez de un único slot plantilla.

• Corregido: un literal con signo que abre el siguiente subíndice se leía como una suma
MOVE ELEM1 (IN1 +3) TO TEMP es ELEM1 (IN1, 3): la coma separadora es opcional, y un signo pegado a sus dígitos es un literal con signo. Se leía como IN1 + 3, un solo subíndice, que caía en una ocurrencia que nunca se había escrito. La indexación relativa — ELEM1 (IN1 - 1, 3), con el operador espaciado por ambos lados — no cambia, y tampoco TBL (I+1), donde el signo está pegado por ambos lados y los programas ya escritos así significan aritmética.

• Corregido: un MOVE numérico no truncaba los dígitos de orden alto
01 M PIC 99V999. MOVE 123.45 TO M. dejaba 123.450. El extremo de orden bajo ya se recortaba con el reescalado; el extremo de orden alto se trunca con el mismo silencio según el estándar, y ahora se hace. La aritmética sigue comprobando la capacidad del receptor antes de almacenar, de modo que una sentencia con ON SIZE ERROR conserva su valor anterior y una sentencia sin ella trunca.

• Corregido: las posiciones de escalado decimal P se ignoraban
PIC S999PP contiene tres dígitos que representan centenas y PIC PP99 dos que representan diezmilésimas: P es una posición de dígito que el ítem abarca pero no almacena. Se omitía por completo, así que MOVE 12300 TO IF-D15 almacenaba 300 una vez en vigor el truncado, y ADD … GIVING N-42 sobre PIC 9(3)P(4) daba 8888888 donde el estándar exige 8880000. El escalado ahora cuenta para la capacidad del ítem y para su escala, y las posiciones que las P representan se leen como cero. El trazado del registro no cambia — esas posiciones no ocupan bytes.

• Corregido: REDEFINES era una copia única, no una superposición viva
Una escritura a través de una descripción era invisible a través de la otra, y un ítem redefinidor se contaba en los bytes de su grupo, dejando el grupo el doble de ancho que su almacenamiento. Las descripciones que comparten bytes ahora se mantienen sincronizadas: una escritura en cualquier punto de una se vuelve a renderizar en las demás, y sólo el destino contribuye al ancho del grupo. Sobre esto se construye cada detalle de fallo de CCVS (COMPUTED-N REDEFINES COMPUTED-A, impreso a través del grupo que los engloba). Las superposiciones de más de 256 slots de almacenamiento expandidos conservan el antiguo almacenamiento por descripción — refrescar una tabla redefinida de 10×10×10 en cada escritura recorre mil ocurrencias dos veces y hacía el programa inejecutable.

• Corregido: DIVIDE entre cero abortaba la ejecución
La división entre cero es la condición de error de tamaño para DIVIDE. Ahora ejecuta ON SIZE ERROR cuando lo hay, suprime NOT ON SIZE ERROR en ambos casos y deja los receptores intactos. Abortar, en cambio, terminaba el programa a mitad del informe; NC251A prueba exactamente esto y no imprimía nada en absoluto.

• Corregido: una descripción redefinidora más ancha que su destino provocaba un pánico
deserialize_decl acotaba sólo el final de su ventana de bytes, de modo que un cursor situado más allá del final del almacenamiento producía start > end y el slice entraba en pánico (range start index 58 out of range for slice of length 57).

Post añadido a las 09:10. Post anterior a las 09:07

• Corregido: gramática que el Nucleus escribe y el parser rechazaba
- ALTER es una serie: ALTER a TO PROCEED TO b, c TO PROCEED TO d parseaba un solo par. Los pares extra se encolan, con lo que Stmt::Alter sigue siendo un único par y el AST serializado queda intacto; los destinos pasan por el lector de nombres de procedimiento, así que funcionan los nombres de sólo dígitos.
- El GO TO alterado: GO TO. sin destino parsea, cae al párrafo siguiente mientras no está alterado, y salta en cuanto un ALTER nombra un destino.
- Los nombres de procedimiento de sólo dígitos conservan sus ceros iniciales: 00001 y 000001 son párrafos distintos; ambos se convertían en 1 y colisionaban.
- Una condición que no es un identificador simple: un nombre de condición puede ir subindicado (IF FIRSTZ (1)) o cualificado (IF A OF IF-D32); los subíndices seleccionan la ocurrencia del ítem anfitrión contra la que se prueban los VALUE.
- Un operando aritmético entre paréntesis: UNTIL (WRK + 12) = 100 y WHEN (33 + (99 - 43)) se leían como condiciones anidadas.
- Objetos de WHEN: una expresión aritmética, y un rango entre dos ítems de datos (WHEN WRK-A THRU WRK-B), son ahora objetos de selección.
- GREATER THAN OR EQUAL TO: el OR EQUAL puede situarse a cualquiera de los dos lados del THAN opcional.
- Relaciones combinadas abreviadas: un literal seguido de un operador relacional es un sujeto, no un objeto de abreviatura; el objeto puede ser una expresión aritmética (OR CCON-3 - 1) o una prueba de clase o de signo (AND SIGN-1 ZERO); AND IS NOT LESS THAN x conserva su IS opcional; NOT < tras OR es una abreviatura; y el sujeto que una abreviatura reutiliza puede estar más a la izquierda que el término al que se une (a = b OR c AND "d").
- Ligadura de ELSE: un imperativo de ON SIZE ERROR y la rama ELSE propia de un IF anidado se detienen ambos en el ELSE / END-IF / WHEN / END-PERFORM / END-SEARCH de la sentencia que los engloba.
- Serie de receptores de MULTIPLY / DIVIDE formato 1: MULTIPLY a BY b ROUNDED c ROUNDED d — cada receptor se multiplica por su propio valor actual, así que se encolan como sentencias separadas en lugar de plegarse en GIVING.
- Orden de frases y formas de PERFORM: WITH TEST BEFORE puede preceder a VARYING; el contador de repeticiones puede ser un ítem subindicado (PERFORM P TABLE5-NUM (INDEX5) TIMES); PERFORM imperativo … END-PERFORM sin frase alguna ejecuta su cuerpo una vez; un nombre de párrafo puede cualificarse por su sección (PERFORM PAR-1A OF QUAL-SECTION-1).
- INSPECT: la categoría ALL/LEADING/TRAILING se propaga a los operandos que la siguen; CONVERTING … BEFORE/AFTER INITIAL restringe la región convertida.
- UNSTRING: TALLYING IN se acepta después de WITH POINTER, que es el orden en que lo escribe el estándar.
- Un ítem RENAMES de nivel 66 como receptor aritmético: no tiene PICTURE propio, así que su categoría es desconocida y no no-numérica, y ADD 3500 TO RENAME-12 ya no se rechaza.

=== PowerRustCOBOL 1.62.22 — 2026-08-27 ===

NIST CCVS85 Nucleus (NC): compilación 92 -> 93 de 95, ejecución 28 -> 36 de 95. Dos números separados, siempre. La compilación cuenta los programas que el front end acepta; la ejecución cuenta los programas que corren hasta el final e informan cero fallos en su propio informe CCVS. Las aserciones que los propios programas informan pasaron de 3 302 PASS / 727 FAIL a 3 915 PASS / 540 FAIL — el 87.9 % de 4 455 puntuadas. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Corregido: una frase condicional se tragaba el resto del párrafo
ON SIZE ERROR, AT END, INVALID KEY, ON OVERFLOW, ON EXCEPTION y todos los demás imperativos condicionales leían de largo el punto que termina la oración, de modo que cada oración siguiente pasaba a formar parte de la frase y se ejecutaba sólo cuando la condición se disparaba:


DIVIDE A INTO B GIVING C ON SIZE ERROR MOVE "P" TO XRAY.
DISPLAY XRAY.


Post añadido a las 09:12. Post anterior a las 09:10

El DISPLAY — y todo lo que le sigue — sólo se ejecutaba ante un error de tamaño. Un cuerpo con ámbito ahora termina en el punto, que es donde COBOL-85 lo termina; el cuerpo de un párrafo sigue leyendo tantas oraciones como quiera. TRY … CATCH … END-TRY encierra oraciones, no un imperativo, y no cambia.

• Corregido: DIVIDE a INTO b dividía en el sentido equivocado
BY e INTO nombran sus operandos en órdenes opuestos — DIVIDE dividendo BY divisor pero DIVIDE divisor INTO dividendo — y sólo BY se respetaba. Así, DIVIDE 5 INTO 20 GIVING C almacenaba 0.25, y en la forma con receptor DIVIDE 5 INTO B intentaba almacenar el cociente en el literal 5 y dejaba B intacto. Una serie de receptores ahora divide cada receptor entre el divisor: DIVIDE 2 INTO A B reduce ambos a la mitad.

• Corregido: REMAINDER usaba el cociente entero
COBOL-85 calcula el resto a partir del cociente tal como queda almacenado en el receptor de GIVING — truncado al PICTURE de ese ítem, y truncado incluso cuando el propio cociente llevaba ROUNDED. DIVIDE 7 INTO 23 GIVING C REMAINDER R con C PIC 999V99 da C = 3.28 y R = 0.04, no R = 2.00.

• Corregido: todos los registros 01 de un mismo FD tenían almacenamiento separado
Un FD tiene una sola área de registro por muchas entradas 01 que la describan. Cada una conservaba sus propios bytes, así que un valor movido a través de un registro era invisible a través de otro. Cada programa CCVS85 construye su línea de informe en PRINT-REC y la escribe como DUMMY-RECORD: todos los encabezados de página salían en blanco, y el bloque de salto de página escribía la línea retenida una vez por sentencia en lugar de una sola vez — seis copias idénticas de la misma línea de detalle, que es lo que empujó a varios programas más allá del tiempo límite de ejecución.

• Corregido: PERFORM <section-name> no ejecutaba nada
Un encabezado de sección no lleva sentencias propias; es los párrafos que lo siguen, hasta el siguiente encabezado. PERFORM encontraba la entrada vacía del encabezado y retornaba de inmediato. Las secciones ahora se expanden a su rango de párrafos, THRU con un nombre de sección termina en el último párrafo de esa sección, y el rango conserva la identidad de los párrafos, de modo que un GO TO dentro de él permanece dentro.

• Corregido: la cualificación se detenía tras un nivel
A OF B OF C conservaba sólo el último cualificador: la cadena creciente se colocaba donde sólo cabe un nombre simple, y todos los niveles menos uno se descartaban. Ahora se resuelven hasta los 49 niveles del estándar, de modo que TBL-ITEM-1 OF TABLE-LEVEL-1A IN TABLE-LEVEL-2A OF TABLE-LEVEL-3A alcanza el ítem que nombra y no el duplicado que se declaró primero. Un subíndice escrito antes de los cualificadores (TBL-ITEM (I) OF GRP) se conserva.

• Corregido: las cláusulas VALUE de nivel 88 perdían el resto de la DATA DIVISION
Cuatro defectos en una sola cláusula, cada uno de los cuales desincronizaba el parser y descartaba en silencio todas las entradas posteriores: VALUES ARE 2 THRU 4 — la cópula en plural no se aceptaba; VALUE IS +12.34 y VALUE 100 THRU 128 -9 THRU -2 — un signo inicial no se plegaba en el literal; VALUE IS .01, .11, .21 — un punto que abre un literal numérico se leía como el fin de la entrada (COBOL-85 sólo prohíbe el punto decimal final); y un valor que abre una línea (16 THRU 20 continuado de la línea anterior) se tomaba por el número de nivel de la entrada siguiente.

• Corregido: PIC 999999999999.. perdía su punto decimal de edición
Dos puntos seguidos: el primero pertenece a la imagen, el segundo termina la entrada. Ambos se leían como terminadores, lo que costaba a la plantilla su punto decimal y dejaba un punto suelto que desincronizaba la DATA DIVISION.

• Corregido: ROUNDED no redondeaba hacia un receptor de edición numérica
La escala del receptor se leía del valor que casualmente contenía, y un ítem de edición numérica contiene caracteres editados. MULTIPLY .9 BY 80.12 GIVING <PIC $$$$.99> ROUNDED editaba el $72.10 truncado en lugar de $72.11. La escala ahora procede del PICTURE.

Post añadido a las 09:13. Post anterior a las 09:12

• Corregido: ADD/SUBTRACT CORRESPONDING ignoraban ON SIZE ERROR y ROUNDED
Ambas frases se parseaban y se descartaban. La condición de error de tamaño pertenece a la sentencia, no a una pareja: un receptor que desborda queda sin cambios, los demás siguen recibiendo sus resultados, y el único imperativo se ejecuta una sola vez.

• Corregido: una condición entre paréntesis redundantes se leía como aritmética
Sólo el nivel de anidamiento más externo se inspeccionaba en busca de un operador relacional o lógico, así que ((((((0 - CONT-D EQUAL TO CONT-D OR -11 + CONT-F)))))) se tomaba por una única expresión aritmética. Ahora también se acepta una relación abreviada cuyo objeto es una expresión aritmética que abre con un signo (… EQUAL TO CONT-D OR -11 + CONT-F).

• Añadido: nist_conformance fails <MODULE|NAME>
Una tercera pasada junto a strict y run: ejecuta cada programa e imprime los registros de detalle FAIL* que lleva su propio informe, con la pareja COMPUTED= / CORRECT = que nombra el defecto. Agrupar esos detalles por causa a lo largo de un módulo es como se encontraron las causas compartidas de arriba — el detalle de un programa es una anécdota; el de cuarenta programas, una categoría.

=== PowerRustCOBOL 1.62.23 — 2026-08-27 ===

NIST CCVS85 Nucleus (NC): compilación 93 -> 94 de 95, ejecución 36 -> 65 de 95. Dos números separados, siempre. La compilación cuenta los programas que el front end acepta; la ejecución cuenta los programas que corren hasta el final e informan cero fallos en su propio informe CCVS. Las aserciones que los propios programas informan pasaron de 3 915 PASS / 540 FAIL a 4 278 PASS / 226 FAIL. La compilación del conjunto completo queda ahora en 419 / 434. Ningún otro módulo fue tocado (GOLDEN RULE #9).

Veintinueve programas CCVS pasaron de fallar a limpios, entre ellos NC102A, NC106A, NC114M, NC119A, NC120A, NC124A, NC126A, NC170A, NC174A, NC176A, NC177A, NC203A, NC215A, NC218A, NC222A, NC224A, NC236A, NC238A, NC242A, NC243A, NC251A, NC253A y NC254A.

• Corregido: NOT ON SIZE ERROR por sí solo no protegía los receptores
COBOL-85 deja un receptor sin cambios cuando desborda y la sentencia lleva una frase de error de tamaño — cualquiera de sus dos mitades. Sólo se comprobaba ON SIZE ERROR, de modo que


ADD A B 6 C GIVING R1 R2 ROUNDED R3 NOT ON SIZE ERROR MOVE "A" TO FLAG.


escribía valores truncados en cada receptor que desbordaba en lugar de dejarlos en paz. Una regla, seis sentencias (ADD, SUBTRACT, MULTIPLY, DIVIDE, COMPUTE, las formas CORRESPONDING) — y seis programas CCVS.

• Corregido: un nombre de párrafo duplicado ejecutaba las sentencias del otro
Los cuerpos de los párrafos se indexaban por nombre, de modo que un segundo PFM-INIT-F2-5. sobrescribía las sentencias del primero mientras el primero conservaba su lugar en el programa. El primer párrafo ejecutaba entonces en silencio el código del segundo. Los cuerpos se mantienen ahora por posición para el flujo secuencial y los rangos THRU, y el índice de nombres conserva la primera definición para las búsquedas.

• Corregido: UNSTRING ignoraba la mayor parte de su propia sentencia
DELIMITER IN, COUNT IN, WITH POINTER y TALLYING se parseaban y luego se descartaban, y el origen se partía con str::split en lugar de escanearse. DELIMITER (la palabra de DELIMITER IN) no es la palabra clave DELIMITED, así que la frase se leía como otro ítem receptor más. UNSTRING ahora escanea desde el puntero, entrega una sola ocurrencia de delimitador bajo ALL, parte por los tamaños propios de los receptores cuando no se escribe DELIMITED BY, distribuye dentro de un receptor de grupo, e informa desbordamiento cuando los receptores se agotan antes que el origen.

Post añadido a las 09:15. Post anterior a las 09:13

• Corregido: edición numérica
- Una imagen cuyas posiciones de dígito están todas suprimidas deja en blanco el ítem entero cuando el valor es cero, punto decimal incluido: PIC ZZZ.ZZ y PIC ++++.++ se leen como espacios, PIC *,***.** como *****.**.
- Los símbolos de inserción flotante situados después del punto decimal son posiciones de dígito: PIC ++++.++ con 12 se lee " +12.00", no " +12.++".
- El símbolo flotante se sitúa inmediatamente a la izquierda del primer dígito mostrado, contado en posiciones de carácter — una coma de agrupación intermedia toma el signo, así que PIC --,---.-- con -123 se lee " -123.00".
- Un carácter de inserción simple dentro de la zona de supresión se sustituye por el carácter de supresión: PIC -*B*99 con -42 se lee -***42.
- Las posiciones de escalado P en un ítem editado reducen el valor a los dígitos que almacena: PIC ZZZPP que recibe 900 se lee " 9".
- PIC 090909 conserva ahora su cero inicial. El lexer había leído la plantilla como un número, así que la imagen salía un carácter más estrecha de lo que es.

• Corregido: MOVE
- Un grupo rellenado con una constante figurativa se rellena entero. MOVE ZERO TO <grupo> establecía sólo el primer campo y dejaba el resto como estaba, y MOVE ALL "ABC…" TO <tabla de 7 dimensiones> rellenaba la primera ocurrencia del OCCURS más externo.
- Des-edición: un emisor de edición numérica movido a un receptor numérico transfiere el valor que sus caracteres deletrean. PIC $(4)9.99CR con "$ 123.45CR" almacena -123.45.
- Un emisor numérico subindicado, cualificado o literal se des-edita como cualquier otro: MOVE FIELD (I) TO <PIC X(4)> y MOVE 2 TO <PIC X(4)> alinean a la izquierda.
- Un ítem numérico sin signo almacena el valor absoluto: PIC 9(18) que recibe un producto negativo contiene su magnitud.
- ROUNDED hacia un ítem con posiciones P redondea a esa posición: PIC S99P que recibe -99 contiene -100.

• Corregido: la modificación de referencia sobre un elemento de tabla
TABLE-1 (3 2) (2:5) leía el slot base de la tabla en lugar de la ocurrencia (3, 2), así que todo elemento de tabla con modificación de referencia volvía vacío.

• Corregido: DIVIDE … REMAINDER
El resto se forma después de que el cociente llega a su receptor, de modo que el propio subíndice del receptor del resto ve el cociente nuevo (DIVIDE 100 BY N GIVING ANS REMAINDER WS-REM (ANS)), y el cociente se trunca al PICTURE del ítem de GIVING incluso cuando ese ítem es de edición numérica.

• Corregido: INSPECT … LEADING / TRAILING
Ambos contaban caracteres que aparecen en el patrón en lugar de ocurrencias contiguas del patrón completo, así que FOR LEADING "AH" sobre "AH YES AH YES" contaba 2. REPLACING LEADING además reexaminaba la misma posición tras cada sustitución y no terminaba nunca cuando BY era igual al patrón.

• Añadido: JUSTIFIED RIGHT
Hasta ahora se parseaba y se ignoraba. Un receptor alfanumérico declarado JUSTIFIED alinea el emisor en su extremo derecho: un emisor corto se rellena por la izquierda y uno largo pierde sus caracteres más a la izquierda.

• Añadido: PICTURE alfanuméricos de edición
PIC XXBXX/XX, PIC ABA y PIC A/AA poseen sus caracteres de inserción: B imprime un espacio, 0 un cero, / una barra, y el emisor aporta sólo las posiciones X/A/9. PIC ABA además se medía como dos caracteres de ancho en lugar de tres.

• Añadido: switches de SPECIAL-NAMES y clases definidas por el usuario


SPECIAL-NAMES.
SWITCH-1 IS SW-1 ON STATUS IS ON-SWITCH-1
OFF STATUS IS OFF-SWITCH-1
CLASS ORDINAL-A-D IS "A" THRU "D".


SET SW-1 TO ON, IF ON-SWITCH-1 e IF ITEM IS [NOT] ORDINAL-A-D funcionan todos. Un switch se establece desde fuera del programa — rcrun --switch SWITCH-1=ON (repetible) o COBOL_SWITCHES=SWITCH-1=ON,SWITCH-2=OFF — porque nada dentro de COBOL puede establecer uno antes de que empiece la ejecución.

Post añadido a las 09:18. Post anterior a las 09:15

• Corregido: PERFORM paragraph OF section ignoraba el cualificador
El cualificador de sección OF/IN se consumía y se descartaba, de modo que un nombre de párrafo repetido entre secciones ejecutaba siempre la primera definición del programa. Ahora selecciona la copia que está dentro de la sección nombrada. (GO TO paragraph IN section aún resuelve sin cualificar — véase la nota de traspaso.)

• Corregido: un receptor de edición numérica no podía provocar un error de tamaño
Un ítem editado contiene sus caracteres editados, así que no conserva entrada de capacidad y la condición de error de tamaño no podía surgir nunca para él: MULTIPLY 999999 BY 999999 GIVING <PIC $**.99> … ON SIZE ERROR … truncaba el producto dentro del campo en lugar de dejarlo intacto. Su capacidad procede ahora de las posiciones de dígito de la plantilla, exactamente igual que ya lo hacía su escala.

• Corregido: SEARCH … VARYING conducía el índice equivocado
VARYING id-2 sustituye al índice de búsqueda sólo cuando id-2 es un índice de esa misma tabla. En caso contrario, el primer índice propio de la tabla sigue conduciendo la búsqueda e id-2 se avanza en paralelo. Buscar siempre sobre id-2 dejaba el índice de la tabla donde empezó, así que un WHEN que subindicaba con ese índice nunca avanzaba y la búsqueda llegaba siempre a AT END.

• Añadido: nist_conformance report <NAME>
Imprime el informe CCVS completo de un programa, repartido de nuevo en sus registros de 120 bytes. fails muestra sólo el entorno de un FAIL*, lo que basta para agrupar una causa a lo largo de un módulo pero no para diagnosticar un programa.

=== PowerRustCOBOL 1.62.24 — 2026-08-27 ===

NIST CCVS85 Nucleus (NC): ejecución 65 -> 66 de 95, aserciones 226 -> 191 fallando. La compilación se mantiene en 94 / 95. Dos números separados, siempre — la compilación cuenta los programas que el front end acepta, la ejecución cuenta los programas que corren hasta el final e informan cero fallos en su propio informe CCVS. NC116A pasó de 23 fallos a limpio; NC252A pasó de 17 a 11 y NC217A de 11 a 5. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Corregido: SIGN IS … SEPARATE CHARACTER no cambiaba el almacenamiento
La cláusula se parseaba y se descartaba, de modo que PIC S9(5) SIGN IS LEADING SEPARATE ocupaba cinco caracteres en lugar de seis y nunca contenía el + o el - para el que la declaración reserva una posición. Un signo separado es una posición de almacenamiento que siempre está ocupada: MOVE 15759 TO un ítem así almacena ahora +15759, y MOVE -15759 almacena -15759, con el signo delante para LEADING y detrás para TRAILING. Escrita sobre un grupo, la cláusula alcanza a todo ítem DISPLAY numérico con signo subordinado que no lleve una propia, y un grupo anidado la redefine para su propio subárbol.

Un signo embebido (no SEPARATE) no cambia: el ítem sigue midiendo exactamente sus posiciones de dígito, que es lo que el overpunch definido por el implementador que fija el estándar deja observable.

DataDecl ganó un campo sign y cobolt_ast::data::SignClause es nuevo — ambos al final de sus declaraciones, según la regla de ordinales de bincode.

• Corregido: un REDEFINES subordinado se serializaba como almacenamiento extra
Dentro de un grupo, una entrada REDEFINES es otra lectura de los bytes de su hermano, no más bytes. El constructor de imágenes de REDEFINES la emitía además, así que cada campo posterior se desplazaba: 02 RDF3 REDEFINES RDFDATA3 insertaba una segunda copia de ALLDONXX66 en el registro, y la superposición de 36 elementos situada encima leía desplazada once bytes. El serializador ahora omite los hijos redefinidores, y el deserializador los rellena desde donde empezó su destino en lugar de desde los bytes que siguen.

Eslopes
31 de agosto de 2026, 14:25
• Corregido: un ítem con nombres de condición de nivel 88 no aportaba bytes
04 RDF3-5-15 PIC 9. con 88 HARD / 88 SOFT debajo era tratado como un grupo por el constructor de imágenes de REDEFINES, porque cualquier hijo convertía un ítem en grupo. Los nombres de condición nombran valores de un ítem, no campos dentro de él, así que el ítem desaparecía por completo de la imagen de su padre y conservaba su valor por defecto mientras el campo siguiente tomaba su byte. El mismo recuento erróneo dejaba a un ítem así fuera de la ordenación del RENAMES de nivel 66.

• Corregido: FILLER era invisible para el RENAMES de nivel 66
Un FILLER contiene bytes como cualquier otro ítem elemental, pero nunca se registraba en el orden de declaración que un rango de RENAMES rebana, de modo que un rango que abarcaba uno cerraba el hueco que ocupa: 66 RENAMES-TEST-3 RENAMES SUB-GRP-FOR-RENAMES-1 THRU ELEM-FOR-RENAMES-2 leía X123 en lugar de "X 123".

• Corregido: MOVE ALL literal a un receptor RENAMES escribía un solo carácter
Un receptor de nivel 66 abarca bytes reales exactamente igual que un grupo, y el literal repetido tiene que alcanzarlos todos. MOVE ALL "X" TO RENAME1 rellenaba un único carácter y dejaba los otros diecinueve como estaban.

=== PowerRustCOBOL 1.62.25 — 2026-08-27 ===

NIST CCVS85 Nucleus (NC): ejecución 66 -> 68 de 95, aserciones 191 -> 183 fallando. La compilación se mantiene en 94 / 95 y la del conjunto completo en 419 / 434. NC215A y NC219A pasaron ambos de fallar a limpios. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Añadido: ALPHABET y PROGRAM COLLATING SEQUENCE
SPECIAL-NAMES. ALPHABET <name> IS … y OBJECT-COMPUTER. … PROGRAM COLLATING SEQUENCE IS <name> se omitían ambos. Un programa que nombra un alfabeto ordena ahora todas las comparaciones alfanuméricas que hace por esa secuencia, y no por el orden nativo de caracteres.

La frase de literales es una lista ordenada de posiciones: un literal solo aporta cada uno de sus caracteres por turno, lit-1 THRU lit-2 se expande a una posición por carácter del rango, y ALSO pliega los operandos que une en una única posición compartida — que es lo que hace que "I" ALSO "J" comparen iguales. Cualquier carácter que el alfabeto no menciona se ordena después de todos los que sí menciona, en orden nativo entre ellos. Una constante figurativa usada como operando nombra su carácter nativo, así que ALSO HIGH-VALUE da a 0xFF la posición escrita. La cláusula también redefine las constantes figurativas: LOW-VALUE y HIGH-VALUE nombran los caracteres de los extremos de la secuencia del programa, de modo que bajo ALPHABET COLLATING-SEQ-1 IS "F" "U" "N" … el LOW-VALUE del programa es la letra F.

NATIVE, STANDARD-1 y STANDARD-2 son el orden nativo y no necesitan tabla. EBCDIC no está implementado y deja en vigor el orden nativo — registrado en la lista de carencias conocidas de docs/cobol85-supported-syntax-en.md. Program ganó alphabets y collating_sequence, y cobolt_ast::program::AlphabetSpec es nuevo — todos al final de sus declaraciones, según la regla de ordinales de bincode.

• Corregido: una cadena no numérica comparaba igual a cero
COBOL-85 (VI-15 4.5.4) hace alfanumérica una comparación en cuanto uno de los operandos es alfanumérico; el operando numérico se lee como sus caracteres. El runtime coaccionaba en cambio el lado de texto a f64, y un parseo fallido es 0.0 — así que IF WS-TEXT = 0 era verdadero para cualquier texto, IF "A" < 0 era falso e IF 9 < SPACE era falso. El texto que realmente es un número conserva la lectura numérica, de modo que IF WS-DIGITS = WS-NUM sigue comparando 42 con 42, y un ítem sin inicializar — que no tiene caracteres en absoluto — también la conserva, así que IF WS-PTR = NULL sobre un POINTER al que nunca se hizo SET no se ve afectado.

=== PowerRustCOBOL 1.62.26 — 2026-08-27 ===

NIST CCVS85 Nucleus (NC): aserciones 183 -> 181 fallando. La ejecución se mantiene en 68 / 95 y la compilación en 94 / 95. Ningún otro módulo fue tocado (GOLDEN RULE #9).

Post añadido a las 09:21. Post anterior a las 09:19

• Corregido: BLANK WHEN ZERO se ignoraba en un PICTURE sin edición
La cláusula se registraba sólo para los ítems de edición numérica, así que 77 DATA-F PICTURE IS 9(10) BLANK WHEN ZERO. con valor cero seguía leyéndose como diez caracteres 0 en lugar de diez espacios. Ahora se aplica a cualquier ítem DISPLAY numérico, que es lo que el estándar permite, y el blanqueo cubre el ítem entero — incluida una posición SIGN … SEPARATE, puesto que la cláusula deja en blanco el ítem y no sólo sus dígitos.

El valor almacenado sigue siendo numérico, así que la aritmética sobre un ítem así no cambia; sólo su forma de caracteres queda en blanco. Una comparación contra un operando alfanumérico lee esa forma de caracteres, que es lo que necesita IF DATA-F EQUAL TO " " (NC107A BZERO-TEST-1/BZERO-TEST-2).

Nota sobre el recuento de NC107A: los fallos del programa pasaron de 9 a 10 entre 1.62.25 y 1.62.26 aunque aquí se corrigieron dos de ellos. RDF-TEST-9 pasaba únicamente porque una cadena no numérica se coaccionaba a 0.0: RDFDATA18 contiene en realidad espacios, no "00000000000000", porque una descripción REDEFINES más ancha que el almacenamiento que redescribe no está respaldada más allá del final de su destino. Ese defecto es preexistente y ajeno a estos cambios — era invisible mientras toda cadena era igual a cero, y ahora es visible.

=== PowerRustCOBOL 1.62.27 — 2026-08-27 ===

NIST CCVS85 Nucleus (NC): ejecución 68 -> 69 de 95, aserciones 181 -> 172 fallando. NC225A pasa de 9 fallos a 0. La compilación se mantiene en 94 / 95. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Corregido: una palabra suelta en EVALUATE se leía como prueba de verdad, no como operando
Dos lecturas de una palabra suelta eran erróneas, y ambas fallaban en la misma dirección: hacían que un WHEN coincidiera cuando no debía. Como el error sólo añadía coincidencias, la mitad de esperar-igualdad de cada prueba pasaba por accidente y la funcionalidad parecía implementada.

Como objeto de selección: en EVALUATE 1234 WHEN WRK-DU-08V00 el objeto es un operando que se compara contra el sujeto. Se parseaba como condición, y un nombre suelto que no es un nivel 88 declarado caía al comportamiento de reserva de "verdadero si no es cero" — así que el ítem que contenía 78 coincidía con el sujeto 1234, y cualquier objeto distinto de cero coincidía con cualquier sujeto. Un objeto que es un nivel 88 declarado sigue siendo una condición; la ambigüedad se resuelve contra la tabla de símbolos exactamente como Condition::NameOrAbbrev ya la resuelve para las relaciones combinadas abreviadas.

Como sujeto de selección: en EVALUATE IT-IS-81 WHEN TRUE el sujeto es un sujeto condicional — selecciona según si la condición se cumple. Leído como ítem de datos, el nombre no resolvía a ningún slot, se evaluaba a 0 y WHEN TRUE nunca coincidía. Un sujeto suelto que nombra un nivel 88 declarado se reescribe ahora a sujeto condicional, que las ramas existentes de comparación de verdad ya manejan; la reescritura se omite por completo cuando ningún sujeto es un nombre de condición, así que el EVALUATE COBOL-CONTROL-ID del bucle de eventos generado no paga nada por evento. Juntas, estas dos correcciones resuelven el caso ALSO de seis sujetos de NC225A (EVA-TEST-GF-31), donde cinco columnas coincidían y la columna del nombre de condición no, de modo que la selección caía a una rama posterior. Pruebas nuevas: test_evaluate_selection.rs (9).

=== PowerRustCOBOL 1.62.28 — 2026-08-27 ===

NIST CCVS85 Nucleus (NC): ejecución 69 -> 71 de 95, aserciones 172 -> 151 fallando. NC247A y NC235A quedan en 0 fallos; NC216A pasa de 13 a 7. La compilación se mantiene en 94 / 95 (conjunto completo 419 / 434). Ningún otro módulo fue tocado (GOLDEN RULE #9).

Post añadido a las 09:22. Post anterior a las 09:21

• Corregido: INSPECT no leía nada en absoluto de un ítem de grupo
Un grupo no posee slot de valor propio — su valor se sintetiza a partir de sus ítems subordinados — e INSPECT leía su operando a través del almacén plano, así que veía la cadena vacía. INSPECT <grupo> TALLYING … contaba cero en todo grupo, incluidos los de longitud fija, y REPLACING escribía el resultado en un slot que el grupo no tiene. Ambas direcciones pasan ahora por los accesores de grupo.

Ésta era la mayor causa individual: además de NC247A, despejó seis fallos en NC216A.

• Corregido: un grupo de longitud variable tenía una sola longitud
Un grupo que contiene una tabla OCCURS … DEPENDING ON tiene dos longitudes, y cuál se aplica depende de la dirección de la referencia (VI-26 5.8.3 SR5). Usábamos el máximo declarado para todo.

Al enviar, sólo participan las ocurrencias que el ítem dependiente cuenta en ese momento. Un grupo con 3 de 9 entradas activas mide 13 bytes, no 19, y eso es lo que ven una comparación, STRING, UNSTRING e INSPECT; las ocurrencias durmientes conservan su contenido pero no forman parte del grupo.

Al recibir se aplica el máximo declarado. El movimiento es lo que escribe el ítem dependiente, así que su valor antiguo no puede acotar al receptor: MOVE ODO-RECORD TO NEW-RECORD copia las nueve ocurrencias en un registro cuyo propio ítem dependiente aún lee 3. NC247A además coloca el ítem dependiente en el primer byte del propio grupo, de modo que un movimiento receptor sobrescribe su propio contador a mitad de camino — medir contra el valor que el movimiento acababa de escribir truncaba la tabla. Leer el contador actual en todas las direcciones es tan erróneo como leer el máximo en todas; las pruebas nuevas cubren ambas mitades.

• Corregido: SEARCH y SEARCH ALL recorrían más allá del final de una tabla variable
Ambos se acotaban por el máximo declarado del OCCURS, así que un valor situado en una ocurrencia durmiente aún se encontraba. Ahora recorren la longitud activa, que es lo que exigen los dos casos AT END de NC247A. Esto también despejó NC235A por completo. Pruebas nuevas: test_odo_group_length.rs (13).

=== PowerRustCOBOL 1.62.29 — 2026-08-27 ===

NIST CCVS85 Nucleus (NC): ejecución 71 -> 72 de 95, aserciones 151 -> 136 fallando (97.0 %). NC234A pasa de 15 fallos a 0. La compilación se mantiene en 94 / 95 (conjunto completo 419 / 434). Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Corregido: una superposición REDEFINES grande perdía todas las escrituras
Dos descripciones de los mismos bytes se mantenían coherentes copiando una en la otra en cada escritura. Eso es asequible para los registros planos de veinte bytes para los que existe el mecanismo y ruinoso para una tabla redefinida de 10×10×10, donde un MOVE recorre mil ocurrencias dos veces — así que las descripciones que superaban REDEFINE_SYNC_BUDGET se rendían y conservaban almacenamiento separado. Cada nombre de la descripción redefinidora se leía entonces como espacios, por mucho que se hubiera rellenado la tabla redefinida.

Cuando las dos descripciones tienen el mismo trazado — los mismos ítems, en el mismo orden, con las mismas dimensiones OCCURS y los mismos PICTURE — no hay nada que copiar: son los mismos bytes leídos de la misma manera, así que ahora comparten directamente los slots de almacenamiento. Exacto, O(1) por acceso, y sin presupuesto que aplicar. Una descripción que contiene un FILLER sin nombre no tiene entrada de símbolo que comparar y conserva la superposición por copia, igual que cualquier pareja cuyos trazados difieran. La compartición es sólo de almacenamiento: cada descripción conserva su propia entrada de símbolo y, en particular, sus propios nombres INDEXED BY — SEARCH GRP-ENTRY-1 debe ser conducido por IDX-1-1, no por el IDX-1 de la tabla redefinida. Por eso el alias se sitúa en los accesores de almacenamiento y no en la resolución de nombres, donde habría entregado en silencio a la búsqueda el índice equivocado.

Post añadido a las 09:23. Post anterior a las 09:22

Medido, no supuesto: primero se probó simplemente subir el presupuesto hasta cubrir la tabla; NC234A tardó entonces 20 s y chocó con el tiempo límite del arnés, cambiando 15 fallos por un programa que no termina. La compartición lo ejecuta en 0.09 s. Sobre la etiqueta: esos 15 fallos estaban agrupados bajo SEARCH VARYING LEV y MULTIPLE SEARCH STMT, que es la funcionalidad que NC234A prueba, no el defecto — SEARCH … VARYING ya era correcto; las pruebas buscan en una tabla redefinidora que nunca había recibido los datos. Una categoría de causa nombra la prueba, no el defecto. Pruebas nuevas: test_redefines_shared_storage.rs (6).

=== PowerRustCOBOL 1.62.30 — 2026-08-27 ===

NIST CCVS85 Nucleus (NC): ejecución 72 -> 76 de 95. Cuatro programas quedan limpios de una vez — NC109M, NC110M, NC302M y NC303M — y la columna "printed no report" pasa de 3 -> 0. La compilación se mantiene en 94 / 95. Ningún otro módulo fue tocado (GOLDEN RULE #9). Estos cuatro se describían antes como inalcanzables sin trabajo en el arnés. Lo eran, y ese trabajo llega aquí; detrás se escondían dos defectos reales del runtime, más el análisis de marcado de conformidad que el estándar exige y que este compilador nunca tuvo.

• Corregido — ACCEPT hacia un grupo dejaba el receptor vacío

Un grupo no posee slot de almacenamiento propio: su valor se sintetiza a partir de sus ítems subordinados. ACCEPT escribía mediante set_str, que dejaba la línea completa del operador en un slot que nada vuelve a leer, de modo que un receptor de grupo quedaba exactamente como estaba y la lectura parecía tener éxito. NC109M acepta en ACCEPT-D1 (dos ítems subordinados) y en X80-CHARACTER-FIELD (un FILLER), y ambos comparaban después igual a nada.

Es el mismo defecto que INSPECT arrastró hasta 1.62.28, y el despacho vive ahora en un solo sitio — Interpreter::store_text — para que el próximo verbo que lo necesite no tenga que redescubrirlo. Toda fuente de ACCEPT con valor de texto pasa por él, no solo el Formato 1: ACCEPT WS-TODAY FROM DATE hacia un grupo de YY/MM/DD es la manera ordinaria de escribir eso.

• Corregido — VALUE QUOTE inicializaba el ítem con espacios

Una constante figurativa en una cláusula VALUE rellena su ítem, exactamente igual que VALUE SPACE. QUOTE caía al caso de "conservar el valor por defecto", así que PICTURE X VALUE QUOTE contenía un espacio. ACCEPT-D18 de NC109M es precisamente esa declaración, y el valor contra el que se comparaba nunca fue por tanto una comilla.

• Añadido — marcado de conformidad para los elementos obsoletos de COBOL-85

cobolt_semantic::flagging::flag_obsolete informa de los elementos del lenguaje que el estándar lista como obsoletos: los cinco párrafos opcionales de la IDENTIFICATION DIVISION, MEMORY SIZE, ALTER, STOP con literal y GO TO sin nombre de procedimiento. No es comprobación de errores: todos ellos compilan y se ejecutan como siempre, y ninguno se convierte en error ni en aviso en la ruta de compilación ordinaria. Es un punto de entrada separado precisamente para que una compilación normal no empiece a quejarse de AUTHOR.

El análisis lee el flujo de tokens, no el AST: la mayoría de los elementos obsoletos son párrafos de la IDENTIFICATION DIVISION y la cláusula MEMORY SIZE, que el parser consume y descarta porque no llevan significado que preservar. Registrarlos en Program solo para que una pasada de diagnóstico pudiera reencontrarlos engordaría el AST para algo que ninguna ruta de ejecución lee.

• Arnés — los cuatro miembros que no podían puntuarse

Post añadido a las 09:24. Post anterior a las 09:23

- NC109M lee once valores del operador. Ahora los recibe: el mazo de entrada se recupera del fuente en vez de inventarse, ya que cada valor aceptado se compara contra un literal VALUE emparejado en la propia DATA DIVISION del programa. 0 pass / 24 fail -> 16 pass / 0 fail.
- NC110M escribe su informe con DISPLAY, a la consola, nunca al fichero de impresión de CCVS. La consola ahora se captura y se puntúa. Sus dos tests enuncian su propio contrato en el texto que imprimen, y ese contrato contiene el marcador literal "PASS " que el puntuador genérico cuenta — así que la lectura específica del miembro corre primero, o la descripción de un test se puntúa como el resultado de uno.
- NC302M y NC303M son miembros de marcado sin maquinaria PASS/FAIL alguna. Cada construcción obsoleta lleva un comentario *Message expected for above statement: OBSOLETE y el programa declara *TOTAL NUMBER OF FLAGS EXPECTED = N.; ese contrato es legible por máquina, así que se lee y se coteja contra flag_obsolete en lugar de transcribirse. 7 / 7 y 4 / 4.

Un miembro de marcado se puntúa ahora en tiempo de compilación y nunca se ejecuta — ejecutarlo no puntúa nada, haga lo que haga. Eso también saca a NC401M de la columna de timeouts, aunque sigue puntuando 0 de sus 40: NC401M valida una clase distinta, NON-CONFORMING STANDARD, que marca construcciones fuera del high subset de COBOL-85 y no las obsoletas. Sus 44 fallos honestos son la razón de que el recuento de aserciones lea 4386 pass / 168 fail frente a 4375 / 124 — los programas que antes no podían puntuarse no estaban pasando: eran invisibles.

Con esto se fueron dos bloqueos latentes: run_one entregaba al proceso hijo una tubería para stdout y otra para stderr y luego no leía ninguna, así que cualquier programa que mostrara más que el búfer de tubería del sistema operativo se bloqueaba para siempre y se puntuaba como timeout. Ambas van ahora a ficheros.

=== PowerRustCOBOL 1.62.31 — 2026-08-27 ===

NIST CCVS85 Nucleus (NC): compilación 94 -> 95 de 95, ejecución 76 -> 77. El módulo ya compila al completo; la suite entera pasa de 419 -> 420 de 434. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Añadido — SPECIAL-NAMES. CURRENCY [SIGN] [IS] literal

NC108M era el último fallo de compilación del Nucleus y estaba registrado como necesitado de una "implementor-defined editing picture", <(3),<<<.99. No necesita tal cosa. Veinte líneas antes el programa declara CURRENCY "<", así que aquello es una picture ordinaria de moneda flotante con < en el papel que normalmente juega $ — COBOL-85 puro, y una laguna más que una extensión. La cláusula se parseaba y se tiraba.

El símbolo declarado reemplaza a $ en vez de sumarse a él, que es lo que dice el estándar: un programa que nombra un signo de moneda ya no puede escribir $ en una picture. La plantilla de PICTURE sigue escribiendo una posición de moneda como $, llame el programa al símbolo como lo llame — $ es el marcador interno de "posición de moneda", de modo que cada regla de anchura y de recuento de dígitos queda escrita una sola vez y solo el formateador sustituye en el momento de emitir uno.

• Corregido — un solapamiento REDEFINES dimensionaba mal todo ítem numérico editado

El trazado del registro tomaba la anchura de un ítem elemental como digits + decimals. Esos cuentan posiciones de dígito, y para una picture numérica editada no son la anchura: PIC $$$,$$$.99 ocupa diez caracteres pero informa dos dígitos y ningún decimal, porque analyze_pic separa las partes entera y fraccionaria por V y el separador de esta picture es un . real — así que ambos nueves caen en la parte entera y las posiciones de $, , y . no las cuenta nadie.

Un solapamiento que creía que el ítem medía dos bytes truncaba todo lo que realmente contenía y desplazaba cada campo posterior del registro ocho posiciones arriba. COMPLETE-FORMAT (19) de NC108M leía < donde estaba almacenado " <1,1". La anchura viene ahora de numedit::edited_width, que es lo que formatea el valor de todos modos.

Post añadido a las 09:25. Post anterior a las 09:24

Esto es anterior al trabajo de moneda y no tenía nada que ver con él: el mismo fallo se reproduce con una picture de $ normal sin cláusula SPECIAL-NAMES alguna.

• Corregido — un VALUE alfanumérico cambiaba la categoría de un ítem numérico

PICTURE IS 9 VALUE IS "5" almacenaba una cadena donde corresponde el número del ítem, así que toda regla que pregunta si el ítem es numérico respondía que no. BLANK WHEN ZERO era una de ellas: se aplica en la ruta de visualización numérica, de modo que el ítem conservaba su dígito donde el estándar exige espacios (NC108M FMT-TEST-GF-3). Un ítem numérico conserva ahora su categoría sea cual sea la del literal, y un literal que no deletrea ningún número deja el ítem en su valor por defecto en vez de adivinar.

=== PowerRustCOBOL 1.62.32 — 2026-08-27 ===

NIST CCVS85 Nucleus (NC): aserciones 168 -> 161 fallando (96.5 %). La ejecución se mantiene en 77 de 95 y la compilación en 95 de 95. NC252A pasa de 13 fallos -> 10, NC250A 14 -> 11, NC105A 20 -> 18, NC107A 10 -> 9, NC103A 2 -> 1. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Corregido — un MOVE de grupo descartaba cada porción que un hijo numérico no podía interpretar

Cuando el ítem receptor es un grupo, el estándar hace todo el movimiento alfanumérico: cada porción aterriza en su ítem subordinado tal cual está, diga lo que diga la PICTURE de ese ítem. Un hijo numérico tiene por tanto que poder contener caracteres que no son dígitos.

set_group almacenaba la porción cuando deletreaba un número, la almacenaba cuando era enteramente blanca, y la descartaba en cualquier otro caso — así que el hijo conservaba en silencio lo que tuviera antes. MOVE REDEF13 TO REDEF12 en NC252A mueve 120 bytes de A a un grupo cuyos hijos incluyen PIC 9(5) y seis PIC 9, y el registro volvía leyendo AAA 0AA 0AAAA, con los dígitos viejos asomando allí donde había un hijo numérico. El comentario sobre ese código ya decía que una porción no numérica "lands in the child as it stands"; solo el caso en blanco lo cumplía.

Un ítem numérico conteniendo caracteres es exactamente lo que el programa pidió. Usarlo después en aritmética es indefinido en COBOL-85, no algo que el runtime tenga que impedir.

• No hecho, y por qué — movimientos de grupo exactos al byte

Estrictamente el estándar no cambia ningún byte en un movimiento de grupo, así que una porción que sí deletrea un número debería conservar también sus propios caracteres: " 123" hacia un hijo PIC 9(5) debería quedarse " 123" en vez de re-renderizarse como 00123 a través de la PICTURE del hijo.

Se implementó y se midió: NC cayó de 77 programas limpios a 74, y las aserciones de 4406/161 a 4386/182. Muchísimos programas mueven un grupo numérico y luego calculan con sus hijos, y la normalización es lo que los mantiene funcionando. La lectura más estricta queda por tanto registrada — en set_group, y como test que fija el comportamiento actual con la medición al lado — y sin aplicar. Revertirlo exige un censo NC nuevo, no una apelación al texto.

=== PowerRustCOBOL 1.62.33 — 2026-08-28 ===

NIST CCVS85 Nucleus (NC): ejecución 77 -> 78 de 95, aserciones 117 fallando (97.4 %). NC401M pasa de 40 fallos -> 0, el último de los cinco miembros que no podían puntuarse en absoluto cuando empezó este trabajo. La compilación se mantiene en 95 de 95, la suite completa en 420 de 434. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Añadido — marcado de conformidad del high subset

Eslopes
31 de agosto de 2026, 14:33
cobolt_semantic::flagging::flag_high_subset informa de cada elemento por encima del high subset de COBOL-85: COMPUTE, EVALUATE, INITIALIZE, STRING, UNSTRING, SEARCH, CORRESPONDING, DIVIDE … REMAINDER, DISPLAY … UPON, ACCEPT … FROM DAY-OF-WEEK, INSPECT … CONVERTING, SET … TO TRUE, PERFORM … VARYING y … WITH TEST AFTER, IF … ELSE, la modificación de referencia, los nombres de datos cualificados, un cuarto subíndice, aritmética en una relación, condiciones de signo y complejas, ALTER … TO PROCEED TO, entradas de nivel 66 y 88, RENAMES … THROUGH, VALUE ALL, ASCENDING/DESCENDING KEY, una tabla de longitud variable con INDEXED BY, un REDEFINES de un REDEFINES, un ALPHABET con rango de literales, SYMBOLIC CHARACTERS, END PROGRAM, y la continuación de una palabra o de un literal numérico.

Todo lo de esa lista es COBOL-85 válido que RustCOBOL implementa y ejecuta. El análisis dice solamente "esto no compilaría en una implementación limitada al high subset", y por eso es un punto de entrada opcional que ninguna compilación ordinaria llama — la misma disciplina que la pasada de elementos obsoletos añadida en 1.62.30, y las dos son deliberadamente independientes: DATE-COMPILED es a la vez obsoleto y superior al subset, y NC303M y NC401M quieren cada uno verlo informado bajo su propia clase.

Dos de los elementos son léxicos y no sintácticos. Una línea de continuación existe en la imagen de tarjeta y ha desaparecido cuando llega al flujo de tokens — MUL + -TIPLY es simplemente la palabra MULTIPLY — así que el análisis lee el fuente junto a los tokens.

• Dos trampas que merecen quedar registradas

Un emparejador voraz esconde un detector erróneo. NC401M puntuó 40 de 40 a la primera, y uno de los cuarenta estaba mal. El detector de subíndices contaba comas, y una coma seguida de espacio es un separador que el lexer descarta — así que (A, B, C, D, 1) llega como cinco operandos sueltos y el detector nunca disparaba. Un flag espurio distinto ocupó su lugar, y como las expectativas reclaman el primer flag no reclamado anterior a ellas, los totales seguían cuadrando. Los subíndices se cuentan ahora como operandos menos operadores binarios, y la pasada flag imprime ambas listas completas cuando discrepan, ya que un excedente temprano siempre aflora como una queja sobre una línea perfectamente correcta al final.

No toda continuación está por encima del subset. Continuar un literal alfanumérico está disponible en todos los niveles de conformidad, y NC401M no marca su propio "SUPERCALIFRAGILISTICEXPIALIDO / -"CIOUS". Lo que marca es la continuación de una palabra o de un literal numérico. Ambos casos se distinguen por aquello con lo que arranca la continuación: una comilla reabre un literal, cualquier otra cosa continúa una palabra o un número.

• Dónde queda ahora NC

Todos los programas del módulo compilan, y 78 de 95 corren hasta el final informando cero fallos. Un programa no termina — NC201A se atasca en PFM-TEST-F4-13 — y dieciséis informan fallos. Los cinco miembros registrados como inalcanzables sin trabajo de arnés están todos puntuados, y los cinco pasan.

=== PowerRustCOBOL 1.62.34 — 2026-08-28 ===

NIST CCVS85 Nucleus (NC): un MOVE de grupo ya no cambia ningún byte. La ejecución se mantiene en 78 de 95 y la compilación en 95 de 95; las aserciones pasan de 117 -> 116 fallando, y NC252A de 10 -> 9. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Corregido — la categoría de un ítem numérico salía de su slot, no de su declaración

1.62.32 registró los movimientos de grupo exactos al byte como una limitación: almacenar los caracteres de una porción que sí deletrea un número costó tres programas limpios, así que un hijo numérico seguía re-renderizándose a través de su PICTURE y " 123" volvía como 00123. Esa medición era real, pero la causa no era la exactitud al byte — eran dos sitios que tomaban "¿qué hay en el slot?" como sustituto de "¿qué es este ítem?". El sustituto se sostenía solo mientras un ítem numérico no pudiera contener caracteres, y un MOVE de grupo es un movimiento alfanumérico que hace exactamente eso posible.

Post añadido a las 09:28. Post anterior a las 09:27

- CobolEnvironment::set asignaba sobre la imagen de bytes en vez de restaurar la identidad numérica del ítem. truncate_to_capacity lee el slot como CobolValue::Numeric para aplicar la regla de magnitud sin signo y el corte de orden alto, así que abandonaba en silencio — y SUBTRACT CORRESPONDING escribió -11 en un PIC 99 sin signo (NC253A SUB-TEST-F3-1, NC220M). Una escritura numérica sobre un slot que contiene bytes reconstruye ahora el ítem con su escala declarada y después asigna, de modo que MOVE 12 TO <PIC 9(3)V99> sigue aterrizando como 12.00.
- is_alphanumeric_field respondía desde el slot. Tras MOVE ZERO TO D-NAMES, la sentencia MOVE 7 TO DNAME-2 escribía por tanto los caracteres "7 ", justificados a la izquierda, en un ítem numérico (NC112A MOVE-TEST-F1-1-2 … -10). Un ítem declarado numérico nunca es ahora alfanumérico, contenga lo que contenga su slot.

Con esos dos arreglados, la exactitud al byte no cuesta nada: los mismos 78 programas limpios, una aserción mejor, y NC252A mejora. MOVE SRC TO DST a través de un grupo deja ahora cada byte donde estaba, que es lo que el estándar dice que hace.

• Verificación — cada detector de marcado queda fijado

flag_high_subset tiene ~30 detectores y 1.62.33 probó unitariamente 15 de ellos; el resto descansaba en el agregado de NC401M, que esa misma versión demostró que puede mentir (un detector muerto y un flag espurio se cancelaron y el total siguió leyendo 40 de 40). Los nueve restantes se prueban ahora individualmente — EVALUATE, SEARCH, IF … ELSE, GO TO., ALPHABET con rango de literales, SYMBOLIC CHARACTERS, una tabla de longitud variable con INDEXED BY, ASCENDING/DESCENDING KEY y END PROGRAM — cada uno junto a la grafía dentro del subset ante la que debe permanecer callado. Un INDEXED BY de longitud fija y un ALPHABET IS NATIVE no producen flag alguno, como debe ser. Ningún detector descansa ya en el agregado.

=== PowerRustCOBOL 1.62.35 — 2026-08-28 ===

NIST CCVS85 Nucleus (NC): ACCEPT … FROM <mnemonic> lee al operador. NC204M pasa de 15 aserciones fallando -> 3, las aserciones del módulo de 116 -> 104 fallando (97.7 % de 4562). La ejecución se mantiene en 78 de 95 y la compilación en 95 de 95 — NC204M conserva tres fallos, todos ellos un único defecto de REDEFINES sin relación con esto. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Corregido — un mnemónico declarado se leía como variable de entorno

SPECIAL-NAMES asocia un dispositivo del implementador con un nombre a elección del programa, y el Formato 1 de ACCEPT puede entonces nombrarlo:


SPECIAL-NAMES.
XXXXX057 IS ACCEPT-INPUT-DEVICE.
...
ACCEPT ACCEPT-D1 FROM ACCEPT-INPUT-DEVICE.


La cláusula ordinaria <implementor-name> [IS] <mnemonic> se saltaba token a token — solo la forma de switch (… ON STATUS IS …) se reconocía — así que nada registraba que el nombre había sido declarado. parse_accept_source caía a su último caso, la extensión de PowerRustCOBOL que lee una variable de entorno con ese nombre, no encontraba ninguna definida y no almacenaba nada. Toda lectura a través de un mnemónico devolvía vacío.

- El parser registra ahora la cláusula (Parser::mnemonics), igual que ya registra los switches, las clases y el símbolo de moneda; vive en el parser porque la ENVIRONMENT DIVISION precede a la PROCEDURE DIVISION, así que el conjunto declarado se conoce para cuando un ACCEPT lo necesita.
- AcceptSource::Mnemonic(String) (nuevo, al final del enum — el AST se serializa con bincode por ordinal de variante) marca la lectura estándar; el intérprete lo trata exactamente como un ACCEPT simple: una línea del operador, distribuida por un receptor de grupo y rellenada con espacios hasta la anchura del ítem.
- La extensión queda intacta: un nombre que ninguna cláusula SPECIAL-NAMES declara sigue leyendo la variable de entorno. Qué lectura aplica lo decide la declaración, nunca la grafía del nombre.

• Arnés — el mazo de operador de NC204M

Post añadido a las 09:29. Post anterior a las 09:28

NC204M es el gemelo de NC109M: quince ACCEPT, cada uno comparado contra un ítem emparejado cuyo valor el programa fija justo encima de la lectura. El mazo se recupera por tanto del fuente, no se inventa, exactamente como se hizo con el de NC109M — hasta los espacios significativos iniciales y finales, el QUOTE y el valor DISPLAY-F de 200 caracteres. nist_conformance lo suministra por la stdin del proceso hijo.

Doce de las quince comparaciones de NC204M pasan con eso en marcha. Las tres restantes (ACC-TEST-F1-10, -14-1, -14-2) son un único defecto separado: un solapamiento REDEFINES no comparte los bytes del ítem redefinido, así que TAB-ACCEPT lee espacios donde deberían estar los puntos de ACCEPT-VALUE21, y un grupo FILLER REDEFINES lee vacío tras un ACCEPT exitoso. Es la misma familia que NC107A y NC252A y no se aborda aquí.

• Documentación

- docs/cobol85-supported-syntax-en.md — la entrada de ACCEPT indica ahora cuál de las dos lecturas aplica y por qué. El marcador NIST se actualiza (NC 95/95 compile, 78/95 execution) y la sección que afirmaba que cinco miembros "can never reach 0 failures" se sustituye: los cinco están puntuados, así que no hay techo estructural por debajo de 95 en el eje de ejecución. Esa afirmación estaba desfasada desde 1.62.30.
- docs/developers-guide-en.md — una nueva sección "Naming the console: mnemonic device names" bajo SPECIAL-NAMES, incluida la nota de que un mnemónico declarado y un nombre no declarado son fuentes distintas.

• Conocido — ACCEPT … FROM ENVIRONMENT "name" es inalcanzable

Encontrado durante las pruebas, no causado por este cambio ni reparado por él: ENVIRONMENT se lexea como palabra clave propia (la división abre con ella), así que parse_accept_source nunca la ve como identificador y su rama ENVIRONMENT "name" está muerta — la sentencia se lee como FROM DATE y el literal suelto descarrila la frase que la sigue. FROM ENVIRONMENT-VALUE es una fuente distinta y funciona. Ningún programa NC escribe la forma literal, así que se registra aquí en vez de arreglarse dentro de una pasada de módulo (GOLDEN RULE #9).

=== PowerRustCOBOL 1.62.36 — 2026-08-28 ===

NIST CCVS85 Nucleus (NC): dos maneras en que un solapamiento REDEFINES perdía los bytes de su objetivo. NC204M queda ahora limpio, así que la ejecución pasa de 78 -> 79 de 95 y las aserciones de 104 -> 101 fallando (97.8 % de 4561). La compilación se mantiene en 95 de 95. Ningún otro módulo fue tocado (GOLDEN RULE #9). Ambos defectos parecían idénticos desde fuera — la descripción redefinidora volvía leyendo espacios como quiera que se hubiera rellenado el ítem que redescribe — y no tenían nada en común por debajo.

• Corregido — las claves de almacenamiento del solapamiento perdían sus cualificadores externos

sync_redefines sembraba la copia inicial recorriendo la declaración redefinidora desde una ruta de ancestros vacía, así que cada clave que escribía carecía de los cualificadores externos. Eso es invisible mientras los nombres dentro del solapamiento son únicos — canon_key devuelve una hoja única tal cual, se le dé la ruta que se le dé — y erróneo en cuanto uno se duplica, que es exactamente cuando la clave cualificada lleva la información.

NC204M declara TAB-A bajo ACCEPT-D21 y bajo ACCEPT-D23, así que su ACCEPT-D21 REDEFINES ACCEPT-VALUE21 escribía en una clave que nada leía jamás: ACC-TEST-F1-10 calculaba " ABCD " donde estaba declarado "....ABCD....". El recorrido lleva ahora la ruta de ancestros, construida igual que la construye init_decl_h — un grupo sin nombre no aporta nada, cada grupo con nombre se aporta a sí mismo — de modo que los dos coinciden en la respuesta.

• Corregido — una descripción redefinidora sin nombre no tenía clave alguna

02 FILLER REDEFINES <item>. nombra bytes que su objetivo ya posee, sin nombre propio. redefine_pairs solo se registraba if is_named, así que no existía par de solapamiento y ninguna escritura al objetivo llegaba jamás a la descripción: ACC-14-CHARS-1-10 leía vacío tras un ACCEPT exitoso en el ítem que redescribe (ACC-TEST-F1-14-1, -14-2).

Post añadido a las 09:30. Post anterior a las 09:29

Una hoja sin nombre ya recibe una clave sintética bajo su padre; un grupo sin nombre que redefine recibe ahora lo mismo, registrado con el trazado de sus hijos con nombre, y la maquinaria ordinaria de solapamiento se hace cargo. Es una descripción, no un alias de su primer hijo: varios hijos se reparten los bytes del objetivo, y una escritura por cualquiera de los dos lados aterriza donde el trazado dice.

• Arnés — las dos últimas líneas del mazo de NC204M estaban mal

Hay que leer los solapamientos, no sus nombres: ambos grupos FILLER REDEFINES ACCEPT-TEST-14-DATA empiezan en el primer byte del ítem, así que ACC-14-CHARS-11-15 son los bytes 1–5 pese a lo que su nombre sugiere. Cada ACCEPT tiene por tanto que empezar con lo que comprueba el párrafo posterior, y el mazo es ahora "ABCDEFGHIJ" y luego "KLMNO" — que es también lo que las instrucciones de ejecución de CCVS85 hacen teclear al operador para el test VI-71 6.5.4 GR4(a).

• Conocido — un grupo sin nombre con hijos lee corto en su padre

Encontrado durante las pruebas, no causado por este cambio: 01 G. 02 T PIC X(5). 02 FILLER. 03 C1 PIC X(5). renderiza G como T a solas. Un grupo sin nombre con hijos aporta un slot FILLER sintético al trazado de su padre y luego no almacena nada en él, así que el padre lee corto. Aquí solo se repara la forma redefinidora; la forma simple queda registrada en vez de cambiarse dentro de una pasada de módulo (GOLDEN RULE #9), y a_plain_filler_group_is_not_treated_as_an_overlay fija la parte que es correcta.

=== PowerRustCOBOL 1.62.37 — 2026-08-28 ===

NIST CCVS85 Nucleus (NC): la protección de cheques rellena un CR/DB de dos caracteres. NC175A queda ahora limpio, así que la ejecución pasa de 79 -> 80 de 95 y las aserciones de 101 -> 98 fallando (97.9 % de 4560). La compilación se mantiene en 95 de 95. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Corregido — un cero protegido volvía un carácter más corto que su propia anchura

Un valor cero en una picture cuyas posiciones de dígito son todas * rellena cada posición de carácter con asteriscos, con la única excepción del punto decimal. El relleno recorría los símbolos expandidos de la picture y emitía un carácter por símbolo — pero CR y DB ocupan dos posiciones de carácter, que es exactamente lo que edited_width cuenta para ellos.

PIC $**.**CR conteniendo cero producía por tanto siete caracteres donde el ítem mide ocho, y lo que lo rellenaba hasta la anchura ponía un espacio en la última posición: "***.***" más un espacio, frente al "***.****" que el estándar exige (NC175A SUB-TEST-F2-28-5, -30-5, -32-5 — tres aserciones, una causa).

La ruta de valores distintos de cero ya era correcta y no cambia: allí solo se protegen los ceros iniciales, así que un $ solitario es una inserción fija que conserva su posición (-2.34 -> $*2.34CR), y CR se imprime porque el valor es negativo.

• Documentación

docs/cobol85-supported-syntax-en.md gana la regla de protección de cheques bajo PICTURE (y el marcador NIST pasa a 80/95); docs/developers-guide-en.md la gana junto al ejemplo PIC $**.**CR que ya llevaba, con la nota de que CR son dos posiciones de carácter.

=== PowerRustCOBOL 1.62.38 — 2026-08-28 ===

NIST CCVS85 Nucleus (NC): un literal numérico mueve sus caracteres, y un operando numérico comparado con uno no numérico se pseudo-mueve. NC202A y NC103A quedan ahora limpios y NC105A pasa de 18 -> 13, así que la ejecución alcanza 80 -> 82 de 95 y las aserciones 98 -> 88 fallando (98.1 % de 4559). La compilación se mantiene en 95 de 95. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Corregido — un literal numérico perdía los dígitos con los que fue escrito

COBOL-85 mueve los caracteres de un literal numérico a un receptor alfanumérico, no su valor. MOVE 060820000200 TO CORR-DATA-2, con seis hijos PIC 99, los rellena 06 08 20 00 02 00. El lexer conservaba solo el valor, así que el cero inicial desaparecía y los once dígitos restantes desplazaban cada hijo una posición a la izquierda; el ADD CORRESPONDING posterior calculaba 63 donde 09 era lo correcto (NC202A ADD-TEST-F3-7-1/2/3/5 — cuatro aserciones, una causa).

Post añadido a las 09:32. Post anterior a las 09:30

La anchura escrita viaja ahora como recuento de dígitos y no como texto, así que nada reserva memoria: Token::IntegerLiteral(i64, u8) y, para los literales que lo necesitan, Literal::IntegerDigits(i64, u8) (nuevo, al final del enum — el AST se serializa con bincode por ordinal). Un literal cuyo valor se renderiza de vuelta a lo escrito sigue siendo Literal::Integer, de modo que el caso ordinario conserva la forma que cada brazo existente ya maneja.

Ensanchar la aridad del token en vez de añadir una variante hermana es lo que hizo esto seguro: cada uno de sus 24 sitios de patrón se convirtió en error de compilación, así que el compilador enumeró el trabajo en vez de que un matches! dejara de casar en silencio. Atención: la anchura del receptor nunca interviene. MOVE 2 TO <PIC X(4)> sigue siendo "2 "; rellenar hasta el receptor lo convertiría en "0002".

• Corregido — comparar un operando numérico con uno no numérico

COBOL-85 VI-89 6.15.4 GR2: cuando un operando de una relación es numérico y el otro no numérico, la comparación es no numérica — el operando numérico se trata como si se hubiera movido a un ítem alfanumérico del mismo tamaño, lo que transfiere sus posiciones de carácter y no su signo operacional. Nosotros comparábamos algebraicamente, así que PIC S9(18) conteniendo -123456789012345678 no era igual a PIC X(18) conteniendo "123456789012345678" (NC103A IF-TEST-GF-98).

El pseudo-movimiento vive junto a as_comparand, donde un operando es todavía una expresión que nombra un ítem — "del mismo tamaño" es el tamaño del ítem, y un CobolValue ha dejado de ser un ítem para cuando compare_values lo ve.

Tres precondiciones deciden si la regla aplica, y cada una costó un programa mientras faltó: el operando numérico debe ser entero — el estándar lo dice, y un ítem PIC S9(9)V9(9) no tiene posición de carácter para su punto decimal, así que pseudo-moverlo hacía fallar comparaciones que imprimen igual (NC112A); "no numérico" es una propiedad de la declaración, nunca del slot — tras un MOVE de grupo un hijo PIC 99 contiene caracteres legítimamente, e IF XYZ-13 = 0 sigue siendo una relación entre dos operandos numéricos (NC208A MOV-TEST-F1-3); y ALL literal toma el tamaño del otro operando, porque no tiene otro — IF ALL "00" NOT > ZERO-D con ZERO-D PICTURE 9 compara contra un carácter (NC250A IF--TEST-106).

• Corregido — alphanumeric_capacity respondía desde el slot

El mismo sustituto que is_alphanumeric_field abandonó en 1.62.34, en su hermano y por la misma razón. Después de que un MOVE de grupo deje caracteres en un hijo PIC 9, MOVE ZERO TO DNAME-1 tomaba la ruta de relleno al estilo MOVE ALL y escribía "0" repetido hasta la anchura del slot de bytes, así que un ítem PICTURE 9 volvía leyendo 000 (NC112A MOVE-TEST-F1-2-*).

De todos modos comparaba igual a 0, porque la vieja comparación entre tipos forzaba ambos lados a número — un pase accidental sobre un ítem genuinamente erróneo, y uno que el arreglo de comparación de arriba convirtió en fallo honesto antes de que esto lo reparara. Un ítem declarado numérico nunca es alfanumérico, contenga lo que contenga su slot.

• Documentación

docs/cobol85-supported-syntax-en.md — las reglas de MOVE y de la condición de relación, y el marcador NIST en 82/95. docs/developers-guide-en.md — una sección "Comparing a number with text", ya que la regla es una con la que un desarrollador de PowerCOBOL o isCOBOL se topará la primera vez que compare un campo de pantalla contra un número.

• Conocido — el test de construcción de cobolt-ast no compila

Encontrado durante el barrido, preexistente y no tocado aquí: crates/cobolt-ast/tests/test_ast_construction.rs inicializa DataDecl sin justified ni sign, y Program sin cinco campos que esos tipos ganaron en sesiones anteriores. El target de tests del crate entero falla por tanto al construirse, así que no ha estado ejecutándose. Se informa en vez de repararse dentro de una pasada de módulo (GOLDEN RULE #9).

=== PowerRustCOBOL 1.62.39 — 2026-08-28 ===

Post añadido a las 09:33. Post anterior a las 09:32

NIST CCVS85 Nucleus (NC): un GO TO cualificado, y MOVE CORRESPONDING donde un lado de un par es un grupo. NC208A queda ahora limpio y NC209A pasa de 8 -> 7, así que la ejecución alcanza 82 -> 83 de 95 y las aserciones 88 -> 84 fallando (98.2 % de 4559). La compilación se mantiene en 95 de 95. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Corregido — GO TO paragraph {OF|IN} section ignoraba su cualificador

El cualificador escoge cuál de dos párrafos homónimos se quiere decir. PERFORM … OF … ya lo honraba; GO TO resolvía contra para_order, que está indexado por nombre pelado y devuelve la primera definición en cualquier parte del programa. NC208A declara PAR-4B en QUAL-SECTION-1 y en QUAL-SECTION-2 y salta a la segunda — aterrizaba en la primera, la copia cuyo propio comentario dice que jamás debe entrarse en ella (PAR-TEST-F2-4).

- Stmt::GoTo ganó section: Option<String> al final de sus campos (el AST se serializa con bincode por orden de declaración), parseado con el helper eat_procedure_name_qualified que PERFORM ya usa.
- RuntimeError::GoTo lleva el cualificador hasta el bucle que capture la señal, y un único resolutor — Interpreter::goto_index — es compartido por los sitios que antes llamaban a para_order.iter().position(…) directamente. Un cualificador desconocido cae a la búsqueda sin cualificar en vez de perder el salto, la misma elección que hace PERFORM … OF ….
- Dos casos deliberadamente no toman cualificador: GO TO … DEPENDING ON, cuyos objetivos son una lista pelada, y un GO TO que un ALTER ha redirigido — la redirección nombra su propio objetivo directamente. El despacho de programas anidados (run_para_sequence) ignora el cualificador a propósito: recorre el espacio de párrafos del propio programa, y section_span lee el externo.

• Corregido — un par corresponding con un grupo a un lado no movía nada

COBOL-85 pide solo que al menos un ítem de un par corresponding sea elemental, así que un grupo puede legítimamente encararse a un ítem elemental. Un grupo no posee slot de almacenamiento — su valor se sintetiza de sus ítems subordinados — de modo que: un receptor de grupo se escribía por env.set bajo el propio nombre del grupo, lo que dejaba el registro donde nada lo vuelve a leer (MOV-TEST-F1-4: fuente MOVE-CORR-4 PIC XXX = "XYZ", receptor un grupo de 999 + XXX, que conservó sus espacios); y un emisor de grupo se leía por env.get, que no producía nada, así que el receptor quedaba como estaba (MOV-TEST-F1-5: MOVE-CORR-G5 es un grupo de XXX + 99 enviando a un X(5) simple).

Ambos pasan ahora por las rutas conscientes de grupos — Interpreter::store_text y CobolEnvironment::group_value — que INSPECT (1.62.28) y ACCEPT (1.62.30) ya usan. Dos grupos encarados entre sí siguen recursando; ese emparejamiento no es el caso elemental y no se aplana en un único movimiento alfanumérico.

• Documentación

docs/cobol85-supported-syntax-en.md — el GO TO cualificado y la regla de CORRESPONDING con grupos, y el marcador NIST en 83/95. docs/developers-guide-en.md — ambos, junto al material al que pertenecen.

=== PowerRustCOBOL 1.62.40 — 2026-08-28 ===

NIST CCVS85 Nucleus (NC): constantes figurativas, fidelidad de bytes, JUSTIFIED, la prueba de clase NUMERIC, dos reglas de REDEFINES, un símbolo de moneda flotante y la serie de delimitadores de STRING. NC211A, NC174A, NC107A y NC217A quedan limpios, así que la ejecución pasa de 83 -> 87 de 95 y las aserciones fallidas bajan de 84 -> 53 (98,8 % de 4558). La compilación se mantiene en 95 de 95. Ningún otro módulo fue tocado (GOLDEN RULE #9).

Eslopes
31 de agosto de 2026, 14:40
• Corregido — una constante figurativa toma el tamaño de aquello contra lo que se escribe
- VALUE ALL "literal" rellena su elemento: PICTURE X(6) VALUE ALL "ABC" es "ABCABC". Todas las demás constantes figurativas son de un solo carácter y las ramas anteriores del código rellenaban con él; ALL es la única cuya unidad puede ser más ancha que un byte, así que caía en el valor por defecto del elemento y este quedaba lleno de espacios (NC211A FIG-TEST-1/2). ALL delante de otra figurativa nunca llega a esa ruta: el parser pliega ALL SPACES a SPACES.
- Una constante figurativa en una relación se repite hasta el tamaño del otro operando: IF QT = QUOTE con QT PICTURE XXX compara """ contra """. Con un solo carácter se comparaba contra " rellenado a la derecha con espacios, que no es igual. SPACE y ZERO nunca mostraron el defecto — el relleno que ya recibe un operando corto produce justo lo que SPACE repetiría, y ZERO se compara algebraicamente —, así que QUOTE, HIGH-VALUE y LOW-VALUE lo cargaban en solitario (NC211A FIG-TEST-3).

• Corregido — HIGH-VALUE es un byte, no un carácter
HIGH-VALUE es 0xFF, que no es UTF-8 válido y no tiene grafía de un byte de ancho. Toda ruta que llevara un registro a través de un String de Rust lo sustituía por un U+FFFD de tres bytes y desplazaba dos posiciones cada campo posterior. El elemento seguía leyéndose como "algún valor alto", de modo que solo lo delataba un campo situado más adelante en el registro.
- CobolEnvironment gana group_bytes, display_bytes, set_group_bytes y set_bytes; set_group delega ahora en la forma de bytes, de modo que el corte y el relleno que hace un movimiento de grupo ocurren sobre bytes en todo el trayecto. MOVE <group> TO <group> toma los bytes almacenados del emisor. STRING ensambla su resultado como bytes, compara los delimitadores byte a byte y almacena a través del nuevo store_bytes (NC217A STR-TEST-GF-9). No cubierto: la modificación por referencia (X (1:1)) sigue leyendo la forma de texto; es el siguiente miembro de esta familia.

• Corregido — JUSTIFIED RIGHT sobre un elemento alfabético
La cláusula solo se registraba para PicKind::Alphanumeric, así que PICTURE A(5) JUSTIFIED RIGHT se analizaba, se olvidaba, y cada MOVE hacia el elemento alineaba a la izquierda. Faltaban las dos mitades de la regla: un emisor corto se empuja a la derecha, y uno largo pierde sus caracteres más a la izquierda (NC107A JUST-TEST-03, JUST-TEST-04; también cinco aserciones en NC105A).

• Corregido — la prueba de clase NUMERIC
Un elemento cuya PICTURE no lleva signo operacional es numérico solo cuando cada posición de carácter contiene un dígito. Leerlo con parse::<f64> aceptaba un signo, un punto decimal, un exponente y espacios alrededor, así que PICTURE X(5) con "+1234" respondía "numérico" (NC211A CC--TEST-GF-48, NC174A CLASS-TEST-GF-8/10). La declaración decide, como siempre: un elemento numérico que contiene caracteres — cosa que un MOVE de grupo puede producir — conserva la lectura basada en el valor.

• Corregido — dos reglas independientes de REDEFINES
- Una superposición lleva los bytes del destino a un par numérico. La sincronización filtraba todo carácter no-dígito y rellenaba lo que quedaba, así que "00ABCDEFGHI 4321 " se releía como 004321000000000000 e IS NUMERIC respondía que sí sobre un elemento lleno de letras. Cuando los bytes sí deletrean dígitos, la lectura numérica no cambia (NC174A CLASS-TEST-GF-8).
- Un REDEFINES de nivel 01 puede describir más almacenamiento del que redefine. NC107A superpone a un REDEF10 de 46 bytes un REDEF11 de 92 bytes y un REDEF12 de 120, y luego lee más allá del final del elemento redefinido. Los bytes más allá de la longitud propia de una descripción no le corresponde declararlos a esa descripción, así que el par conserva los suyos; renderizar la más corta sobre la más larga rellenaba la cola con espacios y la borraba (RDF-TEST-9, RDF-TEST-10; también dos en NC252A).

Post añadido a las 09:35. Post anterior a las 09:34

• Corregido — un símbolo de moneda alfabético flota a lo largo de toda su tirada
CURRENCY SIGN IS "W" convierte PICTURE WWWWW en una cadena de moneda flotante. Cuando el símbolo es una letra, la tirada llega como un único identificador — WWWWW, no cinco tokens —, de modo que comprobar si había un identificador de un solo carácter rechazaba toda picture de moneda flotante que no usara $ (NC107A CURR-TEST-1).

• Corregido — STRING
- DELIMITED BY gobierna toda la serie de emisores que lo precede, no solo aquel tras el que está escrito (COBOL-85 6.24.3). Ligarlo al último emisor dejaba a los anteriores sin frase, lo que equivale a DELIMITED BY SIZE, así que se anexaba cada uno entero y STRING "A0" "B0D" ... DELIMITED BY ZERO construía "A0B0D" donde el estándar quiere "ABCDE" (NC217A STR-TEST-GF-10, GF-11, GF-14).
- STRING ... INTO <group> alcanza a los hijos del grupo. Un grupo no posee slot de almacenamiento, así que escribir en su propio slot dejaba el registro intacto (STR-TEST-GF-21) — el mismo despacho por el que ya pasan INSPECT y ACCEPT.

• Corregido — un operando ordinal de CLASS o ALPHABET en su propia línea de código
Un número que abre una línea se lexea como número de nivel siempre que pueda serlo, y 1-49, 66, 77 y 88 todos pueden. La posición ordinal de A es exactamente 66, así que CLASS ORDINAL-A-ONLY IS seguido de 66 en la línea siguiente llegaba como LevelNumber(66), no casaba con nada y la clase no describía ningún carácter. Solo la DATA DIVISION tiene números de nivel y el lexer no tiene contexto de división; la propia cláusula dice qué es el número. Tanto CLASS como ALPHABET arrastraban el mismo hueco (NC174A CLASS-TEST-GF-39/41/43).

• Arnés de pruebas — las sustituciones de implementador del CCVS
XXXXX090 y XXXXX091 son los marcadores del conjunto para las posiciones ordinales de A y D en el juego de caracteres del implementador; NC174A y NC254A declaran las mismas tres clases dos veces, una con literales y otra con estos marcadores, y comprueban que ambas coinciden. nist_conformance los sustituye ahora (66 y 69), rellenados hasta los ocho caracteres del propio marcador para que el área de secuencia del formato fijo en las columnas 73-80 no se arrastre al área de contenido. Es la misma clase de entrada del implementador que ya suministran los ajustes de interruptores y los mazos de operador.

• Pruebas
test_figurative_and_byte_fidelity.rs (6), test_justified_class_currency.rs (6), test_string_delimiter_series.rs (5).

=== PowerRustCOBOL 1.62.41 — 2026-08-28 ===

NIST CCVS85 Nucleus (NC): nombres de condición cualificados, las reglas del MOVE con operando de grupo, HIGH-VALUE como byte, lo que CORRESPONDING no puede emparejar, y el único barrido que comparte una serie INSPECT ... TALLYING. NC246A, NC104A y NC105A quedan limpios, así que la ejecución pasa de 87 -> 90 de 95 y las aserciones fallidas bajan de 53 -> 17 (99,6 % de 4555). La compilación se mantiene en 95 de 95. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Corregido — un nombre de condición cualificado elige la declaración que nombra
Un mismo nombre de nivel 88 puede declararse bajo varios grupos — CCVS85 declara EQUALS-A bajo tres tablas separadas — y una referencia los distingue con OF/IN, exactamente como una referencia a datos. El entorno guardaba una sola entrada por nombre, así que la última declaración ganaba silenciosamente cada búsqueda y el subíndice escrito en la referencia se aplicaba entonces al huésped equivocado: EQUALS-M OF ... OF GROUP-1-TABLE (13) probaba la ocurrencia 13 de una tabla que solo tiene cuatro, no leía nada y salía falso. La pista era que el caso más difícil pasaba: la tabla tridimensional resultaba estar declarada en último lugar (NC246A QUAL-TEST-08/10/11, NC250A).
cond_names guarda ahora cada declaración en orden de fuente con su propia ruta de cualificación, y cond_name_qual resuelve contra ella con la misma regla de subsecuencia que resolve_canonical usa para los nombres de datos.

Post añadido a las 09:37. Post anterior a las 09:35

• Corregido — un operando de grupo transporta bytes, no valores
COBOL-85 6.18.4 hace un movimiento alfanumérico-a-alfanumérico siempre que cualquiera de los operandos sea un elemento de grupo: la PICTURE del otro operando aporta su tamaño y nada más. Un receptor de grupo ya seguía esa regla; un emisor de grupo no, así que un receptor elemental seguía editando, des-editando o parseando lo que llegaba.
- MOVE <grupo con "123ABC"> TO <PIC 0XXXXX0> deja "123ABC ", no el editado "0123AB0" (NC105A MOVE-TEST-F1-20); hacia un PIC 9999V999 deja esos seis caracteres y un espacio, que el REDEFINES PIC X(7) del elemento relee (F1-17); hacia un PIC 99 trunca a "12" (F1-16). JUSTIFIED RIGHT sigue decidiendo qué extremo rellena y cuál se pierde: un grupo de quince bytes hacia un PIC A(7) JUSTIFIED conserva sus siete de más a la derecha (NC107A JUST-TEST-04). Un grupo distribuye sus propios bytes en sus hijos tal cual: a un hijo alfanumérico-editado se le reimponían sus caracteres de inserción sobre una porción ya editada, convirtiendo "1 A05" en "1 0A" (NC105A F1-13). Y una cláusula VALUE en un grupo llega a sus hijos: se almacenaba en el slot propio del grupo — y un grupo no tiene slot que nadie relea —, así que 01 MOVE29A VALUE "$123.45". 02 MOVE30 PIC $999.99. dejaba MOVE30 vacío (NC104A MOVE-TEST-F1-29).
Nuevos: CobolEnvironment::set_verbatim_bytes (el escritor "los bytes son bytes", usado también por la distribución de grupo) y set_move_bytes (lo mismo con la regla de alineación del movimiento alfanumérico).

• Corregido — HIGH-VALUE rellena su receptor, y un grupo se lee como bytes
0xFF no es un carácter, así que HIGH-VALUE no podía viajar por la ruta de relleno de cadena que usan SPACE y ZERO y quedaba en manos de CobolValue::assign, que ponía un byte y rellenaba el resto con espacios. A un receptor de grupo le iba peor: el valor pasaba por as_display_string y llegaba como los tres bytes de U+FFFD.
- MOVE HIGH-VALUE TO <PIC X(10)> son diez bytes 0xFF; hacia un grupo se distribuye entre los hijos (NC105A MOVE-TEST-F1-67). Un receptor alfanumérico-editado sigue colocando sus caracteres de inserción — el relleno solo alcanza las posiciones de origen, así que PIC XX0XXBXXX contiene FF FF '0' FF FF ' ' FF FF FF (F1-69); apply_alnum_edit trabaja ahora sobre bytes. Leer un operando de grupo también es exacto a nivel de byte: eval_expr sintetizaba un grupo a través de un String, así que un grupo lleno de HIGH-VALUE comparaba desigual con HIGH-VALUE y se leía con el triple de su propio ancho. Bajo una PROGRAM COLLATING SEQUENCE la constante nombra un carácter ordinario y los bytes de ese carácter son la unidad de relleno, como antes.

• Corregido — lo que CORRESPONDING no puede emparejar, y lo que debe alcanzar
- COBOL-85 6.18.4 GR1 excluye un elemento descrito con REDEFINES o RENAMES. Una reagrupación de nivel 66 recibía el elemento homónimo del emisor y sobrescribía los dos elementos que renombra (NC209A MOV-TEST-F2-5), y lo mismo le ocurría al elemento bajo una rama REDEFINES (F2-6). La exclusión recae en la declaración, no en el nombre: los niveles 66 ya no entran en ItemSym.children en absoluto, y un elemento redefinidor se omite por clave — excluir por nombre rompía un elemento normal que simplemente comparte nombre con un 66 en otro lugar (F2-4).
- Cualquiera de los dos operandos puede nombrar una ocurrencia de una tabla de grupos. La tabla de símbolos se indexa por el elemento base, así que MOVE CORRESPONDING ... TO C-FLOCK (4) no casaba con nada y la sentencia no movía nada en absoluto. Los subíndices del operando se trasladan ahora a cada clave hija emparejada, y la recursión los sigue trasladando (NC209A MOV-TEST-F2-7, -F2-8).

Post añadido a las 09:38. Post anterior a las 09:37

• Corregido — una serie INSPECT ... TALLYING comparte un solo barrido
COBOL-85 6.17.3 inspecciona el elemento una vez, de izquierda a derecha; en cada posición de carácter los operandos se prueban en el orden en que se escribieron, el primero que casa toma la posición, y el barrido continúa pasados los caracteres que consumió. Antes cada operando barría el elemento entero por su cuenta, así que los mismos caracteres se contaban varias veces: TALLYING t1 FOR ALL "AA" t2 FOR ALL "A" sobre "AABA" daba t2 = 3 donde el estándar da 1 (NC216A INS-TEST-F1-27), y un operando FOR LEADING contaba una tirada cuyo primer carácter ya había tomado un operando anterior (INS-TEST-F3-19). CHARACTERS cuenta ahora solo las posiciones que ningún operando anterior reclamó. Cada operando conserva su propia ventana BEFORE/AFTER, calculada una vez sobre el elemento tal como está.

• Pruebas
test_qualified_condition_names.rs (5) y test_group_sender_moves.rs (8) son nuevos; test_figurative_and_byte_fidelity.rs (+4), test_qualified_goto_and_corr.rs (+4) y test_inspect.rs (+5) crecieron. Dos de las pruebas de nombres de condición afirman el mismo programa en ambos órdenes de declaración: la propiedad es la independencia del orden, no una respuesta concreta.

=== PowerRustCOBOL 1.62.42 — 2026-08-28 ===

NIST CCVS85 Nucleus (NC) está completo: 95 de 95 en compilación y en ejecución, con 0 aserciones fallidas de 4614. La ejecución pasa de 90 -> 95 de 95; todos los programas del módulo corren ahora hasta el final, donde NC201A antes no terminaba en absoluto. Nueve correcciones, cada una de ellas una regla de COBOL-85 que faltaba y no una capacidad nueva. La compilación del conjunto completo no cambia: 420 de 434. Ningún otro módulo fue tocado (GOLDEN RULE #9).

• Corregido — un RENAMES de nivel 66 se cualifica por su registro
Un 66 no tiene padre en la jerarquía de niveles, así que se registraba bajo su nombre a secas y quedaba totalmente fuera de la resolución de nombres. De ahí seguían tres cosas: dos registros que declaraban el mismo nombre 66 resolvían al que se hubiera parseado en último lugar, así que MOVE "CALIFORNIA" TO RENAME-5 OF T-RENAMES-DATA aterrizaba en el otro registro; un nombre compartido con un elemento de datos ordinario resolvía al elemento de datos, así que HARRY OF A-GLOB leía un campo sin relación en lugar de la reagrupación; y RENAME-6 IN T-RENAMES-DATA — una lectura cualificada — nunca consultaba la tabla de RENAMES y volvía como 0.
Cada 66 toma ahora como ruta de ancestros el registro que reagrupa, se indexa igual que un elemento de datos y entra en by_leaf, de modo que resolve_canonical lo alcanza con la regla ordinaria de subsecuencia; los operandos de la cláusula RENAMES se resuelven en ese mismo registro. (NC209A MOV-TEST-F2-5, NC252A RENAM-TEST-8/9/10.)

• Corregido — una tabla renombrada aporta todas sus ocurrencias
elem_order guarda una entrada por declaración, así que un elemento OCCURS cubierto aportaba un único slot: 66 RENAME-7 RENAMES ITEM-1 THRU TABLE-2 se releía como "BOSTO" en lugar de "BOSTON MASSACHUSETTS" (NC252A RENAM-TEST-11).

• Corregido — un RENAMES sobre un único elemento es ese elemento
COBOL-85 da a tal elemento la descripción del elemento que renombra. 66 RENAME-12 RENAMES WIDGET-4, donde WIDGET-4 es PIC 9(4), es un elemento numérico de cuatro dígitos — así que ADD 3500 TO RENAME-12 con 8000 dentro desborda. El receptor era la clave propia del RENAMES, que nada relee, así que ON SIZE ERROR nunca se disparaba y además no se almacenaba nada (NC252A RENAM-TEST-16/17).

• Corregido — un nombre de condición 88 sobre un grupo prueba los bytes del grupo
Un grupo no posee slot: el grupo ES sus hijos. Leer su propio slot hacía falsa toda condición declarada sobre un grupo, contuviera lo que contuviera el registro — 01 TABLE-86. 88 B86 VALUE "ABCABC". 02 ... nunca casaba con los seis bytes que el registro realmente contenía (NC250A IF--TEST-87/88).

Post añadido a las 09:39. Post anterior a las 09:38

• Corregido — una constante figurativa se dimensiona al otro operando, VALUE incluido
ALL literal se repite hasta el tamaño del operando con el que se compara, en ambos sentidos: IF IF-D6 EQUAL TO ALL "BA" contra un elemento de diez caracteres es "BABABABABA", no "BA" rellenado con espacios. La misma regla se aplica a una constante figurativa escrita como VALUE de un 88: 88 B VALUE QUOTE sobre un huésped PIC X(4) son cuatro comillas, y 88 D VALUE ALL "BAC" es "BACB". Solo VALUE SPACE había funcionado siempre, porque su relleno resulta ser el carácter que repite (NC250A IF--TEST-4, IF--TEST-26, IF--TEST-28).

• Corregido — un operando de grupo es de categoría alfanumérica
Emparejarlo con un elemento numérico toma la comparación no numérica, en la que el lado numérico se convierte en sus caracteres rellenados por la derecha: PIC 9(5) con 12345 es "12345 " contra un grupo de diez bytes con "0000012345", y desiguales. Se estaban comparando algebraicamente (NC250A IF--TEST-77).

• Corregido — NOT delante de un objeto de abreviatura niega la relación
En una condición de relación combinada abreviada, un NOT seguido de un objeto y no de un operador relacional niega la relación implícita: a > b OR NOT c es a > b OR NOT (a > c). Se leía como una prueba de la verdad propia del objeto, que da la misma respuesta solo cuando el objeto es cero — por eso NC250A IF-TEST-122 pasó siempre e IF-TEST-123, cuyo operando es 12, no.

• Corregido — una serie INSPECT ... REPLACING comparte un solo barrido
El estándar da a una serie de operandos de reemplazo una única inspección compartida de izquierda a derecha, como ya la tenía una serie de conteo: en cada posición los operandos se prueban en el orden escrito, el primero que casa reemplaza esos caracteres y el barrido continúa pasados ellos. Aplicados de uno en uno, cada operando veía la salida del anterior — FIRST "L " BY "ZZ" AFTER INITIAL "AL" borraba justo el "L " sobre el que estaba anclado el operando siguiente, y "BAD" sobrevivía hasta el final (NC216A INS-TEST-F3-19).

• Corregido — un elemento DISPLAY con signo no tiene signo menos entre sus caracteres
INSPECT lee las posiciones de carácter de un elemento, y un elemento DISPLAY con signo lleva su signo como sobreperforación en un dígito, no en una posición propia. Así que INSPECT <PIC S9(5) con -12345> TALLYING ... FOR ALL "-" es 0. El signo se restaura a la salida, de modo que un REPLACING sobre los dígitos no lo cambia. SIGN IS ... SEPARATE CHARACTER no se ve afectado: ahí el signo sí es una posición (NC216A INS-TEST-F1-23).

• Corregido — las superposiciones REDEFINES anidadas se mantienen sincronizadas
Una clave dentro de más de una superposición conservaba solo la última clase construida, y una única bandera global suprimía todo refresco posterior en lugar de solo la escritura que rebotaba de vuelta. Entre ambas cosas, una escritura a través de una redefinición de nivel 01 llegaba al registro redefinido y ahí se detenía: MOVE 11 TO RDFDATA16 nunca alcanzaba el RDF3 REDEFINES RDFDATA3 de su interior, ni el RDF3-5-1 REDEFINES RDF3-5 dentro de aquel, por cuyo 88 pregunta la prueba. Los enlaces llevan ahora todas las clases a las que pertenece una clave, y la guarda bloquea solo la descripción que se está escribiendo (NC252A RDF-TEST-12/13).

Post añadido a las 09:40. Post anterior a las 09:39

• Corregido — PERFORM ... WITH TEST AFTER VARYING, y lo que un bucle VARYING deja tras de sí
Tres reglas separadas de PERFORM VARYING, y la razón por la que NC201A nunca terminaba:
- WITH TEST AFTER se parseaba y se descartaba para la forma VARYING, así que todo bucle así corría con prueba-antes. Un cuerpo que asigna sus propias variables de bucle — NC201A PFM-TEST-F4-14 fija las dos — aumentaba entonces la exterior más allá de su valor de terminación en cada pasada y nunca satisfacía la condición en el punto donde se probaba. Ese único programa era la totalidad de la columna de "timed out" del módulo. La grafía en línea PERFORM WITH TEST AFTER VARYING ... END-PERFORM se rechazaba de plano y ahora parsea. Un identificador AFTER se restablece a su valor FROM cuando termina su bucle, antes de aumentar el nivel inmediatamente exterior, así que una variable interior no sobrevive a su propio bucle; la más exterior sí (PFM-TEST-F4-3, PFM-TEST-F4-4). Y un identificador VARYING con subíndice sigue a su subíndice: se resolvía una sola vez, así que el aumento escribía una ocurrencia fija mientras el UNTIL — evaluado en fresco — leía otra (PFM-TEST-F4-24).

• Pruebas
test_qualified_renames.rs (6), test_condition_operands.rs (8), test_inspect_series_and_signs.rs (5), test_nested_redefines.rs (3) y test_perform_varying_forms.rs (4) son nuevos; cada uno fija la regla y no el programa, y varios fijan el caso que seguía pasando por la razón equivocada.
Las pruebas propias de cobolt-ast vuelven a compilar: tests/test_ast_construction.rs venía inicializando DataDecl y Program sin campos que esos tipos ganaron hace varias sesiones, así que las 31 pruebas de esa crate llevaban tiempo sin ejecutarse. Se suministran los campos que faltaban.

=== PowerRustCOBOL 1.62.43 — 2026-08-28 ===

NIST CCVS85 Sequential I/O (SQ) compila por completo — 85 de 85 — y pasa de 10 -> 44 de 85 en ejecución. Los veinte programas que se estrellaban de inmediato ahora corren todos: compartían un único defecto. Las aserciones pasan de 215 PASS / 190 FAIL a 471 PASS / 162 FAIL, y el único programa que agotaba el tiempo ya no lo hace. Compilación del conjunto completo: 420 -> 422 de 434. Nucleus no cambia: 95 de 95 en ambos ejes, 4 614 aserciones sin ninguna fallida (GOLDEN RULE #9: SQ es el módulo en curso, y el techo de NC se protege re-midiéndolo tras cada cambio). Esta versión restaura además la propiedad Picture del TextBox, presente en las versiones más tempranas y eliminada en algún momento sin causa.

• Corregido — los párrafos de una declarativa conservan sus nombres
Cada párrafo de una sección de DECLARATIVES se aplanaba en una sola lista de sentencias y los nombres se tiraban, así que un manejador USE no podía alcanzar su propio código: PERFORM DECL-FAIL y GO TO OUTPUT-ERROR-PROCESS morían con "undefined paragraph", y el manejador ejecutaba el cuerpo de todos los párrafos en secuencia salvo que un GO TO escapara antes. Ese único defecto es todo el clúster de veinte programas estrellados (SQ122A, SQ132A, SQ226A y diecisiete más).
Un manejador se entra ahora por el principio de su sección y fluye hasta el final de la sección, con sus párrafos nombrados. Estos viven en su propio espacio de nombres, que el estándar impone en ambos sentidos: el control nunca cae del cuerpo principal a una declarativa, y un nombre declarado en ambos resuelve a la copia de la declarativa mientras corre un manejador y a la del cuerpo en el resto de los casos. El cuerpo principal sigue a las declarativas en ese espacio, así que un manejador puede también hacer PERFORM de un párrafo de la porción no declarativa — y toda sección declarativa está incluida en él, porque el manejador de EXTEND de SQ226A ejecuta con PERFORM un párrafo que pertenece a la sección del manejador de OUTPUT.

Eslopes
31 de agosto de 2026, 14:48
• Corregido — un elemento FILE STATUS declarado como grupo recibe el código
COBOL-85 permite que el elemento sea un grupo de dos caracteres, y SQ132A declara 01 SQ-FS1-STATUS sobre dos hijos PIC X. Un grupo se relee desde sus hijos, no desde su propio slot, así que escribir el código en la clave del grupo dejaba a los hijos con lo que el programa hubiera sembrado: IF SQ-FS1-STATUS = "42" comparaba contra esa siembra, y todas las pruebas de estado sobre tal fichero fallaban. Esto es lo que más movió el módulo — 14 -> 38 programas limpios por sí solo — y con ello se despejó también el timeout de 20 segundos de SQ121A.

• Corregido — un OPEN de un fichero ya abierto es 41
Devolvía 00 y reabría el fichero, lo que truncaba silenciosamente un fichero OUTPUT que el programa acababa de escribir. El estado es ahora 41, el fichero queda exactamente como estaba y la declarativa del fichero se dispara (SQ139A, SQ140A, SQ131A).

• Corregido — un READ secuencial tras AT END es 46
Seguir leyendo tras el final devolvía un segundo 10. El AT END no dejó registro siguiente válido, así que el siguiente READ secuencial es 46 — un estado de clase 4, lo que significa que para él no corre ni AT END ni NOT AT END y que la declarativa USE es la que lo maneja. Un OPEN nuevo, o un START con éxito, restablece un registro (SQ136A–SQ138A).

• Corregido — un solo OPEN puede llevar varios grupos de modo
OPEN INPUT SQ-FS1, SQ-FS3 OUTPUT SQ-FS4. es el OPEN multifrase de COBOL-85. El parser leía exactamente un modo y luego recogía nombres de fichero, así que OUTPUT — una palabra clave, no un identificador — quedaba sin consumir y la sentencia se rechazaba: unexpected token in statement: Output. Cada grupo abre ahora en su propio modo, con SHARING / WITH LOCK / REGISTERED USER aplicando a la sentencia. Esto es la totalidad de la ganancia de compilación (SQ128A, SQ206A).

• Corregido — un TextBox declara la PICTURE COBOL que obedece su contenido
La propiedad Picture existía en las primeras versiones y se eliminó en algún momento sin causa, lo que dejaba a un TextBox generando PIC X(512) fuera cual fuera el propósito de la caja: una caja que recogía un importe producía un elemento alfanumérico, toda comparación contra ella seguía las reglas alfanuméricas y el operador podía teclear cualquier cosa dentro.
La propiedad vuelve al TextBox y se respeta en todos los sitios donde el control se dibuja o se genera. Hace dos trabajos. Como validador decide, por posición de carácter, qué bytes son legales — PIC A(3) letras y espacios, PIC 9(3) dígitos, PIC X(3) cualquier cosa —, la lectura de COBOL-85, no la permisiva; la entrada sigue siendo texto plano validado por pulsación, nunca una máscara de separadores que pre-siembra caracteres de agrupación y pasea el cursor por encima de ellos. Como máscara muestra la forma editada en reposo y el valor almacenado plano bajo el cursor, de modo que PIC ZZ9.99 con 12.34 se lee " 12.34" hasta recibir el foco. El separador decimal y el carácter de moneda salen del SPECIAL-NAMES del formulario, así que un formulario en ejecución y el COBOL que genera no pueden discrepar sobre DECIMAL-POINT IS COMMA.
El elemento generado lleva ahora esa misma picture, con lo que una comparación contra él es correcta por construcción y no por una coerción en tiempo de ejecución. Un .cfrm existente no se ve afectado: una Picture vacía significa "no establecida", y la picture efectiva es entonces X(n) derivada de MaximumLength exactamente como antes; donde sí hay picture, su propio ancho es la autoridad y MaximumLength ya no acota el campo. El motor de PICTURE numérico-editada se movió de cobolt-runtime a cobolt-forms (numedit.rs, acompañado del nuevo picture.rs) para que el TextBox y el intérprete editen por el mismo código; cobolt-runtime lo reexporta, dejando intacto cada sitio de llamada crate::numedit::...

Post añadido a las 09:43. Post anterior a las 09:42

• Pruebas
crates/cobolt-runtime/tests/test_declaratives_and_status.rs — seis casos: un manejador haciendo PERFORM y saltando a sus propios párrafos (y no ejecutando el que su GO TO omite), el cuerpo principal sin caer en una declarativa, un FILE STATUS de grupo, el 41 dejando intacto el contenido del fichero, el 46 y su liberación por un OPEN nuevo, y un OPEN de dos grupos. crates/cobolt-codegen/tests/textbox_picture.rs — la picture que declara un TextBox llega al elemento de datos generado, y un formulario escrito sin la propiedad sigue generando lo que siempre generó.

• Documentación
docs/cobol85-supported-syntax-en.md — la entrada de declarativas enuncia ahora la regla de espacio de nombres y la entrada/salida acotada por sección; OPEN gana la forma multigrupo y el 41; READ gana el 46. El marcador incorpora una tabla de ejecución de SQ junto a la de NC, la tabla por módulo y el historial de conformidad se re-miden, y el resumen obsoleto "tres cuartos / 102 restantes" se corrige a 97,2 % / 12. docs/developers-guide-en.md — la nota "un manejador declarativo es lineal, GO TO no está soportado" era errónea y se sustituye por un manejador multipárrafo desarrollado, la salvedad de que las dos porciones nunca se invaden mutuamente, y una tabla de los tres estados de error que solo una declarativa hace visibles.

=== PowerRustCOBOL 1.62.44 — 2026-08-28 ===

NIST CCVS85 Sequential I/O (SQ) pasa de 44 -> 67 de 85 en ejecución, con las aserciones subiendo de 471 PASS / 162 FAIL a 560 PASS / 60 FAIL (74,4 % -> 90,3 %). La compilación se queda en 85 de 85 para SQ y 422 de 434 para el conjunto completo. Nucleus no cambia: 95 de 95 en ambos ejes, 4 614 aserciones sin ninguna fallida (GOLDEN RULE #9: SQ es el módulo en curso, y el techo de NC se re-mide tras cada cambio). Seis correcciones a cómo viaja un registro entre un programa COBOL y un fichero secuencial, todas ellas comportamiento COBOL-85 que faltaba y no capacidad nueva.

• Corregido — registros de longitud variable
La cláusula RECORD del FD se saltaba por completo, así que todo fichero secuencial era una tirada de registros de igual tamaño. Las tres grafías se leen y respetan ahora: RECORD CONTAINS n CHARACTERS — longitud fija, comportamiento sin cambios; RECORD CONTAINS n TO m CHARACTERS — variable, la descripción de registro que nombra el WRITE da la longitud; RECORD [IS] VARYING [IN SIZE] [FROM n] [TO m] [DEPENDING ON item] — el elemento de datos ES la longitud: fijado antes de un WRITE es el número de caracteres escritos, y tras un READ contiene la longitud del registro leído.

FD CUSTOMER-FILE
RECORD IS VARYING IN SIZE FROM 120 TO 151 CHARACTERS
DEPENDING ON RECORD-LENGTH.
01 SHORT-RECORD.
02 CUST-KEY PIC X(120).
01 LONG-RECORD.
02 CUST-KEY-2 PIC X(120).
02 CUST-NOTES PIC X(31).

Un FD cuyos registros 01 son de tamaños distintos es de longitud variable lo diga o no, que es lo que COBOL-85 exige y de lo que depende SQ107A. Un fichero de longitud variable lleva la longitud de cada registro junto al registro; los bytes de un fichero de longitud fija no cambian.

• Corregido — las descripciones de registro de un FD comparten una sola área de registro
Cada 01 bajo un FD describe el mismo almacenamiento. Un WRITE envía ahora esa área entera — donde el registro nombrado tiene FILLER, se trasluce lo que otra descripción de registro puso allí — y un READ entrega los bytes a través de todas las descripciones de registro, así que un FD con un registro corto y uno largo devuelve también los campos extra del largo.

• Corregido — un FILLER ocupa sus bytes en un registro de FD
Un FILLER se saltaba junto con su anchura, así que cada campo posterior quedaba en el offset equivocado: 03 FILLER PIC X(120). 03 EXT-18 PIC X(18). medía 18 bytes con EXT-18 en el offset cero. Un registro que no es más que FILLER — 01 PRINT-LINE. 02 FILLER PIC X(120). — se escribía como 120 espacios por mucho que el programa hubiera movido dentro.

Post añadido a las 09:44. Post anterior a las 09:43

• Corregido — SIGN IS SEPARATE CHARACTER ensancha el elemento en un registro
PIC S9(5) SIGN IS LEADING SEPARATE CHARACTER ocupa seis posiciones de carácter, no cinco. El trazado del registro contaba cinco, así que cada campo posterior quedaba desplazado un byte respecto al área de registro.

• Corregido — READ ... INTO sigue las reglas del movimiento de grupo
READ file INTO identifier es el READ seguido de un MOVE de grupo, así que el registro se distribuye entre los elementos subordinados del receptor y se corta a la anchura propia del receptor. Se escribía en el slot propio del grupo, que nada relee, dejando a los hijos del receptor con sus valores antiguos. Un receptor con subíndice (READ f INTO TABLE-ENTRY (1)) aterriza ahora en el elemento subindexado y no en el primero, y el registro se mueve como bytes, así que un registro que contiene un byte que no es un carácter llega intacto.

• Corregido — REWRITE sobre un fichero secuencial por registros
REWRITE estaba implementado solo para ficheros indexados; sobre un fichero secuencial informaba de un error de E/S permanente y no cambiaba nada. Ahora reemplaza en el sitio el registro que entregó el último READ y no toca la posición de lectura, así que un bucle de leer-modificar-reescribir recorre el fichero exactamente una vez.
Con los estados que el estándar exige: 49 cuando el fichero no está abierto en I-O, 43 cuando ningún READ con éxito estableció un registro — tras AT END, o en un segundo REWRITE sin un READ entre medias — y 44 cuando el registro nuevo no tiene la misma longitud que el leído. En un fichero RECORD ... DEPENDING ON el valor del elemento ES esa longitud, así que cambiarlo y reescribir es la manera en que un programa pide otra longitud, y 44 es la respuesta.

• Corregido — el arnés NIST no podía leer un informe con bytes crudos
nist_conformance run leía el fichero de impresión de cada programa como texto UTF-8. NC107A imprime las constantes figurativas, así que su informe lleva legítimamente bytes HIGH-VALUE (0xFF) y LOW-VALUE (0x00) — el fichero entero se rechazaba y sus 177 aserciones superadas puntuaban como "no report printed". Los informes se leen ahora como bytes y se decodifican con pérdida controlada: un runtime que escribe fielmente el byte que se le mandó escribir ya no parece un fallo.

=== PowerRustCOBOL 1.62.45 — 2026-08-28 ===

NIST CCVS85 Sequential I/O (SQ) pasa de 67 -> 79 de 85 en ejecución, aserciones 560 PASS / 60 FAIL -> 595 PASS / 25 FAIL (90,3 % -> 96,0 %). La compilación no cambia: 85 de 85 para SQ y 422 de 434 para el conjunto completo; Nucleus sigue en 95 de 95 en ambos ejes con 4 614 aserciones y ninguna fallida. De los seis miembros de SQ aún cortos, tres (SQ302M, SQ303M, SQ401M) son pruebas de flagging: no llevan aserciones y puntúan los diagnósticos OBSOLETE / NON-CONFORMING del compilador, 24 de los cuales aún no se emiten. Esos 24 componen la mayor parte del FAIL restante. Cinco piezas más de COBOL-85 que los verbos de fichero le debían al programa.

• Corregido — ON es opcional en una declarativa USE
USE AFTER STANDARD ERROR PROCEDURE OUTPUT. se leía como un cajón de sastre — el parser se saltaba todo hasta ON, y sin un ON presente la palabra de modo de apertura se iba con lo saltado. Un programa que declaraba un manejador para OUTPUT y otro para INPUT ejecutaba por tanto el manejador de OUTPUT sobre un fichero de entrada.

DECLARATIVES.
OUTPUT-ERRORS SECTION.
USE AFTER STANDARD ERROR PROCEDURE OUTPUT.
...
INPUT-ERRORS SECTION.
USE AFTER ERROR PROCEDURE ON INPUT.

Ambas grafías — con ON y sin él — seleccionan ahora por modo.

• Corregido — CLOSE ... REEL / CLOSE ... UNIT no cierra el fichero
REEL y UNIT terminan un volumen de una cinta multivolumen, no el fichero. Se parseaban y se descartaban, así que el fichero se cerraba del todo y el siguiente WRITE o CLOSE fallaba sobre un fichero que el programa aún creía abierto. En disco no hay volúmenes, para lo que el estándar tiene un estado — 07, con éxito pero el fichero no está en un medio de carrete/unidad — y el fichero permanece abierto.

Post añadido a las 09:46. Post anterior a las 09:44

• Corregido — solo OPEN OUTPUT crea un fichero
OPEN I-O y OPEN EXTEND se abrían con "crear si falta", así que abrir un fichero que no estaba informaba de éxito. Ambos informan ahora 35, como ya hacía OPEN INPUT. SELECT OPTIONAL file-name es la excepción, y ahora se respeta: el fichero no necesita estar presente, uno ausente se crea, y el OPEN informa 05 para que el programa lo sepa. OPEN INPUT de un fichero OPTIONAL ausente se comporta como un fichero vacío: el primer READ levanta AT END.

• Corregido — LINAGE-COUNTER vale uno cuando el fichero se abre
Se dejaba en lo que hubiera alcanzado la página anterior, así que un programa que cerraba y reabría su fichero de impresión veía un número de línea rancio. Abrir el fichero lo posiciona en la línea uno.

• Corregido — una longitud de registro fuera del rango declarado es una violación de límites
Los valores de RECORD ... DEPENDING ON se recortaban al rango FROM ... TO del FD, lo que convertía calladamente una longitud que el FD prohíbe en una legal. La longitud se usa ahora tal como la fijó el programa, y una fuera del rango es 44 sin que nada se escriba — que es también lo que permite que un REWRITE pida una longitud distinta y reciba un no.

=== PowerRustCOBOL 1.62.46 — 2026-08-28 ===

NIST CCVS85 Sequential I/O (SQ) alcanza 84 de 85 en ejecución — aserciones 595 PASS / 25 FAIL -> 623 PASS / 1 FAIL (99,8 %) — con todos los programas corriendo ya hasta el final. La compilación sigue en 85 de 85 para SQ y 422 de 434 para el conjunto completo; Nucleus sigue en 95 de 95 en ambos ejes, 4 614 aserciones, ninguna fallida. El único miembro aún corto, SQ203A, necesita XXXXD001 — un fichero de datos que suministra la instalación del CCVS85. Ningún miembro del conjunto lo escribe, así que la mitad "fichero presente" de su prueba de SELECT OPTIONAL no puede correr aquí; la mitad "fichero ausente" pasa.

• Corregido — una cláusula LINAGE puede nombrar elementos de datos, no solo números

FD PRINT-FILE
LINAGE LINAGE-CTR
FOOTING FOOT-CTR
TOP TOP-CTR
BOTTOM BOTTOM-CTR.

Cada parte de la cláusula puede nombrar un elemento en lugar de declarar un entero, de modo que un programa puede dimensionar su página en tiempo de ejecución. Exigir enteros hacía que la cláusula entera no parseara, y el fichero quedaba entonces sin página alguna: AT END-OF-PAGE nunca podía hacerse verdadero, así que un informe escrito con WRITE PRINT-REC AFTER ADVANCING 1 LINE AT END-OF-PAGE PERFORM PAGE-TRAILER END-WRITE, en bucle hasta el fin de página, no paraba nunca. Los NIST SQ208M y SQ210M escribieron cada uno gigabytes antes de que el arnés los matara; ambos corren ahora hasta el final. La página se mide a partir de los elementos nombrados en cada escritura, así que cambiar uno entre escrituras cambia la página.

• Añadido — flagging de conformidad para los elementos de E/S secuencial
nist_conformance flag informa de las construcciones que COBOL-85 lista como obsoletas o por encima del subconjunto alto. Los elementos de E/S secuencial no se detectaban en absoluto; ahora se detectan todos, sin ningún flag espurio en todo el conjunto.
- Obsoletos — RERUN, MULTIPLE FILE TAPE, LABEL RECORDS, VALUE OF, DATA RECORDS, OPEN ... REVERSED.
- Por encima del subconjunto alto — SELECT OPTIONAL, RESERVE, PADDING CHARACTER, RECORD DELIMITER, SAME AREA, MULTIPLE FILE TAPE, BLOCK CONTAINS n TO m (el fijo BLOCK CONTAINS n está dentro del subconjunto), RECORD VARYING, LINAGE, VALUE OF, CLOSE ... FOR REMOVAL, OPEN/CLOSE ... WITH NO REWIND, CLOSE ... WITH LOCK, OPEN ... REVERSED, OPEN EXTEND, READ ... NEXT RECORD, WRITE ... AT END-OF-PAGE.
Nada de esto cambia lo que compila o corre: cada construcción listada sigue funcionando exactamente igual que antes. El flagging solo informa de dónde la sitúa el estándar, y sigue siendo un punto de entrada opcional y no parte de una compilación ordinaria. SQ302M pasa de 0 -> 4 de 4, SQ303M de 0 -> 2 de 2 y SQ401M de 0 -> 18 de 18; NC401M no cambia: 40 de 40.

=== PowerRustCOBOL 1.62.47 — 2026-08-29 ===

Post añadido a las 09:47. Post anterior a las 09:46

NIST CCVS85 Sequential I/O (SQ) está completo — 85 de 85 en ambos ejes —, con las aserciones en 624 PASS / 0 FAIL (100 %), desde 623 PASS / 1 FAIL. Nucleus no se mueve: 95 de 95 en ambos ejes, 4 614 aserciones, ninguna fallida, y el censo de compilación del conjunto completo sigue en 422 de 434.

El último miembro, SQ203A, prueba SELECT OPTIONAL con el fichero tanto presente como ausente. La mitad presente lee XXXXD001, un fichero de datos que suministra la instalación del CCVS85 — ningún miembro del conjunto lo escribe, así que en un directorio recién creado esa mitad no podía correr en absoluto y el programa informaba, correctamente, de la ausencia como un fallo.

El arnés de pruebas planta ahora el fichero, de la misma manera en que ya suministra los mazos de operador, los ajustes de interruptores externos y la tarjeta RERUN XXXXX053. El registro es del propio conjunto, no de las aserciones: el READ-TEST-GF-04 del propio SQ203A construye unos párrafos más adelante un fichero exactamente de esta forma para SQ-FS3, y cada campo se fija tal como lo fija ese párrafo contra el esqueleto FILE-RECORD-INFO del programa. Solo dos difieren, ambos forzados por cuál es este fichero: XFILE-NAME nombra SQ-FS1, y RECORDS-IN-FILE dice 1 porque el fichero contiene un registro.

Tres pruebas fijan el registro: que es exactamente un registro ASCII de 120 bytes (un byte de más caería pasado todo lo que SQ203A lee, dando por bueno un fichero malformado), que cada campo nombrado está donde lo colocan las anchuras PICTURE del esqueleto, y que ningún otro miembro recibe un fichero plantado. El comportamiento del runtime no cambia: esta versión mueve la medición, no el compilador.

=== PowerRustCOBOL 1.62.48 — 2026-08-29 ===

NIST CCVS85 Indexed I/O (IX) sube de 13 a 16 de 42 en ejecución — aserciones 341 PASS / 439 FAIL -> 354 PASS / 424 FAIL — sin ningún cambio en el runtime. Esta versión corrige la medición, no el compilador.

Unos pocos programas del CCVS85 son la segunda mitad de una pareja: leen un fichero de datos que escribió un miembro anterior, y cada uno lo dice en el comentario de su propia cabecera. IX110A abre con "THE ROUTINE USES THE FILE IX-FS3 WHICH HAS BEEN CREATED BY IX109", y una docena de miembros IX comparten el fichero XXXXX024 que escribe IX109A. El arnés ejecutaba cada programa en un directorio propio, así que cada consumidor abría un fichero que no estaba y reportaba, correctamente, la ausencia como fallo. IX110A puntúa 2 FAIL / 2 PASS en solitario y 4 PASS / 0 FAIL con su productor ejecutado primero, contra un runtime sin cambios.

Cada miembro de este tipo declara ahora su productor, citado de su propia cabecera, y el arnés ejecuta ese productor — transitivamente — dentro del directorio del consumidor primero. Diez miembros declaran uno: IX102A, IX110A, IX114A–IX120A e IX202A.

Compartirlo todo habría sido un error, y medirlo lo zanjó: dar a un módulo entero un único directorio compartido llevó IX a 15, pero rompió miembros que necesitan que un fichero esté ausente — IX111A ("THIS PROGRAM USES THE FILE IX-NOP WHICH DOES NOT EXIST", esperando el estado 35) e IX216A (OPEN EXTEND sobre un fichero OPTIONAL, esperando 05) se pusieron en rojo contra restos de otros programas. Una instalación validadora borra los ficheros entre programas salvo donde un miembro declara que hereda uno, que es lo que la tabla codifica — así que todos los demás programas conservan el directorio limpio que tenían. Dos pruebas guardan la tabla: que ninguna cadena de productores forma ciclos ni deja de terminar, y que un miembro autocontenido no declara nada — IX111A, IX112A, IX113A e IX216A entre ellos, donde un fichero plantado es exactamente lo que la prueba prohíbe.

Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallida.

=== PowerRustCOBOL 1.62.49 — 2026-08-29 ===

Post añadido a las 09:48. Post anterior a las 09:47

Los ficheros indexados obedecen las reglas de ordenación del modo de acceso secuencial — NIST CCVS85 Indexed I/O (IX) pasa de 16 a 19 de 42 en ejecución, aserciones 354 PASS / 424 FAIL -> 358 PASS / 420 FAIL. Tres reglas de COBOL-85 que un fichero INDEXED declarado ACCESS MODE IS SEQUENTIAL no estaba imponiendo:
- WRITE exige orden ascendente de RECORD KEY. Una clave que no es mayor que la anterior es el estado 21 y el registro no se escribe. Una escritura rechazada no hace avanzar la secuencia, así que la clave siguiente se juzga contra la última clave realmente escrita. Bajo acceso RANDOM o DYNAMIC se sigue permitiendo cualquier orden, y un choque con un registro existente sigue siendo 22.
- REWRITE reemplaza el registro que entregó el READ anterior, así que tiene que haber uno: sin un READ ejecutado con éxito inmediatamente antes, el estado es 43. El REWRITE consume el registro, así que un segundo REWRITE sin un READ intermedio es 43 otra vez.
- DELETE sigue la misma regla, y es 43 en los mismos términos.

START posiciona el fichero pero no entrega registro, así que un REWRITE o DELETE tras uno sigue siendo 43 — igual que la primera sentencia de ese tipo tras OPEN, tras un WRITE y tras un READ sin éxito. Esto es lo que IX120A comprueba reescribiendo con sus READ comentados y esperando que su declarativa USE AFTER EXCEPTION se entre con el estado 43, y lo que IX109A comprueba escribiendo las claves 1...50 y luego la 49. IX109A, IX119A e IX120A corren ahora limpios.

Cinco pruebas cubren las reglas directamente, incluida que una escritura rechazada deja el fichero sin cambios y que el acceso DYNAMIC no se ve afectado. Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallida. La compilación del conjunto completo sigue en 422 de 434.

=== PowerRustCOBOL 1.62.50 — 2026-08-29 ===
• Las claves de registro de un fichero se distinguen por su calificador OF/IN
Las aserciones de Indexed I/O de NIST CCVS85 pasan de 358 PASS / 420 FAIL a 361 PASS / 417 FAIL; los programas que ejecutan limpio se mantienen en 19 de 42. COBOL permite que un fichero declare varias claves cuyos data-names son idénticos y que solo se separan por el grupo en el que se encuentra cada una. IX-FD3 de IX215A hace exactamente eso: RECORD KEY IS IX-FD3-KEY IN IX-FD3-RECKEY-AREA, y después dos claves alternas también llamadas IX-FD3-KEY, calificadas en IX-FD3-ALTKEY1-AREA e IX-FD3-ALTKEY2-AREA. El parser descartaba el calificador y toda búsqueda iba por el nombre a secas, así que las tres claves resolvían al primer campo: el fichero llevaba tres índices sobre un mismo conjunto de bytes, y una lectura por una clave alterna buscaba su valor en los caracteres de la clave primaria.

El calificador forma ahora parte de la identidad de la clave de extremo a extremo:
- El parser conserva la cadena OF/IN escrita tras RECORD KEY IS y tras cada ALTERNATE RECORD KEY IS.
- La disposición de bytes del registro anota los grupos que contienen cada campo, de modo que un campo puede encontrarse por nombre y por ascendencia. La coincidencia es por contención, como exige el estándar — B OF D nombra el campo incluso cuando está en B, dentro de C, dentro de D — y un nombre sin calificar sigue tomando el primer campo con ese nombre.
- Leer y escribir un campo pasa por su clave de almacenamiento calificada, así que un registro que tiene el mismo nombre en varios grupos ya no dirige todos ellos al que casualmente quedó almacenado bajo el nombre a secas.
- READ … KEY IS y START … KEY IS eligen la clave de referencia por data-name y calificador juntos, que es lo único que distingue las tres claves de IX-FD3.

Una clave cuyo calificador no nombra ningún grupo de la disposición sigue resolviendo por el nombre a secas, así que nada de lo que funcionaba antes se pierde. Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. La compilación de la suite completa sigue en 422 de 434, y la suite de cobolt-runtime está en verde.

Eslopes
31 de agosto de 2026, 14:57
=== PowerRustCOBOL 1.62.51 — 2026-08-29 ===
• La Guía del Desarrollador cubre el comportamiento de ficheros indexados publicado en 1.62.49 y 1.62.50
(GOLDEN RULE #3: un cambio observable por el desarrollador actualiza la guía.) Dos secciones nuevas en "Indexed files — a first-class resource":
- Qué cambia ACCESS MODE al escribir y actualizar. Las reglas de orden que impone SEQUENTIAL y que RANDOM/DYNAMIC no imponen: WRITE en orden ascendente de RECORD KEY o 21; REWRITE/DELETE solo inmediatamente después de un READ con éxito o 43. Con un ejemplo desarrollado y las tres trampas: un WRITE rechazado no avanza la secuencia, una clave igual es 21 y no 22, y START no satisface el requisito del READ. Lleva la advertencia de que 43 es de clase 4, así que una cláusula INVALID KEY no lo capturará.
- Distinguir claves con el mismo nombre mediante OF / IN. Declarar y usar claves que comparten data-name y difieren solo por su grupo, incluyendo que la calificación es por contención y no por parentesco inmediato.

Solo el canónico en inglés: la GOLDEN RULE #8 sigue suspendida, así que no se toca ningún fichero de traducción.

=== PowerRustCOBOL 1.62.52 — 2026-08-29 ===
• Un elemento REDEFINES ya no ocupa bytes propios en un registro FD
Las aserciones de Indexed I/O de NIST CCVS85 pasan de 361 PASS / 417 FAIL a 343 PASS / 133 FAIL, elevando la tasa de aciertos del 46.4 % al 72.1 %. IX215A por sí solo cae de 297 fallos a 15.

Un elemento que redefine es otra descripción de un almacenamiento que ya existe: no añade nada al registro y no desplaza lo que le sigue. La disposición del registro ignoraba REDEFINES por completo y daba a cada uno de esos elementos bytes propios, con lo que cada uno desviaba todos los campos posteriores en su anchura. IX-FD1 de IX215A describe una clave de registro de 13 bytes y, encima de ella, IX-REDF-RECKEY REDEFINES IX-FD1-KEY: el registro crecía 13 bytes, y las dos claves alternas declaradas después indexaban las columnas equivocadas de cada registro del fichero.

El recorrido de disposición coloca ahora el elemento que redefine en el offset del elemento redefinido y continúa donde el registro ya estaba. El objetivo se busca entre los hermanos ya colocados, de modo que una redefinición de una redefinición resuelve correctamente — IX-FD1 tiene una: R-REDF-RECKEY-1-7 REDEFINES R-RECKEY-1-7, a su vez dentro de un elemento que redefine la clave de registro. Un REDEFINES que nombra algo que no está en ese nivel se dispone donde caía, como antes. Tres tests fijan la disposición: que una redefinición comparte los bytes de su objetivo y el campo siguiente no se desplaza, que una redefinición de una redefinición resuelve en su propio nivel, y que se anotan los grupos que contienen cada campo para distinguir campos homónimos en grupos distintos. Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando; la compilación de la suite completa sigue en 422 de 434 y la suite de cobolt-runtime está en verde.

=== PowerRustCOBOL 1.62.53 — 2026-08-29 ===
• Un RECORD KEY que nombra un grupo ahora indexa los bytes de ese grupo
Las aserciones de Indexed I/O de NIST CCVS85 pasan de 343 PASS / 133 FAIL a 475 PASS / 122 FAIL, elevando la tasa del 72.1 % al 79.6 % — y el número de tests que los programas borran por fallar su propia preparación cae de 108 a 1, así que ahora se ejecuta de verdad una parte mucho mayor de la suite. La mayoría de las claves de registro en COBOL nombran un grupo, no un elemento elemental: la de IX214A es IX-FS1-KEY, tres elementos subordinados a lo largo de 13 bytes. La disposición del registro solo listaba los campos elementales, así que una clave de grupo no se encontraba en ninguna parte.

Post añadido a las 09:51. Post anterior a las 09:50

Fallaban dos cosas a la vez: el motor recurría a indexar el offset 0 del registro entero, con lo que cada clave del fichero cubría la imagen completa de 240 bytes del registro; y leer el valor de la clave no encontraba ningún elemento con ese nombre y devolvía espacios, así que START buscaba en el fichero una clave en blanco y devolvía 23 cada vez. El READ posterior entregaba el registro sobre el que casualmente estuviera el cursor, y el programa informaba "TEST IMPROPERLY INITIALIZED" y se borraba a sí mismo.

La disposición anota ahora la extensión de cada grupo junto a los campos elementales, y una clave resuelve a un grupo cuando ningún campo elemental coincide — los elementos elementales se buscan primero, así que nada que ya resolvía cambia. Los bytes de un grupo se piden como grupo, puesto que un grupo no tiene valor propio: sus caracteres son los de sus hijos, concatenados. Leer y escribir registros no se toca: solo la resolución de claves consulta las nuevas extensiones de grupo. Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando; la compilación de la suite completa sigue en 422 de 434 y la suite de cobolt-runtime está en verde.

=== PowerRustCOBOL 1.62.54 — 2026-08-29 ===
• El trabajo de conformidad NIST se convierte en un bucle reanudable en lugar de un relevo por sesión
Sin cambio de runtime; es infraestructura de proceso. Dos artefactos nuevos:
- NIST/progress.json — el libro de registro de conformidad, versionado en el repositorio y desde ahora la única fuente de verdad sobre el estado de la suite. Lleva las cifras de compilación y ejecución de cada módulo, las líneas base protegidas que todo cambio futuro debe preservar, una work_queue ordenada por prioridad con la evidencia y el esbozo de arreglo de cada elemento, una lista dead_ends para que una sesión nueva nunca reintente un enfoque fallido, y el historial de versiones. Sustituye a los ficheros HANDOFF-*.md escritos a mano, que se rederivaban desde cero en cada sesión y divergían.
- .claude/skills/nist-grind/SKILL.md — el protocolo del bucle, para que cada sesión y cada subagente arranquen idénticos: orientarse desde el libro de registro, tomar el primer elemento de trabajo, diagnosticar (fan-out permitido, solo lectura), reducir a una reproducción mínima, arreglar una sola cosa, probar, ejecutar la regresión completa, pasar la puerta de las líneas base protegidas, añadir un test de regresión, subir la versión, changelog, actualizar el libro de registro, commit, push, repetir.

La puerta es lo que hace seguro ejecutarlo sin supervisión: NC 95/95 con 4 614 PASS / 0 FAIL y SQ 85/85 con 624 PASS / 0 FAIL deben mantenerse tras cada cambio, y un arreglo que rompa una de ellas se revierte y se registra como callejón sin salida en lugar de perseguirse hacia delante. La skill codifica las trampas que esta suite ya ha tendido: invocaciones de cargo separadas para rcrun y el harness, que zsh no divide en palabras las variables sin comillas, que un test borrado no es un test que pasa, y que las limitaciones del harness parecen exactamente regresiones del runtime. Fusionar a main y publicar en el foro quedan fuera del bucle: ambos requieren una petición explícita.

=== PowerRustCOBOL 1.62.55 — 2026-08-29 ===
• START acepta una clave genérica (parcial)
Las aserciones de Indexed I/O de NIST CCVS85 pasan de 475 PASS / 122 FAIL a 481 PASS / 115 FAIL, del 79.6 % al 80.7 %. COBOL-85 permite que START … KEY IS <op> data-name nombre un elemento subordinado de la clave de registro — su parte más a la izquierda — y el fichero queda entonces posicionado sobre ese prefijo. En su lugar, la clave se rellenaba con espacios hasta la anchura completa, lo que rompía ambas relaciones: EQUAL TO sobre un elemento de cinco caracteres buscaba una clave de trece bytes terminada en ocho blancos — ningún registro la tiene, así que devolvía 23 cada vez, y el READ posterior entregaba el registro sobre el que casualmente estuviera el cursor — y GREATER THAN comparaba contra esos blancos y se detenía en el primer registro que compartía el prefijo en lugar de pasarlos todos.

Post añadido a las 09:53. Post anterior a las 09:51

El posicionamiento usa ahora el prefijo: EQUAL cae en el primer registro cuya clave empieza por el valor, GREATER THAN en el primero pasados todos los registros que lo comparten, y NOT LESS THAN en el primero cuyo prefijo lo alcanza. Nombrar la clave completa es el caso particular en el que el prefijo es la clave entera, así que el START ordinario no cambia. READ … KEY IS no se toca: solo START lee una clave de forma genérica. Arreglado en los tres motores indexados — el almacén en memoria, el B+tree PRCIDXD1 y el almacén redb — puesto que cada uno implementa el posicionamiento por su cuenta.

De esto depende el START-INITIALIZE-RECORD de IX214A; al fallar, el programa borraba sus propios tests en lugar de informar de ellos. La Guía del Desarrollador gana una sección "Positioning on part of a key". Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando; la compilación de la suite completa sigue en 422 de 434 y la suite de cobolt-runtime está en verde.

=== PowerRustCOBOL 1.62.56 — 2026-08-29 ===
• Una clave de referencia puede nombrarse a través de un REDEFINES
Las aserciones de Indexed I/O de NIST CCVS85 pasan de 481 PASS / 115 FAIL a 484 PASS / 112 FAIL, del 80.7 % al 81.2 %. Una clave es el almacenamiento que nombra, no el nombre en sí, y REDEFINES da a los mismos bytes un segundo nombre. IX215A declara ALTERNATE RECORD KEY IS IX-FD1-ALTKEY1 y luego hace START sobre IX-REDF-ALTKEY1 REDEFINES IX-FD1-ALTKEY1. Cotejar el operando de START/READ solo contra los nombres declarados dejaba esa sentencia en la clave de referencia 0 — buscando en el índice primario los caracteres de una clave alterna — así que tomaba el camino de INVALID KEY cada vez.

Cuando el operando no es uno de los nombres de clave declarados, su rango de bytes en el registro se compara ahora con la extensión de cada clave declarada, y un elemento que cubre exactamente los bytes de una clave es esa clave. Nombrar directamente una clave declarada sigue coincidiendo primero por nombre, así que nada de lo que funcionaba antes cambia.

Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. La compilación de la suite completa sigue en 422 de 434, y la suite de cobolt-runtime está en verde.

=== PowerRustCOBOL 1.62.57 — 2026-08-29 ===
• Una tarjeta X se identifica por su número, no por la letra en la posición 5
Fidelidad del harness de pruebas; sin cambio de runtime. El mazo CCVS85 escribe la tarjeta 24 de tres maneras — XXXXX024 (43 veces), XXXXP024 (6) y XXXXD024 (2) — y la propia cabecera de IX103A la lista una sola vez, como "X-24 INDEXED FILE IMPLEMENTOR-NAME IN ASSGN TO CLAUSE FOR DATA FILE IX-FS1". Una instalación en proceso de validación sustituye cada tarjeta por un único nombre de implementador, así que las tres grafías son un mismo fichero. El harness las dejaba como tres, lo que rompía en silencio una cadena que la suite declara: IX102A crea IX-FS1 como XXXXP024, IX103A procesa "THE FILE USED IS THAT RESULTING FROM IX102" como XXXXD024, y cada lectura secuencial daba AT END en la primera llamada porque el fichero que abría nunca se había escrito.

Las grafías P y D colapsan ahora sobre la canónica XXXXX nnn — los mismos ocho caracteres, porque un operando más corto arrastraría el área de secuencia de las columnas 73-80 al área de contenido de un mazo de formato fijo. XXXXY382 y XXXXY066 se dejan en paz: sus números no coinciden con ninguna otra tarjeta. IX103A entra además en la tabla de productores, citando su propia cabecera.

Post añadido a las 09:54. Post anterior a las 09:53

Las aserciones de IX pasaron de 484 PASS / 112 FAIL a 482 PASS / 114 FAIL, y eso es una ganancia, no una pérdida: el barrido de borrado de IX103A alcanza ahora 35 registros en lugar de morir en el primer READ, y dos aserciones que jamás se habían ejecutado ahora corren — y fallan con verdad. Un test que nunca corrió nunca estuvo pasando; los fallos restantes de IX103A son reales y son lo siguiente a arreglar. El libro de registro fija la regla que esto zanja: la puerta de regresión es absoluta para los módulos terminados y el censo de compilación, pero el módulo en curso puede puntuar legítimamente más bajo cuando la medición se vuelve más honesta — y eso debe declararse, nunca ocultarse ni revertirse para proteger una cifra. Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando; la compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.58 — 2026-08-29 ===
• Borrar durante un recorrido secuencial ya no salta el registro siguiente
Un arreglo de conformidad COBOL-85 en el motor STORAGE IS DISK, demostrado con una reproducción reducida y fijado con un test de regresión. El cursor de lectura del motor de disco era un slot (página hoja, índice de entrada), y DELETE quitaba la entrada sin tocarlo. Una hoja de B+tree desplaza sus entradas a la izquierda al eliminar, así que el registro que estaba en idx + 1 cae en idx y el siguiente READ le pasa por encima. Un recorrido que borraba uno de cada cuatro de 20 registros solo veía 16 de ellos: cada borrado costaba un registro sin leer.

El recorrido se reanuda ahora desde la clave que había alcanzado, capturada antes de que los índices se muevan: reorganice lo que reorganice el borrado, "la entrada en esta clave o después de ella" nombra después el mismo registro que antes, cosa que un índice de slot no hace. Dos detalles que la medición obligó a precisar: la reanudación compara la clave almacenada completa, no sus primeros bytes de longitud de clave — una clave alterna WITH DUPLICATES lleva un id de registro al final, y una comparación por prefijo saltaba todos los registros que compartían el valor alterno en lugar de solo la entrada borrada; seis aserciones de IX215A dejaron de ejecutarse cuando esto entró por primera vez — y la reanudación solo se arma para la forma secuencial, en la que el registro borrado es el que el recorrido acaba de leer: un DELETE por clave elimina otro registro mientras el cursor está en otra parte, esa entrada sigue existiendo y avanzar desde su slot sigue siendo correcto. Los motores en memoria y redb usan claves en sus cursores y nunca estuvieron afectados.

Las puntuaciones NIST no cambian: IX 482 PASS / 114 FAIL, 19 de 42 — los programas que mostrarían esta ganancia todavía se detienen antes por otras razones (el fichero heredado de IX103A es corto, que es lo siguiente a perseguir). Es un defecto real arreglado por su propia evidencia, no un movimiento de puntuación. Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando; la compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.59 — 2026-08-29 ===
• Una cadena de productores se ejecuta en el directorio del propio consumidor
Fidelidad del harness de pruebas; sin cambio de runtime. Indexed I/O de NIST CCVS85 pasa de 482 PASS / 114 FAIL a 483 PASS / 113 FAIL. run_producers hacía recursión con cada productor como nuevo objetivo y derivaba el directorio de trabajo de ese nombre, así que cada generación aterrizaba en un sitio distinto. IX103A hereda de IX102A, que hereda de IX101A: IX102A corría en un directorio donde IX101A nunca había corrido, procesaba un fichero vacío y dejaba tras de sí uno corto. El barrido de IX103A alcanzaba entonces 35 registros de 500 — mientras IX101A e IX102A informaban perfectamente limpios, porque cada uno hizo exactamente lo que se le pidió en el directorio que se le dio.

Post añadido a las 09:56. Post anterior a las 09:54

La cadena es ahora plana y del más antiguo al más nuevo, y cada miembro de ella corre en el único directorio que pertenece al consumidor. Las cifras del harness para IX103A coinciden ahora exactamente con una ejecución manual de IX101A -> IX102A -> IX103A.

Esto aísla un defecto real del runtime que estaba oculto tras el del harness: IX103A lee secuencialmente su fichero de 500 registros y cuenta 502 registros; un lector manual sobre el mismo fichero cuenta exactamente 500. El recorrido entrega dos de más. Es ahora el primer elemento de runtime, registrado con tres causas ya descartadas por la evidencia: no es el fichero, no es el cursor de borrado arreglado en 1.62.58 y ya no es la cadena. Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando; la compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.60 — 2026-08-29 ===
• ORGANIZATION IS y la palabra KEY son palabras opcionales

El módulo Indexed I/O de NIST CCVS85 salta de 19 a 22 de 42 programas ejecutando limpio, con aserciones 483 PASS / 113 FAIL -> 512 PASS / 81 FAIL (81.0 % -> 86.3 %) — la mayor ganancia individual de esta tanda. COBOL-85 escribe las dos cláusulas como [ORGANIZATION IS] {SEQUENTIAL | LINE SEQUENTIAL | RELATIVE | INDEXED} y RECORD [KEY] [IS] data-name. Ambas partes entre corchetes pueden omitirse, e IX103A omite las dos — su cabecera lo dice sin rodeos: "SELECT ... INDEXED ... (WITHOUT THE OPTIONAL WORD <ORGANIZATION>)".

Ninguna de las dos omisiones hacía fallar la compilación, y eso es lo que hizo caro encontrar el problema. Un INDEXED a secas caía fuera del despacho de cláusulas y se descartaba, así que el fichero conservaba la organización SEQUENTIAL por defecto y un fichero indexado se abría como un simple flujo de bytes: un recorrido de un fichero de 500 registros entregaba 870 "registros" delimitados por saltos de línea. Un RECORD data-name a secas se descartaba igual, dejando el fichero sin clave alguna, de modo que el motor indexaba el registro completo y un OPEN de un fichero escrito con una clave real informaba del desajuste de esquema con el estado 39. RECORD DELIMITER IS empieza con la misma palabra y está excluida explícitamente, para que su operando no se confunda con una clave.

Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. La compilación de la suite completa sigue en 422 de 434 — estos programas siempre compilaron; simplemente hacían lo incorrecto.

=== PowerRustCOBOL 1.62.61 — 2026-08-29 ===
• Se implementa SAME RECORD AREA

Indexed I/O de NIST CCVS85 pasa de 22 a 24 de 42 programas ejecutando limpio; los fallos bajan de 81 a 79. Los ficheros nombrados juntos en una cláusula SAME [RECORD] AREA de I-O-CONTROL comparten una única área de registro: sus niveles 01 describen el mismo almacenamiento, así que un READ de cualquiera de ellos es visible a través de los nombres de registro de todos. IX205A enuncia la expectativa en su propia nota — "IN TESTING THE SAME AREA CLAUSE THE RECORD AREA SHOULD BE SHARED BY BOTH FILES IX-FD1 AND IX-FD2, THEREFORE FILE IX-FD2 IS READ AND THE RECORD IDENTIFIED FOR IX-FD1 IS ACCESSED" — e IX206A prueba lo mismo.

Post añadido a las 09:57. Post anterior a las 09:56

La cláusula no estaba modelada en ninguna parte: el parser leía FILE-CONTROL y dejaba el resto de la INPUT-OUTPUT SECTION a un cajón de sastre, así que el párrafo I-O-CONTROL entero se saltaba y la cláusula no hacía nada. Igual que con las palabras opcionales de 1.62.60, esto nunca hizo fallar una compilación — los programas construían limpio y, en silencio, hacían lo incorrecto. Ahora el AST lleva los grupos, el parser lee un párrafo I-O-CONTROL y sus cláusulas SAME, y un READ con éxito deposita la imagen del registro en las descripciones de registro de todos los ficheros que comparten el área. Las palabras calificadoras son todas opcionales en cualquier combinación — IX205A la escribe en su forma más abreviada, SAME RECORD IX-FD1 IX-FD2., sin AREA ni FOR. RERUN y las demás cláusulas de I-O-CONTROL se siguen consumiendo e ignorando, como antes. El SAME AREA a secas comparte además los búferes de los ficheros y restringe cuáles pueden estar abiertos a la vez; los miembros de aquí usan la forma RECORD con ambos ficheros abiertos, así que solo esa está implementada.

Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. La compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.62 — 2026-08-29 ===
• SELECT OPTIONAL se aplica también a los ficheros con clave

Indexed I/O de NIST CCVS85 pasa de 24 a 27 de 42 programas ejecutando limpio; aserciones 512 PASS / 79 FAIL -> 517 PASS / 71 FAIL (86.6 % -> 87.9 %). Un fichero declarado SELECT OPTIONAL no necesita existir cuando el programa corre: se abre de todos modos y el programa se entera por el estado 05. Esa regla estaba escrita solo para la rama secuencial de OPEN. La rama INDEXED pasaba tal cual el estado del propio motor, así que un OPEN EXTEND de un fichero que no estaba informaba un 00 liso y el programa no podía distinguir los dos casos — IX216A comprueba exactamente esa distinción.

OPEN INPUT está incluido: el motor rechazaría de otro modo un fichero ausente con 35, así que el contenedor se materializa abriéndolo I-O y las lecturas lo encuentran entonces vacío y levantan AT END — la misma respuesta que ya da la rama secuencial, donde INPUT sobre un fichero OPTIONAL usa create(true). Un fichero que sí está presente sigue abriéndose con un 00 liso; 05 significa ausente-y-creado y nada más. Tres programas quedaron limpios donde se predijo uno — IX216A, IX217A e IX218A: los otros dos fallaban por la misma regla sin que se hubiera diagnosticado por separado.

Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. La compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.63 — 2026-08-29 ===
• Un REWRITE secuencial no puede cambiar la clave del registro

Indexed I/O de NIST CCVS85 pasa de 27 a 28 de 42 programas ejecutando limpio; aserciones 517 PASS / 71 FAIL -> 518 PASS / 70 FAIL. En el modo de acceso secuencial un REWRITE reemplaza el registro que entregó el último READ, así que la clave que lleva debe seguir siendo la de ese registro. Una distinta levanta la condición INVALID KEY con el estado 21. Se estaba informando 92, un error de lógica — un código de implementador de clase 9 que ninguna frase INVALID KEY maneja y que IX119A, que espera 21 o 22, no podía aceptar.

Bajo acceso RANDOM o DYNAMIC el registro se direcciona por la clave que lleva, así que no hay nada con lo que discrepar y la regla no aplica; un REWRITE con clave no se ve afectado. Corregido en los tres motores indexados — el almacén en memoria, el B+tree PRCIDXD1 y el almacén redb — puesto que cada uno comprueba la clave por sí mismo.

Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. La compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.64 — 2026-08-29 ===
• La frase condicional de una sentencia cubre una condición, no cualquier fallo

Eslopes
31 de agosto de 2026, 15:06
Indexed I/O de NIST CCVS85 pasa de 28 a 29 de 42 programas ejecutando limpio. Un READ secuencial tiene AT END, que es el estado 10 y nada más; uno con clave tiene INVALID KEY, estados 21-24. Cualquier otro error — 30, 47, 48, 49, 92 — es uno que la sentencia no puede manejar: no corre ninguna de las dos frases y lo atiende la declarativa USE del fichero.

La frase de fallo se estaba ejecutando para cualquier estado distinto de cero, y ejecutarla contaba además como haber manejado la condición, lo que suprimía la declarativa. Así, un READ de un fichero que nunca se abrió tomaba el camino de AT END e IX114A nunca veía su manejador para el estado 47. El caso del 46 ya se había tratado como caso especial antes de esto; es la misma regla, ahora enunciada una sola vez.

Una aserción se movió en sentido contrario, y vale la pena decirlo con claridad. IX203A pasó de 8 fallos a 9. Su bucle de recorrido es READ IX-FD1 NEXT RECORD AT END GO TO ... con una válvula de seguridad tras 501 iteraciones, y su READ NEXT falla en todas las llamadas — el área de registro nunca se llena. La frase AT END atrapaba ese primer fallo y salía temprano del bucle; desaparecido el enmascaramiento, el bucle corre hasta su válvula y cuenta 502 registros que nunca recibió. El defecto siempre estuvo ahí y ya hacía fallar tres aserciones; una cuarta ahora lo informa con verdad. Queda registrado como el primer elemento de trabajo y es el mayor grupo restante — la misma familia de READ NEXT suma nueve fallos en cada uno de IX208A e IX209A. Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. La compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.65 — 2026-08-29 ===
• IX203A declara su productor y completa la cadena IX2xx

Fidelidad del arnés de pruebas; sin cambio en el runtime. Las aserciones de Indexed I/O de NIST CCVS85 van de 518 PASS / 70 FAIL -> 521 PASS / 67 FAIL, con IX203A cayendo de 9 fallos a 6. Los programas ejecutando limpio se mantienen en 29 de 42. La cabecera de IX203A dice "THE FILE USED IS THAT RESULTING FROM IX202" — la serie IX2xx refleja la IX1xx: un miembro crea el fichero y los dos siguientes lo procesan. Solo estaba registrado IX202A <- IX201A, así que IX203A corría en un directorio vacío y cada READ ... NEXT RECORD fallaba por falta de fichero.

Esto corrige un diagnóstico, no solo una puntuación. La entrega anterior registró "el READ NEXT de IX203A nunca tiene éxito" como el mayor defecto de runtime restante, razonando a partir de un recorrido que contaba 502 registros que nunca recibió. El síntoma era real y la causa era errónea: no había ningún bug de READ NEXT, solo un fichero ausente. El libro de registro ahora lo dice explícitamente, para que ninguna sesión futura salga a buscar uno. Lo que queda en IX203A es la familia DELETE-TEST-GF — seis fallos que citan IX-21 4.3.2, incluidos un desajuste de clave y un registro incorrecto encontrado.

Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. La compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.66 — 2026-08-29 ===
• La reanudación tras un DELETE se decide por qué registro sale, no por cómo se direccionó — y un START o un READ con clave descartan la que esté pendiente

Indexed I/O de NIST CCVS85 pasa de 29 a 30 de 42 programas ejecutando limpio; aserciones 521 PASS / 67 FAIL -> 528 PASS / 60 FAIL (88.6 % -> 89.8 %). IX203A cae de seis fallos a uno, e IX212A e IX213A quedan limpios. Son dos correcciones al trabajo de cursores de 1.62.58.

Post añadido a las 10:00. Post anterior a las 09:59

La guarda comprobaba lo equivocado. Un recorrido bajo ACCESS MODE IS DYNAMIC lee con READ NEXT y luego borra, y ese borrado se direcciona por clave — pero el registro que sale sigue siendo aquel sobre el que está el cursor, así que la ranura del B+tree se desplaza bajo él exactamente igual que en la forma secuencial. Guardar sobre "¿fue un borrado con clave?" lo pasaba por alto por completo: el recorrido de IX203A perdía 96 de 500 registros y 24 de sus 125 borrados. La condición ahora es si el registro que se borra es el actual del cursor, lo que cubre ambas formas y sigue dejando en paz un borrado con clave de algún otro registro.

Una reanudación pendiente vivía más de la cuenta. La reanudación dice "continúa después del registro que acaba de salir"; un START dice dónde está el fichero ahora, y debe ganar. Nada la limpiaba, así que el START ... KEY IS EQUAL de IX213A quedaba anulado y el READ siguiente informaba FILE IS AT END en lugar del registro sobre el que se había posicionado. START y un READ con clave la descartan ahora. Los motores en memoria y redb llevan sus cursores por clave y nada de esto les afecta. Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. La compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.67 — 2026-08-29 ===
• Dos operandos numéricos se comparan algebraicamente, sin importar cómo se llenaron sus posiciones

Indexed I/O de NIST CCVS85 pasa de 30 a 32 de 42 programas ejecutando limpio y supera el noventa por ciento de aserciones — 528 PASS / 60 FAIL -> 528 PASS / 58 FAIL, 89.8 % -> 90.1 %. Un MOVE de grupo es un movimiento alfanumérico: transfiere caracteres y no convierte nada, así que un hijo PIC 9(6) acaba conteniendo legítimamente la cadena "000007". El ítem sigue siendo numérico y una relación entre dos operandos numéricos sigue siendo algebraica — pero nada devolvía el valor a forma numérica. Cuando ambos lados de una comparación se habían llenado así, ninguno parecía numérico, el pseudo-move no se aplicaba y se comparaban las dos cadenas de presentación: "000000007" contra "000007", desiguales.

Un lado contra un numérico con valor de literal siempre era correcto, y por eso pasó desapercibido. IX103A e IX203A comprueban ambos un número de registro contra los dígitos incrustados en la clave de ese registro, y veían cada uno de los 500 registros como un desajuste. Ahora un operando numérico se relee a través de su propia PICTURE antes de la comparación. La escala del ítem decide el valor — los mismos seis caracteres son 123456 en un PIC 9(6) y 1234.56 en un PIC 9(4)V99 — así que las posiciones decimales declaradas se llevan en el símbolo y se usan aquí. Una posición que contiene algo que no es una ristra de dígitos se deja exactamente como está; inventar un número sería peor que comparar lo que hay.

Este es el núcleo de expresiones por el que pasa Nucleus, y fue el primer cambio de esta tanda con alcance real más allá de los ficheros indexados. Ambas líneas base protegidas están exactas: NC 95/95 con 4 614 PASS / 0 FAIL, SQ 85/85 con 624 PASS / 0 FAIL. La compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.68 — 2026-08-29 ===
• Una clave genérica de START puede nombrar un ítem subordinado de una clave alternativa

Indexed I/O de NIST CCVS85 pasa de 32 a 35 de 42 programas ejecutando limpio; aserciones 528 PASS / 58 FAIL -> 550 PASS / 33 FAIL (90.1 % -> 94.3 %). IX209A, IX210A e IX214A quedan limpios, e IX215A cae de ocho fallos a dos. El operando de START ... KEY IS no tiene por qué ser la clave misma. Un ítem que empieza donde empieza una clave y es más corto que ella nombra esa clave y busca por el prefijo — la forma genérica, implementada para la clave primaria en 1.62.55. El START-TEST-GF-23 de IX209A lo dice sin rodeos: "AN OPERAND IN THE KEY PHRASE WHICH IS NOT THE NAME OF AN ALTERNATE KEY BUT IS THE NAME OF A DATA ITEM WHICH IS SUBORDINATE TO THE ALTERNATE KEY."

Post añadido a las 10:01. Post anterior a las 10:00

La selección de la clave de referencia solo casaba con una extensión de bytes exacta, lo que es correcto para un REDEFINES de una clave pero erróneo para un ítem subordinado: al ser más corto, no casaba con nada y se caía a la clave 0, buscando en el índice primario los caracteres de una clave alternativa. Cada START de esa forma tomaba el camino de INVALID KEY. Una extensión exacta sigue ganando donde la hay, de modo que una clave que resulta ser la parte más a la izquierda de una clave más larga se resuelve a sí misma y no a su contenedor.

Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. La compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.69 — 2026-08-29 ===
• AT END hace secuencial un READ incluso donde el modo de acceso no lo haría

AT END pertenece al READ secuencial e INVALID KEY al de clave, y NEXT es opcional en el formato secuencial. Un READ ... AT END bajo ACCESS MODE IS DYNAMIC es por tanto secuencial: continúa desde donde un START dejó el fichero, en lugar de releer por RECORD KEY. KEY IS sigue forzando sin más la forma con clave.

Sin movimiento en la puntuación NIST, y vale la pena decirlo con claridad. El programa que motivó el cambio, IX208A, declara ACCESS MODE IS SEQUENTIAL, así que sus lecturas ya eran secuenciales y esto no las toca. La corrección se conserva porque está validada por sí misma — un START ... KEY IS GREATER seguido de READ ... AT END bajo DYNAMIC ahora avanza a los dos registros siguientes donde antes volvía a entregar el registro que la clave aún nombraba. El defecto real de IX208A queda registrado en su lugar, con tres causas descartadas con evidencia: no es el formato del READ, no es un productor ausente y no es la forma simple de la construcción, que una repro recorre correctamente.

Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. IX se mantiene en 35 de 42, 550 PASS / 33 FAIL. La compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.70 — 2026-08-29 ===
• Los opt-codes T y U de CCVS85 son mutuamente excluyentes, y el harness ahora selecciona T.

El módulo Indexed I/O de NIST CCVS85 pasa de 35 a 37 de 42 programas ejecutando limpio; las aserciones van de 550 PASS / 33 FAIL a 561 PASS / 20 FAIL (94.3 % -> 96.6 %). IX207A e IX208A quedan limpios. Una línea de CCVS85 puede llevar una letra de opt-code en la columna de indicador, y la tarjeta *OPT de la instalación dice cuáles están activas. La mayoría de las letras marcan añadidos inocuos de incluir, y el harness las tomaba todas. T y U son distintas: son alternativas, y tomar ambas produce un registro más largo de lo que cualquiera de las dos lecturas pretende.

El caso más claro es IX-FS2R1-F-G-240 de IX208A. Su propio nombre dice 240 caracteres; con T sola mide 240, con U sola mide 240, y con ambas mide 250 — cada campo posterior a la primera clave desplazado cinco posiciones, razón por la que START sobre su clave alternativa se salía del final del fichero y fallaban ocho aserciones. T es la lectura para la que estos programas están escritos: IX208A construye WRK-IX-FS2-ALTKEY a partir de una línea T más un número de cinco dígitos, con lo que mide diez caracteres — la misma forma que IX-FS2-ALTKEY1 bajo T y no bajo U. El programa mueve uno dentro del otro antes de cada START, así que tienen que coincidir.

Hay exactamente diez líneas U en toda la suite, en IX107A, IX207A e IX208A. Ningún miembro de otro módulo lleva ninguna, de modo que un módulo terminado no puede verse perturbado — y no lo fue. La letra se sustituye por * en lugar de eliminarse, de modo que cada columna posterior conserva su posición. Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. La compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.71 — 2026-08-29 ===
• El compilador señala las construcciones de Indexed I/O que están por encima del subconjunto alto.

Post añadido a las 10:03. Post anterior a las 10:01

El módulo Indexed I/O de NIST CCVS85 pasa de 37 a 38 de 42 programas ejecutando limpio; las aserciones van de 561 PASS / 20 FAIL a 567 PASS / 14 FAIL (96.6 % -> 97.6 %). IX401M pasa de cinco errores a ninguno. Un miembro 4nn de CCVS85 no tiene maquinaria de PASS/FAIL: su veredicto son los diagnósticos que emite el compilador. IX401M enumera diez construcciones que espera ver señaladas como por encima del subconjunto alto, y cinco no se detectaban:

- ACCESS MODE IS DYNAMIC — el modo que permite leer un fichero de las dos maneras en una sola apertura, cuando el subconjunto solo tiene SEQUENTIAL y RANDOM.
- ALTERNATE RECORD KEY — una segunda clave sobre el fichero.
- RECORD IS VARYING IN SIZE — ya se señalaba, pero solo escrita sin IS: SQ401M escribe RECORD VARYING, IX401M escribe RECORD IS VARYING IN SIZE FROM 18 TO 36 CHARACTERS, y la palabra intermedia es opcional. Añadir una segunda regla para la forma larga emitía dos señalizaciones sobre la única cláusula de SQ401M, lo que consumía una expectativa de más y desalineaba su última señalización — la regla existente ahora acepta el IS opcional.
- READ ... KEY IS y START ... KEY IS — nombrar la clave de referencia, algo que solo necesita un fichero con más de una clave.

Nucleus y Sequential I/O no se mueven ni en los dos ejes ni en su propia señalización: NC 51 señalizaciones coincidentes / 0 erróneas, SQ 24 / 0, NC 95/95 con 4 614 PASS / 0 FAIL, SQ 85/85 con 624 PASS / 0 FAIL. La compilación de la suite completa sigue en 422 de 434. IX301M se deja fallando a propósito y queda registrado como cuestión de alcance: prueba la señalización del subconjunto intermedio, y cada construcción que nombra — ORGANIZATION IS INDEXED, ACCESS MODE IS RANDOM, RECORD KEY IS, las frases NOT INVALID KEY — es una que PowerRustCOBOL implementa. Un compilador de subconjunto alto no debe señalarlas.

=== PowerRustCOBOL 1.62.72 — 2026-08-29 ===
• IX301M queda excluido de la puntuación de ejecución (decisión del operador).

El módulo Indexed I/O de NIST CCVS85 se puntúa ahora sobre 41 y queda en 38 de 41, con aserciones 566 PASS / 8 FAIL — 98.6 %. La propia cabecera de IX301M dice lo que es: "TESTS THE FLAGGING OF INTERMEDIATE SUBSET FEATURES THAT ARE USED IN LEVEL 1 INDEXED INPUT-OUTPUT". Cada construcción que espera ver señalada — ORGANIZATION IS INDEXED, ACCESS MODE IS RANDOM, RECORD KEY IS y las frases NOT INVALID KEY — es una que PowerRustCOBOL implementa.

Un compilador que valida en el subconjunto alto no debe señalar una característica que soporta, así que esas siete expectativas son inalcanzables por diseño y no por defecto. Solo una validación de subconjunto mínimo las satisfaría. Compárese con IX401M, que pide señalización del subconjunto alto y puntúa 10 de 10. La exclusión afecta solo a la puntuación de ejecución: IX301M es COBOL perfectamente válido, compila limpio y sigue contando en el censo strict — que se mantiene en 422 de 434, sin moverse.

Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. Quedan registradas en el ledger dos decisiones más del operador que entran en vigor a continuación: Relative I/O comienza ahora en lugar de después de IX, y redb pasa a ser el motor de disco por defecto para que el orden de recuperación de duplicados pueda corregirse allí en lugar de mediante un cambio de formato de PRCIDXD1.

=== PowerRustCOBOL 1.62.73 — 2026-08-29 ===
• redb pasa a ser el motor por defecto de ficheros indexados para STORAGE IS DISK (decisión del operador), y se corrigen dos defectos suyos de longitud de registro.

Post añadido a las 10:04. Post anterior a las 10:03

El módulo Indexed I/O de NIST CCVS85 pasa de 38 a 39 de 41; las aserciones van de 566 PASS / 8 FAIL a 568 PASS / 6 FAIL — 99.0 %. El cambio se hizo para resolver el orden de recuperación de duplicados. Cuando un REWRITE cambia el valor de una clave alternativa, el registro abandona un conjunto de duplicados y se une a otro, y debe ocupar su lugar al final del nuevo. PRCIDXD1 ordena los duplicados por RecordId, que es permanentemente el orden de escritura original, así que corregirlo allí habría exigido un cambio de formato del contenedor. redb ya mantiene una tabla seq exactamente para esto, e IX211A queda limpio sobre él.

Medir el cambio antes de hacerlo descubrió dos defectos propios, ambos de una sola palabra: fit redimensionaba cada registro a la anchura declarada en la FD, truncando cualquiera más largo — el motor de disco siempre ha usado record_len.max(rec.len()): la anchura declarada es un suelo, no un techo, y un fichero cuyos registros varían almacena cada uno con su propia longitud —, y decode_value hacía lo mismo a la vuelta, de modo que un registro almacenado entero se devolvía igualmente recortado. IX105A escribe registros largos y los relee comprobando la longitud; reportó "WRONG LENGTH OR WRONG RECORD" cuatro veces bajo redb y pasaba bajo PRCIDXD1. Ambos motores coinciden ahora.

El alias de motor default sigue al valor por defecto en lugar de quedar fijado a rust, y rcrun --indexed-engine rust sigue seleccionando PRCIDXD1 para quien lo quiera. Nucleus y Sequential I/O son exactos sobre el nuevo motor por defecto: NC 95/95 con 4 614 PASS / 0 FAIL, SQ 85/85 con 624 PASS / 0 FAIL. La compilación de la suite completa sigue en 422 de 434; 132 tests de librería del runtime y 95 suites de integración en verde.

=== PowerRustCOBOL 1.62.74 — 2026-08-29 ===
• Un REWRITE que cambia una clave alternativa se incorpora al final de su nuevo conjunto de duplicados.

El módulo Indexed I/O de NIST CCVS85 alcanza 40 de 41 — aserciones 570 PASS / 4 FAIL, 99.3 %. El único miembro que sigue fallando es IX106A, que necesita un motor RELATIVE. Un registro cuyo valor alternativo cambia abandona un conjunto de duplicados y entra en otro. No puede conservar posición en un conjunto al que acaba de unirse, así que ocupa su lugar al final. El motor reutilizaba la secuencia de inserción original del registro, lo que lo colocaba donde hubiera caído su inserción en el fichero.

IX215A gira exactamente sobre esto. Una prueba reescribe el registro 176 hacia un valor duplicado; la siguiente reescribe el registro 4 hacia ese mismo valor; un START sobre ese valor debe entregar entonces 176, cuya entrada se unió primero — aunque el registro 4 se escribió antes en el fichero. Entregaba 4.

Se almacena una secuencia por registro en lugar de por alternativa, y la eliminación reconstruye el valor del multimapa a partir de ella, de modo que cuando cambia cualquier alternativa el registro toma una secuencia nueva y todas sus entradas se reapuntan a ella. El coste es que una alternativa sin cambios también se mueve al final de su propio conjunto; una secuencia por alternativa lo evitaría, al precio de recorrer el multimapa para recuperar el valor de cada entrada al eliminar. Nada en la suite observa la diferencia. Nucleus y Sequential I/O no se mueven: NC 95/95 y SQ 85/85 en ambos ejes, 4 614 y 624 aserciones, ninguna fallando. La compilación de la suite completa sigue en 422 de 434; 132 tests de librería del runtime y 95 suites de integración en verde.

=== PowerRustCOBOL 1.62.75 — 2026-08-29 ===
• RELATIVE KEY IS se analiza y se traslada al runtime, y Relative I/O queda con su línea base medida.

Primer paso de la construcción de RL; todavía sin movimiento en la puntuación, porque nada consume la clave. RELATIVE KEY IS data-name nombra el número entero de registro por el que se direcciona un fichero relativo. A diferencia de una RECORD KEY, no forma parte del registro: el programa lo establece antes de un READ/WRITE aleatorio, y el runtime lo rellena en una lectura secuencial. Ahora llega a FileControl y al registro de ficheros del runtime.

Post añadido a las 10:06. Post anterior a las 10:04

Esto repara además un defecto introducido en 1.62.60. Hacer opcional ORGANIZATION IS añadió un brazo de cláusula para un RELATIVE a secas, y ese brazo se tragaba RELATIVE KEY IS RL-KEY como si fuera una cláusula de organización, descartando la clave por completo. La palabra KEY después de RELATIVE es lo que distingue ambos casos.

Línea base de RL, medida por primera vez: 14 de 35 ejecutando limpio, 196 PASS / 140 FAIL (58.3 %). Cuatro miembros — RL102A, RL109A, RL202A y RL207A — agotan el tiempo en lugar de fallar, que es el bucle ya registrado: un READ que da error dentro del bucle ordinario de GO TO se repite para siempre, y sin motor RELATIVE toda lectura da error. Nucleus, Sequential I/O e Indexed I/O no se mueven: NC 95/95, SQ 85/85, IX 40/41, con 4 614, 624 y 570 aserciones pasando. La compilación de la suite completa sigue en 422 de 434.

=== PowerRustCOBOL 1.62.76 — 2026-08-29 ===
• ORGANIZATION IS RELATIVE funciona.

Relative I/O era aceptado por el parser y luego silenciosamente ignorado por el runtime — un programa que lo declaraba compilaba limpio y se comportaba mal. Ahora tiene motor, y cada verbo de fichero despacha hacia él. RL pasa de 14 a 24 de 35 ejecutando limpio, 326 PASS / 38 FAIL (el 89.6 % de sus aserciones, desde el 58.3 %), y los cuatro miembros que antes agotaban el tiempo ya no lo hacen. IX alcanza 42 de 42 compilando y 41 de 41 ejecutando limpio, 574 PASS / 0 FAIL — los últimos cuatro fallos de IX106A eran el fichero relativo que comparte con los indexados. Un fichero relativo es una tabla de ranuras numeradas, no una lista de registros: la ranura n contiene un registro o está vacía, y una ranura vacía conserva su número — borrar el registro 7 no renumera el 8. El número vive en el elemento RELATIVE KEY de WORKING-STORAGE y no dentro del registro, y por eso esto no podía ser un modo de los motores indexados: aquellos leen sus claves de los bytes del registro.

- WRITE — en modo de acceso secuencial el motor asigna la siguiente ranura y pone el número en el elemento RELATIVE KEY, que es como un programa que crea un fichero conoce sus propios números de registro. Bajo RANDOM o DYNAMIC el programa fija primero el número; una ranura ya ocupada es estado 22, y la ranura cero es 24.
- READ — aleatorio por RELATIVE KEY, o NEXT / PREVIOUS en orden de ranura, saltando las vacías. Una lectura secuencial informa de la ranura que entregó.
- START — se posiciona en la primera ranura que cumpla EQUAL / GREATER / NOT LESS (y las formas LESS) sin entregar registro, de modo que el siguiente READ NEXT la devuelve.
- REWRITE / DELETE — por número bajo acceso aleatorio o dinámico, o sobre el registro que entregó el último READ. Sin esa lectura el estado es 43. DELETE vacía la ranura y deja el número direccionable.

Dos contenedores, elegidos por STORAGE [MODE] IS MEMORY | DISK exactamente igual que eligen los suyos los motores indexados, y obligados a responder de forma idéntica: un BTreeMap en RAM, o el contenedor PRCREL1 — una cabecera fija seguida de ranuras de igual tamaño, de modo que la ranura n está a un solo seek de distancia y el fichero no necesita directorio en RAM. Cada ranura lleva un indicador de presencia y la longitud propia del registro, así que un fichero RECORD IS VARYING almacena un registro corto sin rellenarlo hasta la ambigüedad. El harness también aprendió los miembros encadenados de RL: cuatro de sus series son un creador, un actualizador y un verificador sobre un mismo fichero, y los cuatro programas que agotaban el tiempo eran consumidores ejecutándose donde su productor nunca se había ejecutado — el fichero faltaba, OPEN devolvía 35 y cada READ devolvía 47 dentro de un bucle GO TO. Diez miembros declaran ahora sus productores, como ya hacen los de IX.

=== PowerRustCOBOL 1.62.77 — 2026-08-29 ===
• Relative I/O queda terminado: 35 de 35 compilando, 34 de 34 ejecutando limpio, 354 PASS / 0 FAIL.

Eslopes
31 de agosto de 2026, 15:17
Tres correcciones, ninguna dentro del propio motor. Primera: RELATIVE data-name nombra el número de registro, con o sin la palabra KEY. La cláusula se escribe RELATIVE KEY IS RK, RELATIVE KEY RK y RELATIVE RK a secas, y solo las dos primeras se entendían. La tercera se leía como una cláusula de organización a secas, así que la clave se consumía y se descartaba en silencio — el fichero quedaba entonces sin número de registro alguno y cada WRITE aleatorio devolvía 24, una violación de límites sobre la ranura cero. Lo que distingue ambos casos es simplemente lo que sigue: una cláusula de organización está completa por sí sola, de modo que un data-name tras RELATIVE solo puede ser la clave. Esto cerró ocho programas de una vez.

Segunda: estado de fichero 14 — un número de registro relativo demasiado grande para su elemento de clave. La anchura de la PICTURE del elemento RELATIVE KEY forma parte del comportamiento del fichero, no solo de su almacenamiento: con una clave CUST-SLOT PIC 99, leer ese fichero secuencialmente funciona hasta el registro 99 y a partir de ahí no puede informar de dónde está. COBOL-85 tiene un estado exactamente para eso, y es una condición de clase at-end como 10, así que la frase AT END es la que lo maneja.

Tercera: una frase condicional sin terminar se detiene ahora en el ELSE que la engloba. Una frase escrita sin su terminador de ámbito —

IF WS-N < 201
WRITE REC-A
INVALID KEY GO TO BAD
ELSE
WRITE REC-B
INVALID KEY GO TO BAD.

— leía de largo a través del ELSE, y el programa no llegaba a analizarse. El conjunto de parada propio de una frase no puede saber dentro de qué está anidada, así que la regla se aplica ahora una vez por cada cuerpo con ámbito: ELSE, END-IF, WHEN, END-EVALUATE, END-PERFORM y END-SEARCH cierran una construcción dentro de la cual el cuerpo puede estar anidado, y ninguna lista imperativa puede leer más allá de uno de ellos. Conformidad de compilación de la suite completa: de 422 a 423 de 434. Además, RL301M queda excluido de la puntuación de ejecución, la misma decisión que ya lleva IX301M: pide que una implementación de subconjunto alto señale ORGANIZATION IS RELATIVE, ACCESS MODE IS RANDOM, RELATIVE KEY IS y las frases NOT INVALID KEY como no conformes, y todas son características que PowerRustCOBOL soporta. Sigue contando en el censo de compilación, donde pasa.

=== PowerRustCOBOL 1.62.78 — 2026-08-29 ===
• Una coma separadora delante de un signo ya no se descarta, y dos programas que hacían caer el intérprete ahora se ejecutan.

IF (Conditional) pasa de 24 a 32 de 45 ejecutando limpio, de 702 a 776 aserciones pasando (87.4 % -> 91.9 %), sin caídas restantes en el módulo. Una coma seguida de espacio es un separador de COBOL-85: puede aparecer donde puede aparecer un espacio y significa exactamente lo que significa un espacio, así que el lexer la descartaba. Hay un lugar donde eso no es cierto. El estándar distingue un signo de un operador binario por el espacio que le sigue — A -3 son dos operandos, A - 3 es una resta —, de modo que descartar la coma de COMPUTE WS-NUM = FUNCTION MOD(A, -3). deja A -3, que se leía como una única resta. MOD se invocaba entonces con un solo argumento y el intérprete entraba en pánico al leer el segundo. La coma se conserva ahora cuando el token siguiente es un + o un -, lo que basta para que el parser distinga ambos casos sin enseñarle la regla de espaciado.

El punto y coma no conserva tal excepción. Nunca es un separador de listas, solo decoración, y ningún parser se come uno — MOVE ELEM3( +3; +5, +10) TO TEMP es una lista de subíndices que la suite escribe precisamente para demostrarlo. Aplicar la excepción a ambos signos de puntuación costó un programa que venía pasando, que es como se descubrió la distinción.

Post añadido a las 10:11. Post anterior a las 10:07

Una intrínseca invocada con menos argumentos de los debidos ahora se reporta en lugar de ser fatal. FUNCTION MOD needs 2 arguments, 1 given es algo sobre lo que un desarrollador puede actuar; un pánico por índice fuera de rango, no. Un programa COBOL nunca debe poder hacer caer el intérprete, diga lo que diga.

=== PowerRustCOBOL 1.62.79 — 2026-08-29 ===
• NUMVAL y NUMVAL-C leen las formas que COBOL-85 realmente permite, y las funciones intrínsecas se señalan como por encima del subconjunto alto.

IF (Conditional) pasa de 32 a 37 de 45 ejecutando limpio, de 776 a 824 aserciones pasando (91.9 % -> 97.7 %). NUMVAL: el argumento no es un literal float de Rust, y leerlo como tal devolvía cero para la mayor parte de lo que el estándar permite. El signo puede estar en cualquiera de los dos extremos y no tiene que tocar los dígitos; CR y DB son la grafía crédito-débito de un menos final; NUMVAL-C admite además una cadena de moneda y separadores de grupos de dígitos, y su segundo argumento opcional — la cadena de moneda a ignorar — no se leía en absoluto.

COMPUTE WS-NUM = FUNCTION NUMVAL (" - 4929.0323").
COMPUTE WS-NUM = FUNCTION NUMVAL (" 200.0002 - ").
COMPUTE WS-NUM = FUNCTION NUMVAL-C (" $ 90.54 - ", "$").

Los tres devolvían cero antes. Sin segundo argumento, NUMVAL-C usa ahora el CURRENCY SIGN de SPECIAL-NAMES, y DECIMAL-POINT IS COMMA intercambia los papeles de . y , como lo hace en todas partes.

Las funciones intrínsecas están por encima del subconjunto alto de COBOL-85: llegaron con la adenda de 1989, así que un compilador que valida en el subconjunto alto reporta cada uso como no conforme — implemente o no la función. flag_high_subset no las cubría, y eso es todo lo que piden IF401M, IF402M e IF403M: 44 expectativas entre los tres, ahora todas coincidentes.

Con ello cayeron dos falsos positivos. La lista de argumentos de una función no es una lista de subíndices ni una fuente de operadores para una relación que la engloba, de modo que IF FUNCTION ORD-MAX (5, 3, 2, 8, 3, 1) = ... no son 6 subíndices e IF FUNCTION MEAN (5, -2, -14, 0) = ... no contiene aritmética — cada una atraía una segunda señalización errónea. Siete sentencias de IF402M estaban afectadas y el emparejamiento voraz del puntuador lo ocultaba: las señalizaciones espurias suplantaban a las de función intrínseca, así que los totales parecían correctos. Ambos detectores siguen disparándose sobre las construcciones genuinas, lo que ahora está cubierto por tests unitarios en ambas direcciones. Una intrínseca invocada con menos argumentos de los debidos se reporta en lugar de ser fatal — la guarda añadida en 1.62.78 cubre ahora también ANNUITY.

=== PowerRustCOBOL 1.62.80 — 2026-08-29 ===

Un cociente ya no pierde sus decimales frente a un divisor largo, y las funciones intrínsecas de lista de argumentos comparan como compara COBOL. IF (Conditional) pasa de 37 a 44 de 45 programas ejecutando limpio, 824 -> 841 aserciones superadas (97.7 % -> 99.9 %).

• Una división ya no pierde los decimales del cociente ante un divisor largo

Dar al cociente sus dígitos de guarda implica desplazar el dividendo, y ese desplazamiento tiene que caber en el entero de trabajo. Un divisor con muchos decimales lo agranda por sí solo — 1 / SQRT3, donde SQRT3 PIC S9V9(17), pide 43 dígitos — y la ruta de desbordamiento caía entonces a coma flotante a la escala del propio dividendo. El dividendo es el entero 1, así que 0.577 se redondeaba a un valor sin ninguna posición decimal y volvía como 1:


01 SQRT3 PIC S9V9(17) VALUE 1.732050808.
...
COMPUTE WS-NUM = FUNCTION ATAN(1 / SQRT3).


respondía atan(1): un cuarto de vuelta en lugar de un sexto. El cociente ahora descarta dígitos de guarda hasta que el desplazamiento cabe, lo que conserva la ruta entera exacta; solo una magnitud realmente enorme llega a coma flotante, y lo hace a la precisión de trabajo. Nada que antes cabía cambia: la búsqueda empieza en la precisión que la división siempre usó.

• MAX, MIN, ORD-MAX y ORD-MIN comparan como compara COBOL

Post añadido a las 10:13. Post anterior a las 10:11

Las cuatro leían todos sus argumentos como números en coma flotante, lo cual es erróneo por partida doble. Primero, el resultado de MAX/MIN es el propio argumento: cuando los argumentos son alfanuméricos la comparación se hace por la secuencia de intercalación y la respuesta es una cadena de caracteres — FUNCTION MAX("R", I, "I", "a") es "a" —, y leídos como flotantes los cuatro argumentos valían cero. Segundo, gana el primero de varios argumentos iguales: ORD-MAX y ORD-MIN devuelven una posición, así que los empates se ven — FUNCTION ORD-MAX(A, 5, 5, A) es 1 cuando A es el mayor, donde el código anterior respondía 4. La comparación ahora es cob_ordering, la misma ordenación que usan SORT y todas las relaciones.

• Se conserva una coma separadora delante de (

Por la misma razón que la que precede a un signo (1.62.78): quitarla de FUNCTION MAX(A * B, (C + 1) / 2, 3 + 4) deja B (C + 1), que es la sintaxis de una referencia con subíndice. Los dos primeros argumentos se fusionaban en uno y MAX devolvía 17.5 donde la respuesta es 35.

=== PowerRustCOBOL 1.62.81 — 2026-08-29 ===

• Un signo pegado a un literal es un signo, no un operador

El módulo Conditional queda terminado: 45 de 45 en ambos ejes, 841 aserciones, 0 fallos.

COBOL-85 distingue los dos casos por el espacio que sigue: un operador binario debe llevar espacio a ambos lados, de modo que 10.2 -0.2 son dos operandos y 10.2 - 0.2 es una resta. La suite escribe COMPUTE WS-NUM = FUNCTION RANGE(10.2 -0.2, 5.6, -15.6) sin ninguna coma entre los dos primeros argumentos, y espera cuatro: 25.8 es 10.2 menos -15.6. Leído como resta eran tres argumentos y 25.6.

La salvaguarda es deliberadamente más estrecha que la regla: solo se activa cuando el token anterior al signo es un literal numérico o un paréntesis de cierre. Un identificador delante no puede distinguirse de una palabra clave a nivel léxico, y PICTURE -9(9).9(9) y VARYING ... BY -1 son ambos "operando, hueco, signo pegado, dígitos" — ninguno es un lugar donde cambiar nada. Ampliarla es una decisión para la puerta de la suite completa, no una conjetura.

=== PowerRustCOBOL 1.62.82 — 2026-08-29 ===

• Un cambio del arnés NIST, sin ningún cambio del compilador ni del runtime detrás

El módulo Inter-program Communication trata de CALL, y sus programas llamados son miembros separados de la distribución: IC101A llama a IC102A, IC108A llama a IC109A, IC110A e IC111A. El arnés ejecutaba un fichero fuente cada vez, así que el llamante no tenía a quién llamar. La propia suite lo dice en sus tarjetas de control, con una palabra que el divisor no conocía: *HEADER,COBOL,IC101A,SUBRTN,IC102A.

SUBPRG y SUBRTN no son sinónimos, y la diferencia decide si un miembro es o no un test. SUBPRG nombra el siguiente programa del mismo grupo — cada uno es un test completo con su propio informe, e IX101A,SUBPRG,IX102A es exactamente la cadena que la tabla de productores ya describe. SUBRTN nombra un programa al que el test llama; los 24 están en IC y OBIC, y cada uno responde a un CALL literal. Confundir ambos le costó a SQ203A su puesto y llevó SQ de 85 de 85 a 84 de 84, que es como se descubrió la distinción.

Un llamante se ejecuta ahora con sus llamados concatenados en una sola unidad de ejecución, y un llamado ya no se puntúa como un test que no produjo informe. IC pasa de 6 a 10 de sus 25 tests ejecutando limpio, 134 -> 187 aserciones superadas (51.0 % -> 72.5 %). El censo de compilación no cambia: 423 de 434, porque un llamado es COBOL ordinario y ahí sigue contando. Nota: el denominador de ejecución de IC pasa de 47 a 25 con esta versión; los 47 contaban los 22 llamados como tests, que no tienen informe CCVS y, ejecutados en solitario, solo podían fallar. Las cifras comparables son 6 -> 10 de 25.

=== PowerRustCOBOL 1.62.83 — 2026-08-29 ===

• Segmentation sale del alcance NIST (decisión del operador)

SG se une a CM, RW, los tests de elementos obsoletos OB* y EXEC85 como módulo que RustCOBOL no mide.

Post añadido a las 10:14. Post anterior a las 10:13

La segmentación existe para encajar un programa en una máquina demasiado pequeña para contenerlo: las cabeceras SECTION llevan un número de segmento y el runtime superpone los segmentos independientes unos sobre otros. RustCOBOL es una solución de 64 bits sobre sistemas de 64 bits, con más espacio de direcciones del que ningún programa COBOL puede agotar — así que un número de segmento compila y no tiene ningún efecto. No hay comportamiento que el módulo pueda ejercitar, y puntuarlo sería puntuar un mecanismo que nunca existirá.

Sus 13 programas siguen compilando y se informan como N-A en lugar de eliminarse, de modo que la exclusión queda visible en el censo. La suite dentro de alcance es de 421 programas, no 434, y la conformidad de compilación pasa a leerse 410 de 421 (97.4 %) donde antes leía 423 de 434 (97.5 %).

=== PowerRustCOBOL 1.62.84 — 2026-08-29 ===

• CALL ... USING enlaza el almacenamiento del llamante, no una copia de él

Un parámetro BY REFERENCE es ahora un alias sobre su argumento, de modo que qué almacenamiento nombra un parámetro lo decide el argumento, no el nombre que el programa llamado le dé. Enlazar por nombre significaba que el parámetro del llamado era lo que el llamante tuviera bajo ese nombre: en CALL "IC202A" USING DN1, DN2, DN1, DN4 el tercer argumento es DN1; IC202A llama DN3 a su tercer parámetro y escribe a través de él — y el DN3 propio y sin relación del llamante quedaba sobrescrito, porque los dos eran la misma ranura. El informe CCVS85 lo dice con todas las letras: "DN3 VALUE CHANGED BY CALL".

Solo el parámetro se convierte en alias, deliberadamente. Los items subordinados de un parámetro de grupo siguen resolviéndose por nombre a los del llamante, que es como un programa llamado lee los campos del registro que le entregaron; darles ranuras propias recortó IC de 10 programas limpios a 5. BY CONTENT no cambia — una copia tomada a la entrada, sin escritura de vuelta — y el alias se libera cuando la llamada retorna, de modo que un programa llamado desde dentro de otro que puso alias al mismo nombre recupera su propio enlace. IC (Inter-program Communication) pasa de 187 a 194 aserciones superadas (72.5 % -> 75.8 %), 71 -> 62 fallos.

• La puerta de regresión NIST pasa a una vez por módulo (decisión del operador)

Ejecutar todos los módulos terminados tras cada cambio detectó cuatro problemas en todo el proceso, lo que no compensa volver a medir más de 7000 aserciones cada vez. La puerta completa se ejecuta ahora cuando se resuelve la última aserción fallida de un módulo, antes de declararlo terminado; por cada cambio, solo el módulo en curso y el censo de compilación. Un cambio de lexer o de parser sigue pasando la puerta de inmediato: un cambio del front-end no tiene frontera de módulo, y dos de las cuatro detecciones fueron exactamente eso.

=== PowerRustCOBOL 1.62.85 — 2026-08-29 ===

• Los campos de un parámetro de grupo se emparejan con los del argumento por posición

Los dos programas no tienen por qué usar los mismos nombres para ellos, y hasta ahora solo se enlazaba el grupo en sí — así que el programa llamado escribía en sus propias ranuras de LINKAGE y el llamante no veía cambiar nada.


* the caller
01 TABLE-1.
02 DN2 PICTURE XXX.
02 DN3 PICTURE 99.
02 DN4 PICTURE X(5).
...
CALL "IC204A" USING TABLE-1, DN1.

* the called program
01 SUB-TABLE-1.
02 SUB-DN2 PIC XXX.
02 SUB-DN3 PIC 99.
02 SUB-DN4 PIC X(5).
PROCEDURE DIVISION USING SUB-TABLE-1, SUB-DN1.


Los mismos bytes bajo nombres distintos. Emparejar por nombre no alcanza nada, así que los items subordinados se emparejan en orden de declaración — no hay otra cosa por la que emparejarlos — y cada uno se pone como alias del correspondiente del llamante, junto con el grupo. IC (Inter-program Communication) pasa de 194 a 206 aserciones superadas (75.8 % -> 80.5 %), 62 -> 50 fallos. IC203A por sí solo pasó de 13 fallos a 1.

=== PowerRustCOBOL 1.62.86 — 2026-08-29 ===

Post añadido a las 10:16. Post anterior a las 10:14

• BY CONTENT entrega el valor, no el almacenamiento, y la frase BY gobierna todos los operandos que la siguen

Dos defectos, un solo síntoma visible. BY CONTENT copiaba el argumento en el parámetro por nombre, así que cuando el nombre del parámetro era uno que el llamante también usaba, la copia aterrizaba en el almacenamiento del propio llamante y cada escritura pasaba directa. Y la frase se leía por operando: CALL "IC225A-1" USING BY REFERENCE DN1, DN2, CONTENT DN3, DN4 END-CALL pasa dos operandos por cada vía; leída por operando, solo DN3 era BY CONTENT y DN4 recaía en BY REFERENCE. La suite comprueba exactamente eso: "VALUE OF DN4 HAS BEEN CHANGED".

BY CONTENT enlaza ahora como BY REFERENCE y el almacenamiento del llamante — el argumento y cada uno de sus campos — se restaura cuando la llamada retorna. Dentro de una sola unidad de ejecución eso es indistinguible de una copia privada, y hereda el mecanismo de alias que ya hace alcanzables los campos de un parámetro de grupo. IC (Inter-program Communication) pasa de 206 a 212 aserciones superadas (80.5 % -> 82.8 %), 50 -> 44 fallos.

=== PowerRustCOBOL 1.62.87 — 2026-08-29 ===

• Corregido — un programa de consola ya no construye una pila TLS que nunca usa

El compañero de 1.62.9, una plataforma más abajo. Aquel cambio evitó que todas las aplicaciones compilaran SQLite; este evita que un programa de consola enlace la red. ureq y native-tls eran dependencias incondicionales de cobolt-runtime, y native-tls en Linux es OpenSSL — así que compilar allí cualquier aplicación exigía libssl-dev y un compilador de C para openssl-sys, hubiera o no oído hablar el programa de HTTP. google_maps también era incondicional: reqwest más tokio, la mayor dependencia individual que arrastra el runtime, compilada dentro de programas sin ningún control de Maps. Ambos son ahora features, activas por defecto, y el manifiesto generado las apaga para un programa que demostrablemente no alcanza ninguna de las dos. openssl-sys y tokio salen del grafo de dependencias por completo — verificado con cargo tree --target x86_64-unknown-linux-gnu, porque en macOS native-tls usa Security.framework y el problema es invisible.

Maps lo decide el formulario, no el COBOL: MAPS-1::Geocode es una llamada a método sobre un id de control, y nada en el AST la distingue de cualquier otra llamada a método — GET sobre un RestClient se lee exactamente igual que GET sobre cualquier otra cosa. Así que lo que se lee es el .cfrm: un proyecto con un control Maps o WebSearch enlaza el cliente, y uno sin él no. Ese es el cambio que más ahorra, porque una aplicación de formularios corriente no tiene ningún control de Maps. HTTP se enlaza a propósito en toda aplicación de formularios, sin inspeccionar nada: la feature render de cobolt-forms descarga las teselas del mapa base de OSM con su propio ureq, así que una aplicación de formularios enlaza la pila TLS de la plataforma decida lo que decida esto — no hay nada que ganar con ser listos y sí un programa que funciona que perder. Los programas de consola son los que de verdad pueden desprenderse de OpenSSL: no poseen controles y su HTTP llega como un CALL que la build puede leer. Un CALL cuyo destino es un item de datos en lugar de un literal enlaza ahora los tres puentes, no solo SQL: el nombre solo se conoce en tiempo de ejecución, así que podría ser cualquier verbo; la versión anterior lo concedía para SQL y lo asumía en silencio para los demás.

Las builds con un puente apagado informan por el canal de fallo que ese puente ya tenía — HTTP mediante (body, status 0), exactamente donde cae una conexión rechazada; Maps mediante el Err que usan sus propios fallos de API — y cada mensaje nombra la decisión de build y cómo revertirla, en lugar de leerse como una avería de red corriente. Aviso: todos los puentes opcionales son ahora separables, y ninguno cambia lo que enlaza una build por defecto de este workspace: rcrun, el IDE y todos los tests conservan la superficie completa.

=== PowerRustCOBOL 1.62.88 — 2026-08-29 ===

Post añadido a las 10:17. Post anterior a las 10:16

NIST CCVS85 Source Text Manipulation (SM) queda con línea base: 0 -> 4 de 17, con las aserciones pasando de 8 PASS / 15 FAIL a 25 PASS / 9 FAIL (34.8 % -> 73.5 %). Nada cambió en el compilador ni en el runtime — ambas correcciones están en el arnés de conformidad, que medía el módulo injustamente. Todos los módulos terminados quedan intactos: NC 95/95 con 4 614 aserciones, SQ 85/85 con 624, IX 41/41 con 574, IF 45/45 con 841, RL 34/34 con 354, todos con cero fallos, y el censo de compilación de la suite completa sin cambios en 410 de 421.

• Corregido — la pasada de ejecución nunca suministró los copybooks de la suite

La pasada de compilación expande COPY contra la biblioteca de copybooks de CCVS85 desde que el arnés la incorporó. La pasada de ejecución nunca lo hizo. rcrun resuelve un copybook contra el propio directorio del fichero fuente, así que la biblioteca se replica ahora allí antes de ejecutar cada programa.

El módulo Source Text Manipulation trata de COPY, y demuestra un copybook usándolo: SM101A construye un fichero de datos a partir de declaraciones de registro suministradas por COPY, y SM102A vuelve a leer esos registros. Sin la biblioteca, SM101A se expandía a nada, escribía un fichero de cero bytes y SM102A informaba EOF PREMATURELY FOUND — una entrada ausente que se lee exactamente como un defecto del runtime.

• Corregido — las parejas constructor/comprobador de SM declaran sus productores

Tres miembros de SM leen un fichero que un miembro anterior escribió y lo dicen en su propia cabecera, así que entran en la tabla de miembros encadenados: SM102A <- SM101A, SM104A <- SM103A, SM202A <- SM201A. Cada entrada está citada de la cabecera del consumidor ("PROGRAM SM102A TESTS THE OUTPUT FILE PRODUCED BY SM101A"), nunca inferida de una tarjeta X que dos programas casualmente compartan.

ST102A y ST120A leen la misma tarjeta que escribe ST101A y deliberadamente NO se encadenan: ningún miembro de ST declara un productor, y ST102A no tiene PRINT-FILE alguno, de modo que el informe de un productor quedaría atrás y se puntuaría como propio de ST102A. Eso queda fijado como test negativo, con su razón.

=== PowerRustCOBOL 1.62.89 — 2026-08-30 ===

• Corregido — el FILE STATUS de un subprograma se informaba en el almacenamiento del llamante

Un fichero puede compartirse entre programas — eso es lo que significa IS EXTERNAL en una FD, y su estado de apertura y su posición sí son una sola cosa en toda la unidad de ejecución. Su item de FILE STATUS no lo es: lo nombra el SELECT propio de cada programa, sale del almacenamiento propio de ese programa, y una operación informa en el item que pertenece al programa que la ejecutó. Dos defectos superpuestos hacían que CCVS85 IC227A fallara diez aserciones en cinco sentencias — WRITE, CLOSE, OPEN, READ y EOF — con el mismo par intercambiado cada vez.

Eslopes
31 de agosto de 2026, 15:27
- build_file_specs lee solo el programa más externo y nunca recorre nested_programs, así que el item de estado de un fichero era permanentemente el del programa exterior. Toda operación, la hiciera el programa que la hiciera, informaba en el item del llamante: IC227A siembra el suyo con el centinela <>, no hace E/S propia, y lo encontró sobrescrito — MAIN PROGRAM FILE STATUS UPDATED. Un NestedProgram lleva ahora sus propios enlaces, que se activan alrededor de la llamada y se retiran después, de modo que el anidamiento se deshace en orden. Un fichero que el programa en ejecución no declaró él mismo con SELECT sigue recurriendo al exterior, que es lo que necesita un programa anidado que referencia un fichero GLOBAL.
- Con eso corregido, el estado iba al nombre correcto y todavía no al almacenamiento correcto. set_file_status escribía a través de set_str, que indexa por storage_key — que sigue REDEFINES pero no los alias de parámetro que posee resolve_name. Un item de estado puede ser un item de LINKAGE, y entonces es el almacenamiento del llamante y no una ranura propia: IC227A-1 declara FILE STATUS IS LINKAGE-FS, el tercer argumento de su llamante. La escritura llenaba una ranura que nadie lee y el argumento nunca se movía — UNEXPECTED FILE STATUS VALUE RETURNED. Ahora resuelve el nombre como lo hace cualquier otra sentencia.

IC227A: 11 fallos -> 1, y sus tests que se ejecutan con éxito pasan de 8 a 18 de 23. El módulo va de 212 -> 222 aserciones superadas, 44 -> 34 fallidas, 82.8% -> 86.7%. El recuento de programas limpios se mantiene en 10 de 25: el último fallo de IC227A tiene otra causa, el área de registro EXTERNAL y no el item de estado. La suite de runtime, 99 binarios en verde; el censo de compilación de la suite completa se mantuvo exactamente en 410/421. La puerta completa de línea base protegida se ejecuta al completar el módulo, según la decisión del 2026-08-29 — este cambio está en el runtime, no en el front end.

=== PowerRustCOBOL 1.62.90 — 2026-08-30 ===

• Corregido — la facilidad inter-programa se marca como por encima del subconjunto alto

Un compilador COBOL-85 que valida en el subconjunto alto debe indicarlo cuando un programa usa algo por encima de él. Toda la facilidad de fuentes anidadas y compilación separada está por encima de esa línea — un programa que contiene a otro, las cláusulas que permiten que datos y procedimientos crucen la frontera, y las frases que dicen cómo se pasa un argumento — y nada de ello se marcaba. CCVS85 IC401M declara once de estas construcciones en setenta y cuatro líneas y solo emparejaba dos: END PROGRAM, que otro miembro ya había motivado. Las nueve añadidas ahora son IS INITIAL e IS COMMON en PROGRAM-ID, las cláusulas de datos GLOBAL y EXTERNAL, USE GLOBAL, CANCEL, las frases BY REFERENCE y BY CONTENT, y un programa fuente contenido.

Dos detalles merecieron el cuidado que exigieron. USE GLOBAL y la cláusula de datos GLOBAL comparten palabra clave y son mensajes distintos, así que se distinguen por lo que precede a la palabra. Y INITIAL, COMMON y CONTENT no son palabras clave en absoluto: el lexer no tiene mapeo para ninguna de ellas, así que llegan como palabras ordinarias — Token::Content_ existe pero nada lo produce, y emparejar sobre él no marcaba nada. Cada una queda anclada a la palabra que la precede, de modo que un ítem de datos que casualmente se llame CONTENT no se confunda con la frase. Además, la primera versión de la regla del programa contenido marcaba VALUE OF ID IS "X" en una descripción de fichero, porque ID abrevia IDENTIFICATION y se lexea al mismo token; exigir que siga DIVISION es lo que los separa, y la prueba existente fd_clauses_above_the_subset lo detectó antes de salir del crate.

Post añadido a las 10:21. Post anterior a las 10:19

IC401M: 11 de 11 emparejados, 0 erróneos. En el conjunto de los miembros de marcado de la suite el contador de errores baja 34 -> 25, y ningún otro miembro se movió — los trece que ya puntuaban perfecto lo siguen haciendo, incluidos los cuarenta de NC401M. IC pasa de 222 -> 231 aserciones superadas, 34 -> 25 fallidas, 86.7% -> 90.2%, y de 10 -> 11 de 25 programas limpios. NC se volvió a medir exacto en 95/95 y 4614 PASS / 0 FAIL; el censo de compilación se mantuvo en 410/421. flag_high_subset solo lo alcanza el puntuador del arnés de conformidad — nunca la compilación ni la ejecución — así que todo el radio de impacto son los miembros de marcado, y se midieron los veinticuatro.

=== PowerRustCOBOL 1.62.91 — 2026-08-30 ===

• Añadido — sondas que fijan las garantías del almacenamiento de un grupo

Sin cambio de comportamiento. Cinco pruebas en test_linkage_layout_probe.rs afirman las propiedades de almacenamiento de las que depende vincular un parámetro de LINKAGE por desplazamiento de bytes, porque un intento de esa vinculación falló por razones que leer el código no resolvió, y adivinar dos veces habría sido peor que medir una.

Para la forma sobre la que CCVS85 IC203A e IC205A discrepan — dos bytes declarados como 02 DN6 PIC X OCCURS 2 TIMES en un lado y como 02 TV-1 PIC X más 02 TV-2 PIC X en el otro — establecen que ambas descripciones reportan el mismo ancho, que ningún ítem reporta ancho cero (incluido el nombre sin subíndice de un ítem OCCURS), que cada ocurrencia es direccionable y contiene su propio byte, y que escribir una ocurrencia se refleja en el grupo, que es la dirección en la que escribe un programa llamado.

Las cinco se cumplen. Merece quedar registrado como pruebas y no como una nota: eran las dos explicaciones principales del fallo, ambas quedan ahora excluidas, y la siguiente persona que toque esto tendría que volver a deducirlas.

=== PowerRustCOBOL 1.62.92 — 2026-08-30 ===

• Defecto conocido registrado — un REDEFINES dentro de un programa anidado no hace nada

Aún sin corrección; dos reproducciones y una descripción precisa de un defecto que resulta ser más amplio que el fallo de conformidad que lo encontró. Perseguir los tres fallos de REDEFINES-en-LINKAGE de CCVS85 IC106A produjo primero una explicación más estrecha — que el emparejamiento del CALL omite las redefiniciones, lo cual es cierto — y después una segunda reproducción que no tiene LINKAGE, ni parámetro, ni emparejamiento de CALL en absoluto: un programa anidado redefine su propio WORKING-STORAGE, escribe las partes y lee el todo. Falla de forma idéntica, así que la causa no está en cómo se vinculan los parámetros.

push_local_scope inserta los ítems de un programa anidado en el almacén y en la tabla de símbolos y nada más. La maquinaria de redefinición — las clases de equivalencia, los alias para miembros con disposición idéntica que exceden el presupuesto, el refresco que mantiene sincronizadas las clases pequeñas — se construye cuando el entorno se levanta a partir de la DATA DIVISION del programa exterior. Los ítems de un programa anidado llegan por un camino que no construye nada de eso, así que su ítem redefinidor es una ranura independiente y las dos descripciones nunca comparten almacenamiento.

Atención: esto va más allá de la suite de pruebas. Cada manejador de eventos de un formulario RAD es un programa anidado, así que un REDEFINES en el WORKING-STORAGE de un manejador da silenciosamente dos campos sin relación en lugar de dos vistas de uno; debe tratarse como un defecto del producto que NIST encontró de pasada. Ambas reproducciones viven en test_linkage_redefines.rs, marcadas #[ignore] con el motivo para que la suite siga en verde — se ejecutan con --ignored — y están escritas como criterio de aceptación de la corrección: quitar esos atributos es lo que dirá que está terminada.

=== PowerRustCOBOL 1.62.93 — 2026-08-30 ===

• Corregido — un CALL exitoso ejecutaba su propio manejador de desbordamiento

Post añadido a las 10:22. Post anterior a las 10:21

ON es opcional antes de las frases OVERFLOW y EXCEPTION de un CALL. Escrita sin él, la frase no se reconocía: USING recolecta operandos mientras el siguiente token pueda iniciar uno, un OVERFLOW a secas es solo una palabra, así que la lista de argumentos se lo tragaba y todo lo que seguía se convertía en sentencias ordinarias tras el CALL. Una llamada que tenía éxito ejecutaba entonces el manejador escrito para el caso en que falla. ON OVERFLOW nunca tuvo el problema — Token::On no puede iniciar una expresión, así que la lista de argumentos se detenía sola. Por eso sobrevivió: la ortografía que todo el mundo escribe es la segura.

CCVS85 IC201A escribe ambas formas en un mismo párrafo, CALL-TEST-03-01 con el ON y -03-02 sin él, y reportaba OVERFLOW SHOULD NOT OCCUR para la segunda. Ninguna de las dos palabras puede ser un nombre de datos — ambas son reservadas — así que detener la lista de argumentos en ellas no cuesta nada.

IC pasa de 231 -> 292 aserciones superadas, 25 -> 21 fallidas. El total puntuado sube 256 -> 313: sesenta aserciones que antes no podían alcanzarse ahora se ejecutan, porque un programa que saltaba a su propia rama de fallo nunca llegaba a ellas. El censo de compilación de toda la suite sube 410 -> 412 de 421 (97.4% -> 97.9%): dos programas que nunca habían parseado ahora lo hacen — la misma lista de argumentos consumía la palabra clave de la frase y dejaba ilegible el resto de la sentencia. Todas las líneas base protegidas se volvieron a medir exactas: NC 95/95 con 4614 PASS / 0 FAIL, SQ 85/85 con 624/0, IX 41/41 con 574/0, RL 34/34 con 354/0, IF 45/45 con 841/0. Un cambio de parser se verifica de inmediato y no al completar el módulo, y esta es la razón.

=== PowerRustCOBOL 1.62.94 — 2026-08-30 ===

• Corregido — un nivel 88 sobre un ítem de LINKAGE comprobaba la ranura propia del llamado

Un nombre de condición declarado en la LINKAGE SECTION de un subprograma era siempre falso, pusiera lo que pusiera el llamador en el almacenamiento que nombra. Dos fallos independientes, y el primero ocultaba al segundo. Los niveles 88 de un programa anidado nunca se registraban: cond_names se construye cuando un entorno se analiza desde una DATA DIVISION, y los ítems de un programa anidado llegan al entorno compartido por push_local_scope, que llevaba valores y símbolos y nada más. IF sobre el nombre 88 no encontraba entonces ningún nombre de condición y caía al comportamiento "la ranura contiene algo distinto de cero" — falso para un ítem de LINKAGE que el llamado no ha escrito. register_nested ahora cosecha los nombres de condición del análisis que ya realiza, y el CALL los instala mientras dura la llamada. Un nombre que el entorno ya conoce se deja intacto, exactamente igual que un ítem de datos existente: esto solo puede aportar una resolución donde no había ninguna, nunca cambiar una que ya funcionaba.

Una vez registrado, el anfitrión todavía se leía por clave cruda. Un anfitrión de LINKAGE es el almacenamiento del llamador — CALL ... USING pone el parámetro como alias del argumento — y solo una búsqueda que siga el alias lo alcanza. El subíndice sobrevive a la sustitución y sigue siendo un subíndice, así que L-ITM(2) bajo el alias L-ITM -> ITM es ITM(2), una ocurrencia, nunca la tabla entera. Es el mismo fallo de composición que el defecto de FILE STATUS corregido en 1.62.89: dos caminos de resolución, cada uno correcto por separado, que no componen. CCVS85 IC207A LINK-TEST-03 dice lo que comprueba — "THIS TEST VERIFIES THAT THE CONDITION NAMES DEFINED IN THE LINKAGE SECTION OF THE SUBPROGRAM WERE PROCESSED CORRECTLY".

Comunicación inter-programa (IC), ejecución: 292 PASS / 21 FAIL -> 294 PASS / 19 FAIL, programas limpios 12 -> 15 de 25, DELETED 4. Compilación de toda la suite sin cambios en 412 / 421; NC 95/95 (4614 aserciones), SQ 85/85 (624), IX 41/41 (574), RL 34/34 (354) e IF 45/45 (841), todos exactos.

• Corregido — la prueba de aceptación asociada no podía fallar correctamente

Post añadido a las 10:24. Post anterior a las 10:22

test_linkage_condition_name.rs extraía un valor mostrado quitando el prefijo MISS=[ y dejaba puesto el corchete de cierre, de modo que trim().is_empty() nunca era verdadero y la prueba negativa fallaba igual tanto si la respuesta era correcta como si no. Consta en el libro mayor de NIST como que detectó una respuesta errónea en un intento revertido de 1.62.93; no pudo haberlo hecho. Ahora se quitan ambos delimitadores, y la corrección anterior se verifica contra el comportamiento observado.

=== PowerRustCOBOL 1.62.95 — 2026-08-30 ===

• Corregido — un REDEFINES dentro de un programa anidado era inerte

Dos descripciones de una misma área de almacenamiento no la compartían. push_local_scope inserta los ítems de un programa anidado en el almacén y en la tabla de símbolos y nada más: la maquinaria de redefinición — las clases de equivalencia y el refresco que las mantiene sincronizadas — se construye cuando un entorno se analiza desde una DATA DIVISION, y los ítems de un programa anidado llegan por un camino que nunca construyó nada de eso. Su ítem redefinidor era simplemente una ranura independiente. Esto va más allá de la suite de conformidad: cada manejador de eventos de un formulario RAD es un programa anidado, así que un desarrollador que escribiera REDEFINES en el WORKING-STORAGE de un manejador obtenía dos ranuras sin relación y datos silenciosamente erróneos.

register_nested ahora transporta las clases de refresco del programa y el CALL las adopta mientras dura, fusionándolas con la misma regla que usa la construcción original: añadir, nunca reemplazar, nunca duplicar. La deduplicación es lo que lo hace seguro para el área de informe repetitiva de CCVS85: COMPUTED-N REDEFINES COMPUTED-A y sus vecinos se declaran en el programa exterior y en cada programa anidado bajo los mismos nombres, así que los pares son idénticos y adoptarlos es un no-op en lugar de una segunda superposición viva sobre los mismos veinte bytes. pop_local_scope elimina exactamente lo que se añadió — una fuga aquí corrompería el almacenamiento de un programa sin relación en una llamada posterior.

• Corregido — el refresco de REDEFINES escribía a través de claves crudas

refresh_redefine_peers vuelve a renderizar una descripción de un área en las demás con cada escritura. Lo hacía por clave cruda, y un miembro de la clase puede ser un ítem de LINKAGE cuyo almacenamiento es el del llamador — así que el refresco leía y escribía la ranura propia e intacta del llamado. Ambos lados siguen ahora el alias del parámetro.

Por esto la primera corrección no podía aterrizar sola. Adoptar las clases sin ella llevó a CCVS85 IC106A de 3 fallos a 6: su LINK-TEST-06 seguía fallando y tres aserciones sin relación — INDEX IN LINKAGE SECTION, INDEX DATA ITEM SET IN SUBPROG, TABLES DEFINED IN LINKAGE SEC — se rompieron, porque IC107 redefine un GROUP-21 que contiene un DN2 PIC X OCCURS 10 indexado en su LINKAGE SECTION. Con el alias seguido, la regresión desaparece por completo. Es el mismo fallo de composición que el defecto de FILE STATUS (1.62.89) y el del nombre de condición de LINKAGE (1.62.94): dos caminos de resolución, cada uno correcto por separado, que no componen. Cuarta instancia, y ahora registrada en el libro mayor como candidata a una corrección general en vez de un quinto parche.

Los marcadores no cambian y ese es el resultado honesto. La comunicación inter-programa (IC) se mantiene en 294 PASS / 19 FAIL, 15 de 25 limpios; IC106A conserva sus 3 fallos, cuya causa es distinta y sigue abierta. Lo que este cambio compra es el defecto de producto de arriba, cubierto por dos reproducciones — una sin LINKAGE y sin parámetro alguno, con lo que la causa queda probada en el programa anidado y no en la vinculación. Compilación de toda la suite 412 / 421; NC 95/95 (4614 aserciones), SQ 85/85 (624), IX 41/41 (574), RL 34/34 (354) e IF 45/45 (841), todos exactos.

=== PowerRustCOBOL 1.62.96 — 2026-08-30 ===

• Corregido — leer a través de un REDEFINES de un ítem de LINKAGE veía una ranura vacía

Post añadido a las 10:25. Post anterior a las 10:24

Un subprograma que lee una redefinición de uno de sus parámetros obtenía el almacenamiento propio e intacto del ítem redefinidor en lugar de los bytes del llamador. El refresco de redefinición se activa por escritura: vuelve a renderizar una descripción en las demás que comparten su área cuando algo escribe en ella. Para una clase de LINKAGE eso llega tarde: el llamador rellena el argumento antes del CALL, así que para cuando la clase existe y el alias del parámetro apunta al almacenamiento del llamador, la escritura que habría propagado esos bytes ya ocurrió — y la descripción redefinidora nunca llegaba a poblarse.

Las clases adoptadas ahora se ceban una sola vez a la entrada, desde el miembro que efectivamente resuelva a través de un alias de parámetro. Una clase sin miembro con alias se deja en paz: es almacenamiento propio del llamado y no tiene nada que heredar.

CCVS85 IC237A-1 declara 01 L-A PIC 9. y 01 L-A1 REDEFINES L-A PIC 9. en su LINKAGE SECTION y hace MOVE L-A1 TO L-C; el llamador comprueba después WS-C = WS-A. Reducido a 25 líneas, L-A leía 1 mientras L-A1 leía 0. Es la quinta aparición de una misma forma raíz — dos caminos de resolución, cada uno correcto por separado, que no componen — tras FILE STATUS (1.62.89), el nombre de condición de LINKAGE (1.62.94) y el refresco de REDEFINES (1.62.95); el libro mayor la lleva ahora como candidata a una única corrección general en vez de un sexto parche. Comunicación inter-programa (IC), ejecución: 294 PASS / 19 FAIL -> 295 PASS / 18 FAIL, programas limpios 15 -> 16 de 25; IC237A ejecuta limpio. Compilación de toda la suite sin cambios en 412 / 421; NC 95/95 (4614 aserciones), SQ 85/85 (624), IX 41/41 (574), RL 34/34 (354) e IF 45/45 (841), todos exactos.

=== PowerRustCOBOL 1.62.97 — 2026-08-30 ===

• Corregido — los campos de un parámetro de grupo se emparejan por desplazamiento de bytes, no por posición en el árbol

COBOL-85 da a un ítem de LINKAGE una vista sobre el almacenamiento del llamador: el llamado extiende su propia descripción sobre los bytes del llamador, y ni los nombres ni la forma del árbol tienen que coincidir. Los ítems subordinados de un parámetro de grupo se emparejaban con los del argumento por posición en el árbol de declaración, así que un llamado que describía los mismos bytes con un árbol distinto solo vinculaba su primer campo. CCVS85 IC103A enuncia el caso en su propia cabecera — "THE ITEM DESCRIPTIONS ARE DIFFERENT IN THE SUBPROGRAM FROM THE MAIN PROGRAM, BUT THE NUMBER OF CHARACTERS IS IDENTICAL". Diez bytes son dos hijos de nivel superior en el llamador y tres en el llamado. Reducido a una reproducción, el NUM-DISPLAY PIC 99 del llamado recibía entero el GROUP-LEV2 de cinco bytes del llamador ("42XYZ") y su A-FIELD no recibía nada en absoluto.

Cuando ambas descripciones son planas y coinciden en el ancho total, las hojas se emparejan ahora por los bytes que cubre cada una. Cualquier otro caso conserva el emparejamiento posicional, y esa restricción es el objetivo y no un atajo: un alias solo se consulta por nombre base — resolve_name resuelve la hoja sin subíndice y el subíndice se aplica después — así que una entrada escrita DN6(1) -> TV-1 no la consulta nunca nada. Un mapeo de desplazamientos por ocurrencia vincula estrictamente menos que el emparejamiento al que sustituye, que es exactamente el intento revertido de 1.62.90 que llevó a IC203A de 1 fallo a 7. Esa propiedad queda ahora fijada por una prueba.

• Corregido — un recorrido de disposición medía valores renderizados en lugar de los PICTURE declarados

El mapeo anterior aterrizó y no movió nada, porque nunca llegaba a dispararse. Las dos descripciones discrepaban en su ancho total — 9 contra 10 — porque item_width mide lo que haya en la ranura y un PIC 99 de LINKAGE sin escribir se renderiza como 0, reportando ancho 1. La disposición lee ahora el PICTURE declarado. La sonda conservada no_leaf_reports_a_zero_width se escribió para atrapar esta clase de fallo y pasó todo el tiempo: afirmaba que ninguna hoja reportara ancho cero, y este ancho era erróneo, no cero.

Post añadido a las 10:27. Post anterior a las 10:25

Debajo hay un defecto más amplio, registrado en el libro mayor en vez de corregido aquí: los field_caps de un programa anidado nunca llegan al entorno compartido, porque push_local_scope transporta store y symbols y nada más. field_caps es lo que consulta el truncamiento numérico, así que a cada ítem numérico de LINKAGE en un programa anidado le falta actualmente su capacidad declarada.

Comunicación inter-programa (IC), ejecución: 295 PASS / 18 FAIL -> 299 PASS / 12 FAIL. IC103A pasa de 4 fallos -> 1, IC235A de 5 -> 2. Los dos que quedan en cada uno son CALL-TEST-06 .06 (ALPHANUMERIC EDITED) y .08 (SUBSCRIPTED LINKAGE DATA ITEM) — precisamente las dos formas que el recorrido rechaza, un PICTURE editado y una tabla. Programas limpios sin cambios en 16 de 25; el denominador puntuado se movió 313 -> 311 al dejar esos programas de emitir las líneas extra que produce un fallo. Compilación de toda la suite 412 / 421; NC 95/95 (4614), SQ 85/85 (624), IX 41/41 (574), RL 34/34 (354) e IF 45/45 (841), todos exactos.

=== PowerRustCOBOL 1.62.98 — 2026-08-30 ===

• Corregido — los ítems FILLER de un programa anidado no existían

El almacenamiento de un programa anidado se copia como instantánea al entorno compartido cuando se le llama. Esa instantánea venía de CobolEnvironment::iter(), que oculta deliberadamente las claves FILLER sin nombre — correcto para mostrar el almacenamiento, incorrecto para copiarlo. Un FILLER ocupa bytes, así que cada ítem declarado después de uno en un programa anidado quedaba en el desplazamiento equivocado.

CCVS85 IC107 declara, en su LINKAGE SECTION:


01 GROUP-2.
02 GROUP-21.
06 DN2 PIC X OCCURS 10 TIMES.
02 GROUP-2-1 REDEFINES GROUP-21.
03 FILLER PICTURE X(7).
03 DN3 PICTURE XXX.


MOVE ... TO DN3 debe superponerse a los tres últimos bytes de la tabla. El FILLER de siete bytes estaba ausente por completo del entorno — sin valor, sin símbolo — así que reportaba ancho 0 y la escritura caía al principio: 0ABC456789 donde se requería 0123456ABC. all_entries() proporciona ahora la instantánea sin filtrar; iter() queda sin cambios para los llamantes que quieren una vista legible.

La puntuación CCVS no se movió, y ese es el resultado honesto. La comunicación inter-programa (IC) se mantiene en 299 PASS / 12 FAIL, 16 de 25 limpios, e IC106A conserva sus 3 fallos — LINK-TEST-06 tiene una causa adicional que esto no alcanza. Lo que se gana es el defecto de arriba, que va más allá de la suite: un FILLER es COBOL ordinario y cada manejador de eventos de un formulario RAD es un programa anidado. Compilación de toda la suite 412 / 421; NC 95/95 (4614), SQ 85/85 (624), IX 41/41 (574), RL 34/34 (354) e IF 45/45 (841), todos exactos.

=== PowerRustCOBOL 1.62.99 — 2026-08-30 ===

• Corregido — un alias de CALL ... USING sobre una tabla escribía en una ranura que nada lee

Las ocurrencias de una tabla son ranuras de almacenamiento separadas en el runtime, y su nombre base no posee ninguna: todo lector direcciona DN2 (n). Eso es invisible para un programa, que de todos modos debe subindexar un ítem de tabla — pero un alias de CALL ... USING puede caer sobre el nombre a secas, y entonces la escritura no iba a ninguna parte. CCVS85 IC106A pasa 01 TABLE-2. 02 DN2 PIC X OCCURS 10. a IC107A, que describe esos mismos diez bytes como un grupo con una superposición:


01 GROUP-2.
02 GROUP-21.
06 DN2 PIC X OCCURS 10 TIMES.
02 GROUP-2-1 REDEFINES GROUP-21.
03 FILLER PICTURE X(7).
03 DN3 PICTURE XXX.


MOVE AL-CON TO DN3 tiene que superponerse a las tres últimas posiciones de la tabla. La vinculación de parámetros empareja el GROUP-21 del llamado con el DN2 del llamador, así que el refresco de REDEFINES pedía almacenar diez bytes en DN2 — el nombre base — y set_group_bytes lo rechazaba por elemental. DN2 (8), (9) y (10) volvían en blanco frente a los esperados X, Y y Z.

Eslopes
31 de agosto de 2026, 15:36
Una referencia sin subíndice a una tabla es ahora el grupo de sus propias ocurrencias, para leer (group_value, group_bytes), escribir (set_group_bytes, set_from_bytes) y medir (item_width). El nuevo CobolEnvironment::table_extent_keys reconoce exactamente ese caso y nada más: una clave completamente subindexada sigue nombrando una sola ocurrencia, y un grupo sigue leyendo y escribiendo a través de sus propias layout_keys. La expansión por hijo de occurrence_keys se movió a expand_occurrences para que ambos recorridos compartan una única definición de lo que es una ocurrencia, OCCURS ... DEPENDING ON incluido. Prueba de regresión: a_redefines_over_a_linkage_table_reaches_the_calle rs_occurrences en crates/cobolt-runtime/tests/test_linkage_redefines.rs. NIST CCVS85 — Comunicación inter-programa (IC): ejecución 16 -> 17 de 25 programas limpios, 299 -> 301 aserciones PASS y 12 -> 9 FAIL. Conformidad de compilación sin cambios en 47/47 para el módulo y 412 / 421 para toda la suite en alcance.

=== PowerRustCOBOL 1.62.100 — 2026-08-30 ===

• Corregido — un nombre de registro en colisión dejaba sin enlazar los hijos de un parámetro de grupo

Los miembros CCVS85 IC203A e IC205A llaman ambos TABLE-2 a su registro. El del llamador es 02 DN6 PIC X OCCURS 2; el del llamado es 02 TV-1 PIC X. 02 TV-2 PIC X. Como los nombres colisionan, push_local_scope nunca instala el 01 del llamado — gana el del llamador, correctamente — pero el emparejamiento de parámetros de grupo recorría entonces el símbolo del entorno compartido para AMBOS lados y leía dos veces el árbol del llamador. TV-1 y TV-2 del llamado nunca quedaban enlazados a nada: el MOVE "B" TO TV-2 de IC205A escribía en una ranura privada que nadie lee, y CNCL-TEST-05 reportaba COMPUTED= vacío.

Dos cambios, un solo mecanismo. El lado del parámetro del emparejamiento se recorre ahora sobre la propia tabla de símbolos del llamado (register_nested ya la lleva consigo), con los anchos leídos únicamente de la PICTURE. El lado del argumento expande las ocurrencias de una tabla fija como objetivos de alias — TV-2 -> DN6(2) —, que es la dirección consultable: una CLAVE de alias solo se busca por nombre base (a_subscripted_alias_key_is_never_consulted), pero un objetivo se almacena y se sigue textualmente. Una tabla con OCCURS DEPENDING ON sigue rechazándose; su extensión en el momento del enlace no es su extensión durante toda la llamada.

La puntuación CCVS no cambia: 301 PASS / 9 FAIL, y el motivo queda registrado con prueba. La corrección del enlace llevó el COMPUTED= de CNCL-TEST-05 de vacío a XY — las escrituras ya llegan —, pero el miembro sigue en rojo por un segundo defecto separado: una ejecución instrumentada de la cadena fiel muestra que el contador de llamadas de IC206A llega exactamente a 3, y exactamente a 1 tras CANCEL, y aun así las dos pruebas IF DN2 deciden mal. El 77 DN2 COMP privado de IC205A es en realidad el hijo PIC XXX del llamador (el defecto de colisión de nombres medido en el callejón sin salida del shadowing de 1.62.98), así que una comparación numérica se ejecuta como alfanumérica: " 3" contra "3 ". IC203A pasa por tanto al cubo de bloqueados por ámbito de activación junto a IC101A e IC225A; la semántica de comparación se dejó deliberadamente intacta: doblarla para tapar un defecto de ámbito es la vía de heurísticas de palabras que este proyecto rechaza. Compilación de la suite completa 412 / 421; suites de runtime 0 fallidas; NC/SQ/IX/RL/IF intactos por esta vía (el bucle de enlace solo se ejecuta en llamadas a programas anidados, y las comprobaciones por cambio lo cubren) — puerta completa en el límite de módulo según el dictamen del operador.

=== PowerRustCOBOL 1.62.101 — 2026-08-30 ===

• Corregido — un argumento de CALL con subíndice enlazaba la tabla entera

CALL ... USING SUBSCRIPTED-DATA (4) enlazaba el parámetro al nombre desnudo de la tabla: expr_to_name descarta el subíndice, y desde 1.62.99 una escritura a un nombre de tabla desnudo es la tabla entera — así que el valor del llamado aterrizaba en la ocurrencia 1, mientras que CALL-TEST-06-08 de CCVS85 IC235A lee la ocurrencia 4.

Post añadido a las 10:30. Post anterior a las 10:29

El subíndice se evalúa ahora en el momento del enlace, exactamente igual que BY REFERENCE fija la identidad del argumento en el CALL, y la clave con subíndice viaja como objetivo del alias — los objetivos se siguen textualmente; solo las CLAVES de alias son búsquedas por nombre base. La prueba de regresión afirma las dos mitades: la ocurrencia 4 recibe la escritura del llamado y la ocurrencia 1 queda intacta — la segunda aserción es lo que distingue la corrección del accidente de tabla entera que producía S1=[1A].

Comunicación entre programas (IC), ejecución: 301 PASS / 9 FAIL -> 302 PASS / 8 FAIL, programas limpios 17 de 25; IC235A pasa de 2 a 1 fallo, conservando solo su .06 bloqueado por ámbito de activación. Compilación de la suite completa 412 / 421; suites de runtime 0 fallidas; 13 pruebas de sonda en verde.

=== PowerRustCOBOL 1.62.102 — 2026-08-30 ===

• Corregido — un registro pasado BY REFERENCE es una vista sobre sus bytes

Un llamador puede pasar un registro que el llamado describe con un árbol completamente distinto — incluido el extremo en que el llamador no tiene ningún ítem dentro. CCVS85 IC112A pasa el 01 SQ-FS3R1-F-G-120. 02 FILLER PIC X(120). de su FD, e IC113A tiende sobre él un árbol de 120 bytes totalmente nombrado, comprobando XRECORD-NUMBER en el desplazamiento 34. Ningún emparejamiento ítem a ítem puede enlazar eso: no hay nada en el lado del llamador sobre lo que crear un alias.

Cuando el emparejamiento de un parámetro de grupo no enlaza nada por debajo del propio 01, los bytes del argumento se trocean ahora hacia las hojas del llamado a la entrada, y las hojas nombradas se parchean de vuelta sobre los bytes del argumento a la salida, conservando las posiciones FILLER lo que tenía el llamador. Los desplazamientos vienen de compute_layout, el mismo recorrido en el que ya confían los registros de FD. Tres guardas hacen la vía puramente aditiva: solo se dispara cuando cero hijos se emparejaron, escribe solo ranuras insertadas por esta llamada, y exige que el ancho declarado y el real coincidan byte a byte.

• Corregido — el nombre de un llamado podía quedar en alias sobre una ranura FILLER

El emparejamiento posicional cerraba en cremallera las claves de layout en bruto, y esas incluyen ranuras FILLER sintéticas. Que la primera hoja del llamado se emparejara con un FILLER del llamador era un bug de corrupción: un MOVE "BYE" TO HEAD a través de ese alias escribía la ranura FILLER con el ancho del propio FILLER y arrasaba un registro de 20 bytes dejándolo en "BYE" más espacios. Ningún programa puede referenciar una clave FILLER sintética — su separador no puede aparecer en un nombre COBOL —, así que un alias así solo puede fallar. Ambos lados del emparejamiento las excluyen ahora.

La puntuación CCVS no se movió — IC sigue en 302 PASS / 8 FAIL — y el diagnóstico de IC112A es el hallazgo. Con la vista en su sitio, su fallo es RECORDS-IN-ERROR = 649: todos los registros fallan las comprobaciones del llamado, porque los nombres de hoja de IC113A (XRECORD-NUMBER, XFILE-NAME, XLABEL-TYPE) colisionan todos con la tabla del andamiaje CCVS del llamador, FILE-RECORD-INFO-P1-120. Nunca se insertan, la guarda de solo-insertadas rehúsa con razón escribirlas, y las lecturas del llamado se resuelven al andamiaje del llamador en lugar de a su propio registro. Una clave, dos significados: IC112A entra en el cubo de bloqueados por ámbito de activación, que ya contiene seis de los ocho fallos restantes. Compilación de la suite completa 412 / 421; suites de runtime 0 fallidas; todas las repros anteriores re-verificadas.

=== PowerRustCOBOL 1.62.103 — 2026-08-30 ===

• Corregido — los archivos propios de un programa anidado no existían

build_file_specs recorría solo el programa externo, de modo que un SELECT/FD declarado dentro de un subprograma nunca se registraba: OPEN, READ y WRITE sobre él chocaban con unknown file y, en silencio, no hacían nada. CCVS85 IC115A crea, verifica y relee un archivo de 649 registros enteramente desde dentro de un subprograma; una repro de 40 líneas mostraba READ: unknown file.

Post añadido a las 10:32. Post anterior a las 10:30

El registro recorre ahora recursivamente los programas anidados, el más externo primero — un nombre que la unidad de ejecución ya conoce se deja en paz. Esa política es estructural para el único archivo que declara todo programa CCVS85: el PRINT-FILE de cada subprograma sigue resolviéndose al archivo de informe del programa externo, exactamente igual que FILE STATUS cae en cascada. El cursor de lectura persiste entre activaciones, y la prueba de regresión fija los tres comportamientos: registro, persistencia del cursor y AT END.

• Corregido — los símbolos solo viajaban adonde viajaban los valores

push_local_scope insertaba el símbolo de un programa anidado solo cuando existía una clave de almacenamiento con el mismo nombre. Un grupo y el nombre base de una tabla no poseen ranura de almacenamiento, así que exactamente los metadatos que necesita todo recorrido estructurado — dims, layout_keys, las PICTURE — nunca llegaban. Los símbolos se llevan ahora por sí mismos, gana el primero, y se retiran exactamente al hacer pop.

Comunicación entre programas (IC), ejecución: 302 -> 303 PASS / 8 FAIL (una prueba de archivo de IC114A); cada uno de los ocho programas que aún fallan está exactamente en un fallo. El propio LINK-TEST-12 de IC114A sigue en rojo, con un diagnóstico a nivel de byte registrado en el libro mayor: los nombres de campo del andamiaje del subprograma son los nombres de campo del registro del llamador, y los registros escritos entrelazan los campos de los dos programas — la colisión de ámbito de activación al nivel de símbolo. Siete de los ocho fallos restantes están ya en ese cubo. Compilación de la suite completa 412 / 421; suites de runtime 0 fallidas; todas las repros anteriores re-verificadas; 15 pruebas de sonda en verde.

=== PowerRustCOBOL 1.62.104 — 2026-08-30 ===

• Corregido — los datos propios de un programa llamado son suyos: el ámbito de activación

Cada uno de los ocho fallos restantes de IC se había reducido, cada cual con su propia prueba medida, a una única primitiva ausente: la WORKING-STORAGE privada de un programa anidado se fusionaba en un único entorno compartido, de modo que el contador de llamadas 77 DN2 de un llamado ERA el DN2 del llamador, el guardar-en-área-de-trabajo de un llamado destruía el argumento del llamador antes de leerlo, y un ítem numérico de un llamado se comparaba como texto porque su capacidad declarada pertenecía al nombre de otro.

La primitiva, construida a partir de las restricciones de tres intentos revertidos:
- Cualificación en el momento del registro. Todo lo que un programa declara en privado (WS/LS; no GLOBAL, no EXTERNAL, no OBJECT REFERENCE — cada uno de esos es un enlace compartido por la unidad de ejecución) pasa a claves cualificadas por programa, PROG + \u{4} + nombre, con las listas internas de claves de los símbolos, los anfitriones de nombres de condición y las clases de REDEFINES reescritos a través del mismo mapa. El entorno compartido no puede colisionarlas con nadie, por construcción.
- Desvíos por activación. En el CALL, un alias nombre -> cualificado viaja por la vía existente de resolve_name y el vec existente de guardar/restaurar. Gana ANTES de la canonicalización: mientras una activación se ejecuta, solo se ejecutan sus propias sentencias, así que una hoja desnuda que lleva un desvío es el ítem propio de ese programa, sea cual sea el árbol del mismo nombre que declare el llamador. Solo los desvíos toman la vía temprana — sus objetivos llevan el separador que ningún objetivo de SET ADDRESS puede contener.
- Estado descriptivo bajo claves cualificadas, instalado una sola vez. El acarreo total se revirtió en su día sobre claves crudas porque los hermanos podían verlo; las claves cualificadas no pueden colisionar, así que las capacidades, las plantillas de edición y BLANK WHEN ZERO se instalan ahora en la construcción y nunca se deshacen. Sin ellas, un PIC 9(6) privado no tenía capacidad e IF X (1) EQUAL TO 649 comparaba "000649" con "649 " — el bucle de creación escribió treinta megabytes antes de que fuera abortado.

Post añadido a las 10:33. Post anterior a las 10:32

Comunicación entre programas (IC), ejecución: 303 PASS / 8 FAIL -> 305 PASS / 4 FAIL, programas limpios 17 -> 21 de 25. IC101A, IC114A, IC203A, IC226A e IC227A se ejecutan limpios — tres de ellos por primera vez en la historia. Las cuatro pruebas de aceptación pasan, incluida la guarda de que los campos de un parámetro de grupo siguen llegando al llamador. Se actualizaron dos expectativas de prueba en las que los valores eran correctos y solo cambió la representación: el PIC 9(4) de un programa anidado muestra ahora 0001 exactamente igual que siempre lo ha hecho el de un programa externo, porque el ítem por fin tiene su capacidad. Compilación de la suite completa 412 / 421; NC 95/95 (4614), SQ 85/85 (624), IX 41/41 (574), RL 34/34 (354), IF 45/45 (841), todos exactos; suites de runtime 0 fallidas.

=== PowerRustCOBOL 1.62.105 — 2026-08-30 ===

• Corregido — un parámetro LINKAGE con edición edita

MOVE "ABCD" TO EDITED-FIELD, donde EDITED-FIELD PIC XXBX0X es una descripción LINKAGE del llano ALPHA-EDITED PIC X(6) del llamador, almacenaba los cuatro caracteres en crudo. La escritura sigue el alias del parámetro hasta la clave del llamador y pedía la plantilla de edición de ESA clave — que con razón no existe, pues fuera de la llamada el ítem es un X(6) llano. La edición pertenece a la descripción a través de la cual se escribe, no a aquella sobre la que se escribe.

El parámetro presta ahora su plantilla al argumento durante exactamente la duración del enlace, y la recupera a la salida. Es correcto porque, mientras está prestada, solo se ejecutan las sentencias de la activación; la segunda mitad de la prueba de regresión escribe la misma ranura después de la llamada y exige que quede sin editar — una plantilla fugada fallaría precisamente ahí.

Tres piezas tuvieron que alinearse, cada una hallada por medición: pic_char_width acepta los caracteres de inserción alfanumérica B, 0 y / (cada uno ocupa una posición almacenada — eso es lo que hace que XXBX0X mida seis), de modo que el emparejamiento por desplazamientos pueda siquiera tender el árbol de forma distinta del llamado sobre los bytes del llamador; y las plantillas de edición alfanumérica viven en su propio mapa, separado de las numéricas, que tanto la instalación descriptiva en construcción como el préstamo acarrean ahora. Comunicación entre programas (IC), ejecución: 305 PASS / 4 FAIL -> 307 PASS / 2 FAIL, programas limpios 21 -> 23 de 25 — el 99.4% de las aserciones puntuadas del módulo. IC103A e IC235A se ejecutan limpios. Compilación de la suite completa 412 / 421; NC 95/95 (4614), SQ 85/85 (624), IX 41/41 (574), RL 34/34 (354), IF 45/45 (841), todos exactos; 104 suites de runtime, 0 fallidas.

=== PowerRustCOBOL 1.62.106 — 2026-08-30 ===

• Corregido — una vista de registro sobrevive a nombres en colisión, y sus grupos son legibles

Dos remates de la vista de registro de 1.62.102, ambos aflorados por LINK-TEST-08 de CCVS85 IC112A y hallados con una sonda por registro. Los campos de la vista son la ventana privada del llamado sobre los bytes del argumento — no se emparejan con nada, por diseño —, así que un campo cuyo nombre colisiona con algo que declara el llamador toma ahora una clave de activación y un desvío, exactamente como los datos privados, con sus capacidades declaradas reflejadas sobre la clave de la vista. La vieja guarda de solo-insertadas dejaba a la vista sin sustento cada vez que los nombres de campo del llamado coincidían con el andamiaje CCVS del llamador (XRECORD-NUMBER contra el árbol FILE-RECORD-INFO): 649 de 649 registros "in error".

Y los grupos nombrados de una vista se suman a ella para lectura: IC113A comprueba REELUNIT-NUMBER-GROUP, un grupo de dos bytes sobre /0, y una vista que solo desvía hojas elementales enviaba ese nombre a la tabla del mismo nombre del llamador. Los grupos se desvían y se trocean hacia dentro; el copiado de salida sigue parcheando desde las hojas elementales.

Post añadido a las 10:35. Post anterior a las 10:33

Comunicación entre programas (IC), ejecución: 307 PASS / 2 FAIL -> 308 PASS / 1 FAIL, programas limpios 23 -> 24 de 25 — el 99.7%. IC112A se ejecuta limpio. Queda un solo miembro: IC225A. Compilación de la suite completa 412 / 421; 104 suites de runtime, 0 fallidas.

=== PowerRustCOBOL 1.62.107 — 2026-08-30 ===

• IC (Comunicación entre programas) está TERMINADO — 25/25, 309 PASS / 0 FAIL

El último miembro cayó ante la última aplicación de la primitiva de ámbito de activación: un parámetro elemental BY CONTENT es una copia privada. El viejo tratamiento de alias-y-restaurar se rompía en cuanto el mismo argumento viajaba además por referencia — CALL-TEST-02-03 de CCVS85 IC225A pasa DN1 una vez CONTENT y una vez REFERENCE en un mismo CALL, el ADD del llamado llega al llamador por la referencia, y la restauración del CONTENT volvía a poner el valor antiguo. Una copia en una clave de activación no tiene restauración que machaque nada, y una escritura a través del parámetro CONTENT alcanza solo la copia — el significado entero de la cláusula. Los grupos conservan el tratamiento de alias; sus subordinados se resuelven al almacenamiento del llamador por diseño.

La puerta de cierre del módulo, al completo: IC con 25 de 25 programas ejecutándose limpios, 309 de 309 aserciones puntuadas PASS (DELETED 4, rastreadas durante todo el módulo). NC 95/95 (4614 aserciones), SQ 85/85 (624), IX 41/41 (574), RL 34/34 (354), IF 45/45 (841) — todos exactos; compilación de la suite completa 412/421; 104 suites de runtime, 0 fallidas; las seis pruebas de ámbito de activación en verde, incluidos los dos negativos estructurales.

IC entró en el libro mayor en 1.62.81 como el módulo más débil jamás tomado como línea base: 6 de 25 limpios, 134 PASS / 129 FAIL. Veintiséis versiones después es el sexto módulo terminado y se une a las líneas base protegidas. ST (Sort-Merge) está ahora en curso, con línea base 157 PASS / 99 FAIL, 13 de 40 limpios, y con cuatro miembros que no producen informe CCVS alguno — el primer cubo por leer.

=== PowerRustCOBOL 1.62.108 — 2026-08-30 ===

• Arnés — las ocho cadenas de productores de ST, y los miembros sin informe por diseño

Dos correcciones de medición, ningún cambio de runtime. El módulo Sort/Merge está construido como ocho cadenas creador -> clasificador -> verificador, escritas en las propias cabeceras de miembro de la suite (*HEADER,COBOL,ST101A,SUBPRG,ST102A); las quince entradas de inherits_from las reflejan ahora, de modo que cada miembro se ejecuta después de su productor en el mismo directorio. Y un miembro que nunca nombra la tarjeta de impresora X-55 NO PUEDE informar: ST102A tiene setenta y ocho líneas — varios SELECT, una SD, un SORT, STOP RUN — y lo verifica el siguiente programa de su cadena. Salir con código 0 y sin informe es exactamente el aspecto de un miembro así cuando funciona, y ahora se puntúa de esa manera.

Sort/Merge (ST), ejecución: 157 PASS / 99 FAIL -> 588 PASS / 44 FAIL, programas limpios 13 -> 19 de 40 — cientos de aserciones antes inalcanzables se ejecutan ahora, una ganancia de fidelidad de medición en el sentido que registra la regla de puerta del libro mayor. Siguen abiertos: tres clasificadores que referencian la impresora y sin embargo no imprimen nada, un nuevo timeout de 20 segundos (ST127A), y los dos fallos de compilación de COLLATING SEQUENCE. Los seis módulos protegidos re-verificados exactos bajo el arnés cambiado, entre ellos el 309/0 de IC por primera vez; compilación de la suite completa 412 / 421.

=== PowerRustCOBOL 1.62.109 — 2026-08-30 ===

• Arnés — informar es ejecutar PRINT-DETAIL, no nombrar la impresora

ST110A declara PRINT-FILE sobre la tarjeta X-55 y nunca lo abre, así que el predicado de 1.62.108 — "nombra la tarjeta de impresora" — seguía llamando fallos a tres clasificadores que funcionan. La señal verdadera es la propia maquinaria CCVS: todo miembro que informa la mueve a través de PRINT-DETAIL, y un clasificador no tiene ninguno que mover.

Post añadido a las 10:36. Post anterior a las 10:35

Sort/Merge (ST), ejecución: 19 -> 22 de 40 limpios (aserciones sin cambio, en 588 / 44). Lo que queda es real: cinco verificadores de cadena, cada uno con exactamente un fallo; ST103A con cuatro; el timeout de ST127A; dos miembros mayores (ST131A con 6, ST133A con 15); y los dos fallos de compilación de COLLATING SEQUENCE.

=== PowerRustCOBOL 1.62.110 — 2026-08-30 ===
• Corregido — SORT respeta el contrato de registros de longitud variable
SORT ... USING/GIVING leía su entrada con un lector en bloque de tamaño fijo y escribía su salida en bruto, mientras que todos los demás READ y WRITE de un fichero de longitud variable hablan el contrato del prefijo de longitud. Con los registros de 50 a 100 bytes de ST110A de CCVS85, el lector cercenaba cada registro contra sus vecinos y alimentaba al ordenador de registros con basura, y los verificadores de la cadena reportaban EOF prematuro en el fichero GIVING. Ahora ambos lados del sort comprueban is_varying() y usan el prefijo.

Sort/Merge (ST), ejecución: 588 PASS / 44 FAIL -> 601 PASS / 44 FAIL; programas limpios 22 -> 24 de 40 — se despejaron las cadenas de ST109A y ST112M. Los fallos de ST137A pasaron de 1 -> 3 porque empezaron a ejecutarse aserciones antes inalcanzables. Compilación de la suite completa 412/421; 104 suites de runtime, 0 fallidas.

=== PowerRustCOBOL 1.62.111 — 2026-08-30 ===
• Corregido — INPUT/OUTPUT PROCEDURE a THRU b ejecuta el rango completo
El parser leía el extremo THRU de un procedimiento de SORT/MERGE y lo descartaba, de modo que el intérprete ejecutaba solo la primera sección del rango. ST106A de CCVS85 escribe INPUT PROCEDURE INPROC THRU INPROC-EXIT, y su bucle de RELEASE — que abarca el rango — no liberaba nada: el fichero GIVING salía vacío y todos los verificadores posteriores reportaban EOF prematuro. El AST ahora conserva el par, y exec_sort ejecuta un rango THRU exactamente igual que PERFORM a THRU b.

Sort/Merge (ST), ejecución: 601 PASS / 44 FAIL -> 660 PASS / 127 FAIL; programas limpios 24 -> 26 de 40. El crecimiento de los FAIL es del tipo honesto: ST127A dejó de agotar el tiempo y su procedimiento de salida ejecuta ahora 101 aserciones (80 fallando actualmente), y el rango de entrada de ST119A abrió 25 más. Al ser un cambio del parser, la puerta completa se ejecutó de inmediato: NC 95/95 (4614/0), SQ 85/85 (624/0), IX 41/41 (574/0), RL 34/34 (354/0), IF 45/45 (841/0), IC 25/25 (309/0), compilación 412/421, 104 suites de runtime y la suite del parser con 0 fallos.

=== PowerRustCOBOL 1.62.112 — 2026-08-30 ===
• Corregido — un campo DISPLAY con signo conserva su signo en la imagen del registro
field_bytes representaba un numérico con signo mediante unsigned_abs() y distribute convertía cada byte no numérico en '0' antes de parsear sin signo, así que un viaje de ida y vuelta por el registro borraba los signos por completo: el sort de ocho claves de ST127A de CCVS85 ordenaba -5432 después de +501, y ochenta de sus aserciones fallaban con COMPUTED= 000005432 frente a CORRECT = -000005432.

Una picture con signo (S9..., con el signo no SEPARATE) representa ahora su dígito final con el overpunch estándar — {/A-I para +0...+9, }/J-R para -0...-9 — y distribute lee de vuelta esa misma convención, respetando también un - inicial procedente de escritores con signo separado. El recorrido del layout registra la condición de signo campo a campo.

Sort/Merge (ST), ejecución: 660 PASS / 127 FAIL -> 683 PASS / 92 FAIL; programas limpios 26 -> 28 de 40. Las imágenes de registro son terreno compartido, así que se ejecutó la puerta protegida completa: NC 95/95 (4614/0), SQ 85/85 (624/0), IX 41/41 (574/0), RL 34/34 (354/0), IF 45/45 (841/0), IC 25/25 (309/0), compilación 412/421, 104 suites de runtime con 0 fallos.

Eslopes
31 de agosto de 2026, 15:43
=== PowerRustCOBOL 1.62.113 — 2026-08-30 ===
• Harness — ST301M se suma a la exclusión de flagging del subconjunto intermedio
"The following program tests the flagging of intermediate subset features that are used in sort-merge functions" — cabecera del propio ST301M. Sus expectativas son los SD, SAME SORT-MERGE AREA, MERGE y SORT ... GIVING que esta implementación ejecuta a diario, y un compilador que valida en el subconjunto alto no debe señalar features que soporta. La misma decisión del operador que sacó del alcance a IX301M y RL301M lo cubre; Sort/Merge se puntúa sobre 39.

Sort/Merge (ST): 28 de 39 limpios, 682 PASS / 87 FAIL de 769 aserciones puntuadas. Compilación de la suite completa 412/421 sin cambios.

=== PowerRustCOBOL 1.62.114 — 2026-08-30 ===
• Harness — se rellena el recuento de registros del sort grande (X-65)
XXXXX065 es el recuento de registros del fichero del sort grande de ST115A, un tamaño que la distribución deja en manos de la instalación. Sin rellenar, es un identificador no declarado: el MOVE hacia RECORDS-IN-FILE queda en no-op, la cota del bucle de construcción compara contra nada y sale tras UN solo registro de 507 bytes, ST116A ordena un fichero de un registro y el BIG-SORT de ST117A reporta ERROR AT RECORD 1. Cien mantiene "grande" con sentido y el barrido rápido — rellenado hasta las ocho columnas de la tarjeta, o el sello de secuencia se desliza dentro del área de código (el primer intento hizo fallar ambos miembros exactamente así).

Sort/Merge (ST), ejecución: 28 -> 29 de 39 limpios (683 PASS / 86 FAIL); ST115A y ST117A se ejecutan limpios. Compilación de la suite completa 412/421.

=== PowerRustCOBOL 1.62.115 — 2026-08-31 ===
• Un procedimiento de sort sigue un GO TO fuera de su rango — y de vuelta
El INPUT PROCEDURE de ST119A de CCVS85 salta a "código externo" fuera de su rango IN-1 THRU IN-EXIT, y ese código salta directamente de vuelta adentro (XI-19 4.4.4 GR(10)). El ejecutor ordinario de rangos PERFORM escapa ante un GO TO fuera de rango, y esa escapada se desenrollaba a través de la propia sentencia SORT: el sort corría sobre un búfer vacío, los registros liberados llegaban después por caída al nivel superior, y cada test de ordenación leía de vuelta el orden de liberación. Un INPUT/OUTPUT PROCEDURE de sort ahora sigue un GO TO adonde vaya y termina cuando completa su último párrafo; el PERFORM THRU ordinario conserva su semántica de escape.

ST119A: 9 FAIL -> 0 (27 de 27, cinco tests nunca alcanzados se ejecutan ahora). ST (Sort/Merge): 29 -> 30 de 39 limpios; aserciones 683 PASS / 86 FAIL -> 696 / 77. Compilación de la suite completa sin cambios en 412/421.

=== PowerRustCOBOL 1.62.116 — 2026-08-31 ===
• El área de registro compartida de ST131A — tres defectos y una reparación del harness
ST131A de CCVS85 (SAME RECORD AREA FOR SORT3 FILE3) fallaba 6 aserciones por tres defectos sin relación entre sí: el párrafo I-O-CONTROL es UNA sola sentencia y la lista de nombres de fichero de SAME se comía el SAME de la cláusula siguiente como si fuera un nombre de fichero, descartando en silencio el segundo grupo; RELEASE materializaba solo los campos con nombre, de modo que los bytes cubiertos por FILLER — los dígitos de orden bajo de la clave del tercer sort — iban a disco como espacios; y RETURN ... INTO era una asignación de cadena completa con pérdidas que nada vuelve a leer, y ahora es el mismo contrato de distribución de bytes que READ ... INTO (lo que también despeja ST144A). ST131A: 15 de 15. ST (Sort/Merge): 30 -> 32 de 39; aserciones 696 PASS / 77 FAIL -> 707 / 70.

Post añadido a las 10:40. Post anterior a las 10:38

La puerta protegida completa (se tocó el parser) atrapó dos cosas: una variante rechazada del fix de RETURN, que almacenaba la imagen en bruto bajo el nombre de grupo del registro, desplazaba las lecturas de grupo de ST127A (61 -> 68; revertida y registrada como callejón sin salida), y la sustitución global de XXXXX065 de 1.62.114 había corrompido los datos de prueba de clave alternativa de IX106A (...XXXXXXXXXX065ALTKEY1...). La sustitución de tarjetas X ahora está anclada a los límites. IX restaurado a 41/41, 574 PASS / 0 FAIL; NC, SQ, RL, IF, IC exactos; compilación 412/421.

=== PowerRustCOBOL 1.62.117 — 2026-08-31 ===
• El byte de signo fantasma en las imágenes de grupo
Un numérico negativo con signo (no SEPARATE) representaba sus bytes en la imagen de grupo con un - inicial — un byte para el que el ítem no tiene posición de carácter — de modo que cualquier movimiento de grupo a través de un campo negativo desplazaba todos los campos posteriores un byte a la derecha. ST127A de CCVS85 toma una instantánea de su registro de sort a través de tres campos con signo y comparaba la copia desplazada: sus 61 fallos eran todos esto. Los valores negativos se representan ahora al ancho del PICTURE con el signo en overpunch sobre el dígito final (la convención de la imagen de registro), los anchos coinciden, y el lado receptor decodifica esa forma de vuelta al valor; cualquier otro segmento sigue aterrizando literal.

ST127A: 61 FAIL -> 0 (27 de 27). ST133A, ST134A y ST136A vienen detrás — la "familia del área de registro" era en su mayoría este mismo byte. ST (Sort/Merge): 32 -> 36 de 39; aserciones 707 PASS / 70 FAIL -> 709 / 3. Puerta completa: NC, SQ, IX, RL, IF, IC todos exactos; compilación de la suite completa 412/421.

=== PowerRustCOBOL 1.62.118 — 2026-08-31 ===
• Harness — la tarjeta de la secuencia de intercalación nativa (X-63)
XXXXX063 es la declaración de la instalación del orden de intercalación nativo de su máquina, un literal entrecomillado de 51 caracteres contra el que ST137A y ST147A comparan la salida ordenada. Sin rellenar, las secuencias esperadas eran espacios — y las secuencias calculadas ya eran exactamente ASCII. Se rellenó con la tarjeta de muestra ASCII que los propios miembros documentan, con la sustitución anclada a límites porque IX106A lleva los mismos ocho bytes dentro de una serie de X en sus datos de prueba.

ST (Sort/Merge): 36 -> 37 de 39, aserciones 709 PASS / 3 FAIL -> 711 / 0 — todo lo que compila se ejecuta limpio. Los dos miembros restantes son la brecha del eje de compilación (SORT ... COLLATING SEQUENCE IS alphabet-name). IX re-verificado exacto (574/0); compilación 412/421.

=== PowerRustCOBOL 1.62.119 — 2026-08-31 ===
• ST (Sort/Merge) TERMINADO — y SORT aprende COLLATING SEQUENCE
SORT/MERGE ... [COLLATING] SEQUENCE [IS] alphabet-name ahora se parsea en las dos grafías de CCVS85 (SEQUENCE es palabra clave del lexer, así que la cláusula se reconoce por tokens y la lista de claves se detiene en ella) y ordena las claves alfanuméricas del sort según el alfabeto de SPECIAL-NAMES nombrado; las claves numéricas comparan numéricamente bajo cualquier secuencia, y NATIVE/STANDARD-1 no necesitan tabla. ST139A y ST140A compilan y se ejecutan limpios al primer intento.

ST (Sort/Merge) está completo en ambos ejes: 40/40 compilación, 39/39 ejecución, 735 PASS / 0 FAIL / 0 DELETED — desde 29/39 y 683/86 al comienzo de la sesión. Compilación de la suite completa: 412 -> 414 de 421 (98.3%). Puerta de cierre completa: NC, SQ, IX, RL, IF, IC todos exactos. Próximo módulo en curso: SM (Source text manipulation).

Post añadido a las 10:41. Post anterior a las 10:40

=== PowerRustCOBOL 1.62.120 — 2026-08-31 ===
• SM — los copybooks hablan las mismas tarjetas que los miembros
Dos reparaciones de fidelidad del arnés, sin cambio alguno en el runtime: el texto de los copybooks pasa ahora por la misma canonicalización de tarjetas X que el fuente de los miembros (SM103A escribía XXXXP001, SM104A leía XXXXD001 — cinco comprobaciones contra un archivo que nunca existió), y la pasada de ejecución expande COPY contra la biblioteca de la suite antes de juzgar la compilabilidad, como el censo hizo siempre (once de los diecisiete miembros de SM figuraban como "did not compile" en el eje de ejecución mientras catorce compilaban en el censo).

SM (Source text manipulation): 4 -> 9 de 17 limpios; aserciones 32 PASS / 9 FAIL -> 271 / 9. Puerta completa después (la puerta de compilación toca todos los módulos): NC, SQ, IX, RL, IF, IC, ST todos exactos.

=== PowerRustCOBOL 1.62.121 — 2026-08-31 ===
• El pseudotexto se empareja por palabras de texto
COPY ... REPLACING ==a== BY ==b== (y REPLACE) comparaban el pseudotexto con una comparación literal de cadenas, pero el operando y el copybook parten sus líneas de forma distinta, de modo que un operando de varias palabras nunca podía coincidir. El emparejamiento se hace ahora por secuencias de palabras de texto normalizadas en espacios, sin distinguir mayúsculas de minúsculas, con los separadores finales desprendidos como palabras propias (...X(115) coincide con el X(115). pegado del copybook).

SM (Source text manipulation): 9 -> 10 de 17 limpios (SM201A), 271 -> 281 PASS / 10 FAIL; SM208A compila — compilación de toda la suite 414 -> 415 de 421 (98.6%). Puerta completa: NC, SQ, IX, RL, IF, IC, ST todos exactos.

=== PowerRustCOBOL 1.62.122 — 2026-08-31 ===
• Arnés — SM204A se ejecuta después de su constructor
SM204A comprueba el archivo que escribe SM203A (el mismo par constructor/verificador que SM103A/SM104A), pero la cadena nunca se había declarado: abría un archivo que nadie había escrito y cada COPY-TEST leía ceros. Con el productor declarado, SM204A no informa de ningún fallo — el runtime nunca estuvo equivocado.

SM (Source text manipulation): 10 -> 11 de 17 limpios; aserciones 281 PASS / 10 FAIL -> 285 / 6.

=== PowerRustCOBOL 1.62.123 — 2026-08-31 ===
• COPY lee de la biblioteca que nombra
COPY member OF library selecciona qué biblioteca suministra el texto — el CCVS85 SM207A mantiene el mismo nombre de miembro en dos bibliotecas con contenidos distintos, y el expansor ignoraba el calificador y traía el mismo texto para ambas. El calificador se resuelve ahora a un subdirectorio de la ruta de copybooks (la búsqueda plana no cambia cuando está ausente), y el arnés planta las dos bibliotecas alternativas de la suite bajo los nombres de tarjeta X-47 y X-48 que usan sus programas.

SM (Source text manipulation): 11 -> 12 de 17 limpios; 285 PASS / 6 FAIL -> 286 / 5. Puerta completa: NC, SQ, IX, RL, IF, IC, ST todos exactos.

=== PowerRustCOBOL 1.62.124 — 2026-08-31 ===
• Un 01 FILLER REDEFINES de nivel superior comparte su almacenamiento
Una redefinición sin nombre solo se conectaba a la maquinaria de solapamiento cuando tenía un padre bajo el que registrar su ranura sintética — un FILLER de nivel 01 no lo tiene, así que la redefinición entera se descartaba en silencio y sus hijos leían espacios con independencia de lo que contuviera el elemento redefinido. El CCVS85 SM208A relee su resultado de REPLACE de 322 bytes exactamente a través de esta forma.

SM (Source text manipulation): aserciones 286 PASS / 5 FAIL -> 286 / 4. Puerta completa: NC, SQ, IX, RL, IF, IC, ST todos exactos; barrido del runtime limpio.

=== PowerRustCOBOL 1.62.125 — 2026-08-31 ===
• Los separadores son intercambiables en el pseudotexto
Una coma o un punto y coma aislados son separadores, no palabras de texto, así que REPLACE ==MOVE; "FAIL" , TO== debe coincidir con MOVE , "FAIL"; TO — XII-7 3.4 GR6(b), CCVS85 SM208A REP-TEST-8. El emparejamiento descarta ahora las , / ; aisladas de ambas secuencias; el punto sigue siendo significativo.

Post añadido a las 10:43. Post anterior a las 10:41

SM208A: 11 de 11. SM (Source text manipulation): 12 -> 13 de 17, 286 PASS / 3 FAIL. Puerta completa: NC, SQ, IX, RL, IF, IC, ST todos exactos.

=== PowerRustCOBOL 1.62.126 — 2026-08-31 ===
• Los miembros de flagging de SM quedan resueltos
flag_high_subset emite ahora NON-CONFORMING STANDARD para COPY ... REPLACING y para la sentencia REPLACE — las dos expectativas de SM401M coinciden. SM301M prueba el flagging de subconjunto intermedio del COPY simple, la misma clase inalcanzable por diseño que IX301M/RL301M/ST301M, y se une a su exclusión bajo la resolución vigente (SM se puntúa sobre 16).

SM (Source text manipulation): 288 PASS / 0 FAIL, 14 de 16 — el único hueco restante es el eje de compilación (SM202A, SM206A). Puerta completa: NC, SQ, IX, RL, IF, IC, ST todos exactos.

=== PowerRustCOBOL 1.62.127 — 2026-08-31 ===
• SM (Source text manipulation) TERMINADO
La última ronda de semántica de operandos de REPLACING, en un solo aterrizaje: un operando literal de cadena conserva sus comillas; un operando identificador abarca su cadena de calificación IN/OF y su subíndice entre paréntesis; todos los pares se aplican en UNA sola pasada, gana la primera coincidencia y un reemplazo nunca se vuelve a examinar (==1== BY ==5== ==5== BY ==7== se detiene en 5); el modismo de palabra parcial :TAG: es una pre-pasada de subcadenas; y una línea de depuración en el texto de biblioteca participa en el emparejamiento, de modo que el REPLACING destinado a eliminarla puede hacerlo.

SM está completo en ambos ejes: 17/17 en compilación, 16/16 en ejecución (SM301M excluido por la resolución 301M vigente), 311 PASS / 0 FAIL / 3 DELETED — los tres borrados son pruebas comentadas por la propia distribución. Desde 14/17 · 4/17 · 32 PASS / 9 FAIL en la línea base. Compilación de toda la suite: 415 -> 417 de 421 (99.0%). Puerta completa: NC, SQ, IX, RL, IF, IC, ST todos exactos. El único hueco de compilación en alcance que queda en toda la suite son los cuatro miembros de DB.

=== PowerRustCOBOL 1.62.128 — 2026-08-31 ===
• GO-TO es un nombre, no un verbo
El lexer asignaba la palabra única con guiones GO-TO al verbo GO TO. Es una palabra definida por el usuario perfectamente legal — el CCVS85 DB101A da a una SECTION el nombre GO-TO —, así que GO-TO SECTION. se analizaba como una sentencia GO TO. Una línea eliminada: el verbo son las dos palabras GO TO.

DB101A, DB102A y DB103M compilan; compilación de toda la suite 417 -> 420 de 421 (99.8%). El único fallo en alcance restante es DB205A, cuyo contenido es la maquinaria de COMMUNICATION que la resolución de alcance excluye — queda registrada para el operador una propuesta de extensión de esa resolución. Puerta completa: los ocho módulos protegidos exactos.

=== PowerRustCOBOL 1.62.129 — 2026-08-31 ===
• La campaña NIST CCVS85 se cierra al 100 %
Por resolución del operador, DB205A — DB por nombre, COMMUNICATION por contenido (DISABLE/ENABLE con CD-name bajo USE FOR DEBUGGING) — se une a la exclusión de alcance de CM y se puntúa bajo CM. El censo lee 420 / 420, el 100.0 % de la suite en alcance, 0 FAIL, y DB completa su eje de compilación 14/14.

Clasificación final en toda la suite: NC, SQ, IX, RL, IF, IC, ST y SM, todos al 100 % en ambos ejes — 8362 aserciones PASS, 0 FAIL, 3 DELETED (las pruebas comentadas por la propia distribución). Cada exclusión es una resolución del operador registrada en NIST/progress.json.

Anthropic Claude Codex Agent