accesibilidad: el BRAILLE, con displays y contracción
brltty sella con 50 drivers de hardware, 307 tablas propias y liblouis cableada; liblouis con 478 tablas de traducción. Braille no es una alternativa de gusto a la voz: es lo que permite leer código, ortografía y matemáticas —donde escuchar no alcanza— y el único camino para quien además no oye. liblouis cuesta CERO deps nuevas: medido sobre su configure.ac, su única dep externa es yaml y va detrás de --with-yaml (tests). Y tarball de release con configure generado — tercera vez que esa comprobación ahorra escribir un autoconf. ⚠ EL CONTROL QUE IMPORTA: --enable-liblouis no garantiza nada por sí solo. brltty la busca por pkg-config y le pide la variable `tablesdir` para hornearla como LOUIS_TABLES_DIRECTORY (configure.ac:1214-1220). Si el .pc no la definiera, el valor quedaría VACÍO, el build saldría igual, y brltty tendría «soporte de braille contraído» sin saber dónde están las tablas. Se comprueba en el BINARIO, que es donde el valor vive. ⚠ TRES MUROS, Y DOS SON «UN FALLO QUE SALE CON ÉXITO»: 1. -Werror=date-time del lab paró el build en los headers de los drivers de voz y pantalla. Desactivarlo es seguro porque el sandbox exporta SOURCE_DATE_EPOCH=1 y clang hace que __DATE__/__TIME__ rindan esa fecha fija: el binario sigue determinista. Mismo razonamiento que fastfetch/cairo/ appstream — no es una excepción nueva. 2. brltty IGNORA DESTDIR: su common.mk no lo menciona ni una vez, usa INSTALL_ROOT. El sandbox exporta DESTDIR=/out para todos, así que la forma habitual era un no-op y el install escribía en la raíz del sandbox. Falló SÓLO porque el overlay no lo dejó: si lo hubiera dejado, el build habría terminado con éxito y /out VACÍO ⇒ artefacto vacío sellado y cache-hit envenenado. La única razón de que no pasara fue un permiso. Corolario en la receta: que `make install` no falle no prueba que haya instalado donde creés. 3. El msgfmt de gettext-tiny NO soporta la opción larga --output-file, y devuelve rc=0 sin escribir nada. Aislado parte por parte: con -o el mismo ar.po compila (67 300 bytes); con --output-file falla igual el francés ⇒ no es el idioma, es la opción, y ar.po era sólo el primero por orden alfabético. Parche a -o: entran las NUEVE traducciones en vez de perderlas con --disable-i18n. ⚠ Y me equivoqué en el camino: este repo tiene documentado que ese msgfmt muere con el árabe, así que iba a quitar ar.po; probé primero y «compilaba bien» — cierto, pero con -o, que no es la invocación del build. Verifiqué el fichero correcto con el comando equivocado, y la conclusión era correcta por el motivo equivocado. Al reproducir un fallo a mano, la invocación EXACTA del log. --disable-python-bindings explícito y dicho: el binding es un .pyx y necesita Cython, que no está en el corpus ni en ninguna cola, y el configure sólo emite un WARNING si falta — o sea que sin apagarlo se saltaría en silencio. Lo que bloquea es preciso: brltty sirve para la consola y para cualquier app que use BrlAPI en C, pero orca no podrá usar braille hasta que exista una receta de cython. DECLARADO en los 5 escritorios: speech-dispatcher, dotconf, brltty, liblouis. 135 MB. Y queda anotado un hueco que esta línea NO tapa: el perfil `cli` no lleva ninguna pieza de accesibilidad, y brltty es justamente lo que lee la consola de texto, que es lo que ese perfil ES. No se declara ahí por simetría con espeak-ng, pero es una decisión pendiente y no un olvido.
Semántico sayk'uy
Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.
3 qillqakuna · +425 −0
- docs/state/targets.toml +180 −0
- recipes/brltty.toml +175 −0
- recipes/liblouis.toml +70 −0
diff --git a/docs/state/targets.toml b/docs/state/targets.toml
index 1de0061f..675275f2 100644
--- a/docs/state/targets.toml
+++ b/docs/state/targets.toml
@@ -606,6 +606,42 @@ paquetes = [
# `librsvg`, con el mismo coste medido (promover arrastra 35 recetas de GNOME). Con esto la distro
# HABLA; que además LEA LA PANTALLA es la otra mitad y es una decisión de campaña.
"espeak-ng", "pcaudiolib", "sonic",
+ # ── Y LA ACCESIBILIDAD COMPLETA (2026-10-20), salvo el lector de pantalla ───────────────────
+ # `espeak-ng` dio la VOZ. Lo que entra acá es el resto de la cadena, y cada pieza cubre un hueco
+ # distinto que las otras no tapan:
+ #
+ # `speech-dispatcher` el SERVIDOR de voz. espeak-ng es un sintetizador: si cada app lo invoca
+ # `dotconf` sola, dos que hablen a la vez se pisan y nadie puede interrumpir a nadie.
+ # Esto es la cola con PRIORIDADES, la API estable en C (`libspeechd`) y en
+ # Python (`speechd`, que es por donde hablará `orca`), y los sintetizadores
+ # detrás como módulos. `dotconf` es su parser de configuración, la única
+ # dep que faltaba
+ # `brltty` los DISPLAYS BRAILLE, con 50 drivers de hardware. No es una alternativa
+ # de gusto a la voz: braille es lo que permite leer código, ortografía y
+ # matemáticas —donde escuchar no alcanza— y el ÚNICO camino para quien
+ # además no oye. Con voz y sin braille se deja afuera a sordociegos por
+ # completo
+ # `liblouis` braille CONTRAÍDO, con 478 tablas. Sin esto brltty escribe una celda por
+ # carácter: legible, tres veces más largo, y nadie lee así un texto
+ #
+ # ⚠ PESO: 135 MB los cuatro (brltty 62, speech-dispatcher 58, liblouis 15, dotconf <1).
+ #
+ # ⚠ **LO QUE SIGUE FALTANDO, con nombre y causa**: `orca`, el lector de pantalla. Es la pieza que
+ # convierte «el sistema puede hablar» en «el sistema me cuenta qué hay en la pantalla», y está
+ # bloqueada por DOS cosas distintas:
+ # · para hablar necesita `at-spi2-core` y `py3-gobject`, que viven en el mundo paralelo
+ # dinámico+introspectado de `incoming-gnome`. Promover esa raíz cuesta 35 recetas de GNOME
+ # rearmando (medido promoviendo y revirtiendo); la alternativa es duplicar en el corpus.
+ # Es una decisión de operador, no algo que se hace de paso
+ # · para el BRAILLE necesita además el módulo Python `brlapi`, que exige **Cython** — no está
+ # en el corpus ni en ninguna cola. Ver la nota de `recipes/brltty.toml`
+ #
+ # ⚠ **Y UN HUECO QUE ESTA LÍNEA NO TAPA, dicho en vez de disimulado:** el perfil `cli` no lleva
+ # NINGUNA pieza de accesibilidad. Y `brltty` es justamente lo que lee la **consola de texto**, que
+ # es lo que ese perfil ES: alguien que no ve y instala la imagen `cli` no tiene forma de usar la
+ # máquina. No se declara acá por simetría —se sigue dónde estaba `espeak-ng`— pero es una decisión
+ # pendiente y no un olvido.
+ "speech-dispatcher", "dotconf", "brltty", "liblouis",
"fontconfig-shared", # /etc/fonts/fonts.conf y el soname que enlaza el riel
"freetype-shared", # libfontconfig.so.1 pide libfreetype.so.6: sin ella el riel muere al cargar
"libpng-shared", # y freetype pide libpng16.so.16 — la cadena entera entro por querer letras
@@ -803,6 +839,42 @@ paquetes = [
# `librsvg`, con el mismo coste medido (promover arrastra 35 recetas de GNOME). Con esto la distro
# HABLA; que además LEA LA PANTALLA es la otra mitad y es una decisión de campaña.
"espeak-ng", "pcaudiolib", "sonic",
+ # ── Y LA ACCESIBILIDAD COMPLETA (2026-10-20), salvo el lector de pantalla ───────────────────
+ # `espeak-ng` dio la VOZ. Lo que entra acá es el resto de la cadena, y cada pieza cubre un hueco
+ # distinto que las otras no tapan:
+ #
+ # `speech-dispatcher` el SERVIDOR de voz. espeak-ng es un sintetizador: si cada app lo invoca
+ # `dotconf` sola, dos que hablen a la vez se pisan y nadie puede interrumpir a nadie.
+ # Esto es la cola con PRIORIDADES, la API estable en C (`libspeechd`) y en
+ # Python (`speechd`, que es por donde hablará `orca`), y los sintetizadores
+ # detrás como módulos. `dotconf` es su parser de configuración, la única
+ # dep que faltaba
+ # `brltty` los DISPLAYS BRAILLE, con 50 drivers de hardware. No es una alternativa
+ # de gusto a la voz: braille es lo que permite leer código, ortografía y
+ # matemáticas —donde escuchar no alcanza— y el ÚNICO camino para quien
+ # además no oye. Con voz y sin braille se deja afuera a sordociegos por
+ # completo
+ # `liblouis` braille CONTRAÍDO, con 478 tablas. Sin esto brltty escribe una celda por
+ # carácter: legible, tres veces más largo, y nadie lee así un texto
+ #
+ # ⚠ PESO: 135 MB los cuatro (brltty 62, speech-dispatcher 58, liblouis 15, dotconf <1).
+ #
+ # ⚠ **LO QUE SIGUE FALTANDO, con nombre y causa**: `orca`, el lector de pantalla. Es la pieza que
+ # convierte «el sistema puede hablar» en «el sistema me cuenta qué hay en la pantalla», y está
+ # bloqueada por DOS cosas distintas:
+ # · para hablar necesita `at-spi2-core` y `py3-gobject`, que viven en el mundo paralelo
+ # dinámico+introspectado de `incoming-gnome`. Promover esa raíz cuesta 35 recetas de GNOME
+ # rearmando (medido promoviendo y revirtiendo); la alternativa es duplicar en el corpus.
+ # Es una decisión de operador, no algo que se hace de paso
+ # · para el BRAILLE necesita además el módulo Python `brlapi`, que exige **Cython** — no está
+ # en el corpus ni en ninguna cola. Ver la nota de `recipes/brltty.toml`
+ #
+ # ⚠ **Y UN HUECO QUE ESTA LÍNEA NO TAPA, dicho en vez de disimulado:** el perfil `cli` no lleva
+ # NINGUNA pieza de accesibilidad. Y `brltty` es justamente lo que lee la **consola de texto**, que
+ # es lo que ese perfil ES: alguien que no ve y instala la imagen `cli` no tiene forma de usar la
+ # máquina. No se declara acá por simetría —se sigue dónde estaba `espeak-ng`— pero es una decisión
+ # pendiente y no un olvido.
+ "speech-dispatcher", "dotconf", "brltty", "liblouis",
# `dejavu-fonts-nerd` es la MISMA DejaVu Sans Mono parcheada con Nerd Fonts (~10.000 glifos de
# iconos: Powerline, Font Awesome, Devicons…) que usan starship, lsd, eza, neovim y las
# statuslines; sin ellos dibujan cuadraditos. Se AÑADE, no reemplaza: la licencia de Bitstream
@@ -1510,6 +1582,42 @@ paquetes = [
# `librsvg`, con el mismo coste medido (promover arrastra 35 recetas de GNOME). Con esto la distro
# HABLA; que además LEA LA PANTALLA es la otra mitad y es una decisión de campaña.
"espeak-ng", "pcaudiolib", "sonic",
+ # ── Y LA ACCESIBILIDAD COMPLETA (2026-10-20), salvo el lector de pantalla ───────────────────
+ # `espeak-ng` dio la VOZ. Lo que entra acá es el resto de la cadena, y cada pieza cubre un hueco
+ # distinto que las otras no tapan:
+ #
+ # `speech-dispatcher` el SERVIDOR de voz. espeak-ng es un sintetizador: si cada app lo invoca
+ # `dotconf` sola, dos que hablen a la vez se pisan y nadie puede interrumpir a nadie.
+ # Esto es la cola con PRIORIDADES, la API estable en C (`libspeechd`) y en
+ # Python (`speechd`, que es por donde hablará `orca`), y los sintetizadores
+ # detrás como módulos. `dotconf` es su parser de configuración, la única
+ # dep que faltaba
+ # `brltty` los DISPLAYS BRAILLE, con 50 drivers de hardware. No es una alternativa
+ # de gusto a la voz: braille es lo que permite leer código, ortografía y
+ # matemáticas —donde escuchar no alcanza— y el ÚNICO camino para quien
+ # además no oye. Con voz y sin braille se deja afuera a sordociegos por
+ # completo
+ # `liblouis` braille CONTRAÍDO, con 478 tablas. Sin esto brltty escribe una celda por
+ # carácter: legible, tres veces más largo, y nadie lee así un texto
+ #
+ # ⚠ PESO: 135 MB los cuatro (brltty 62, speech-dispatcher 58, liblouis 15, dotconf <1).
+ #
+ # ⚠ **LO QUE SIGUE FALTANDO, con nombre y causa**: `orca`, el lector de pantalla. Es la pieza que
+ # convierte «el sistema puede hablar» en «el sistema me cuenta qué hay en la pantalla», y está
+ # bloqueada por DOS cosas distintas:
+ # · para hablar necesita `at-spi2-core` y `py3-gobject`, que viven en el mundo paralelo
+ # dinámico+introspectado de `incoming-gnome`. Promover esa raíz cuesta 35 recetas de GNOME
+ # rearmando (medido promoviendo y revirtiendo); la alternativa es duplicar en el corpus.
+ # Es una decisión de operador, no algo que se hace de paso
+ # · para el BRAILLE necesita además el módulo Python `brlapi`, que exige **Cython** — no está
+ # en el corpus ni en ninguna cola. Ver la nota de `recipes/brltty.toml`
+ #
+ # ⚠ **Y UN HUECO QUE ESTA LÍNEA NO TAPA, dicho en vez de disimulado:** el perfil `cli` no lleva
+ # NINGUNA pieza de accesibilidad. Y `brltty` es justamente lo que lee la **consola de texto**, que
+ # es lo que ese perfil ES: alguien que no ve y instala la imagen `cli` no tiene forma de usar la
+ # máquina. No se declara acá por simetría —se sigue dónde estaba `espeak-ng`— pero es una decisión
+ # pendiente y no un olvido.
+ "speech-dispatcher", "dotconf", "brltty", "liblouis",
# `dejavu-fonts-nerd` es la MISMA DejaVu Sans Mono parcheada con Nerd Fonts (~10.000 glifos de
# iconos: Powerline, Font Awesome, Devicons…) que usan starship, lsd, eza, neovim y las
# statuslines; sin ellos dibujan cuadraditos. Se AÑADE, no reemplaza: la licencia de Bitstream
@@ -1857,6 +1965,42 @@ paquetes = [
# `librsvg`, con el mismo coste medido (promover arrastra 35 recetas de GNOME). Con esto la distro
# HABLA; que además LEA LA PANTALLA es la otra mitad y es una decisión de campaña.
"espeak-ng", "pcaudiolib", "sonic",
+ # ── Y LA ACCESIBILIDAD COMPLETA (2026-10-20), salvo el lector de pantalla ───────────────────
+ # `espeak-ng` dio la VOZ. Lo que entra acá es el resto de la cadena, y cada pieza cubre un hueco
+ # distinto que las otras no tapan:
+ #
+ # `speech-dispatcher` el SERVIDOR de voz. espeak-ng es un sintetizador: si cada app lo invoca
+ # `dotconf` sola, dos que hablen a la vez se pisan y nadie puede interrumpir a nadie.
+ # Esto es la cola con PRIORIDADES, la API estable en C (`libspeechd`) y en
+ # Python (`speechd`, que es por donde hablará `orca`), y los sintetizadores
+ # detrás como módulos. `dotconf` es su parser de configuración, la única
+ # dep que faltaba
+ # `brltty` los DISPLAYS BRAILLE, con 50 drivers de hardware. No es una alternativa
+ # de gusto a la voz: braille es lo que permite leer código, ortografía y
+ # matemáticas —donde escuchar no alcanza— y el ÚNICO camino para quien
+ # además no oye. Con voz y sin braille se deja afuera a sordociegos por
+ # completo
+ # `liblouis` braille CONTRAÍDO, con 478 tablas. Sin esto brltty escribe una celda por
+ # carácter: legible, tres veces más largo, y nadie lee así un texto
+ #
+ # ⚠ PESO: 135 MB los cuatro (brltty 62, speech-dispatcher 58, liblouis 15, dotconf <1).
+ #
+ # ⚠ **LO QUE SIGUE FALTANDO, con nombre y causa**: `orca`, el lector de pantalla. Es la pieza que
+ # convierte «el sistema puede hablar» en «el sistema me cuenta qué hay en la pantalla», y está
+ # bloqueada por DOS cosas distintas:
+ # · para hablar necesita `at-spi2-core` y `py3-gobject`, que viven en el mundo paralelo
+ # dinámico+introspectado de `incoming-gnome`. Promover esa raíz cuesta 35 recetas de GNOME
+ # rearmando (medido promoviendo y revirtiendo); la alternativa es duplicar en el corpus.
+ # Es una decisión de operador, no algo que se hace de paso
+ # · para el BRAILLE necesita además el módulo Python `brlapi`, que exige **Cython** — no está
+ # en el corpus ni en ninguna cola. Ver la nota de `recipes/brltty.toml`
+ #
+ # ⚠ **Y UN HUECO QUE ESTA LÍNEA NO TAPA, dicho en vez de disimulado:** el perfil `cli` no lleva
+ # NINGUNA pieza de accesibilidad. Y `brltty` es justamente lo que lee la **consola de texto**, que
+ # es lo que ese perfil ES: alguien que no ve y instala la imagen `cli` no tiene forma de usar la
+ # máquina. No se declara acá por simetría —se sigue dónde estaba `espeak-ng`— pero es una decisión
+ # pendiente y no un olvido.
+ "speech-dispatcher", "dotconf", "brltty", "liblouis",
# ⚠ «sin puntero» lo decía este comentario desde que se escribió y NO era cierto: `cosmic-icons`
# instala iconos de aplicación, no cursores, así que este perfil llegó a 43/43 con el puntero
# INVISIBLE. Lo cazó `vigia-imagen.py` el 2026-09-04. Los cursores son su propia raíz.
@@ -2223,6 +2367,42 @@ paquetes = [
# `librsvg`, con el mismo coste medido (promover arrastra 35 recetas de GNOME). Con esto la distro
# HABLA; que además LEA LA PANTALLA es la otra mitad y es una decisión de campaña.
"espeak-ng", "pcaudiolib", "sonic",
+ # ── Y LA ACCESIBILIDAD COMPLETA (2026-10-20), salvo el lector de pantalla ───────────────────
+ # `espeak-ng` dio la VOZ. Lo que entra acá es el resto de la cadena, y cada pieza cubre un hueco
+ # distinto que las otras no tapan:
+ #
+ # `speech-dispatcher` el SERVIDOR de voz. espeak-ng es un sintetizador: si cada app lo invoca
+ # `dotconf` sola, dos que hablen a la vez se pisan y nadie puede interrumpir a nadie.
+ # Esto es la cola con PRIORIDADES, la API estable en C (`libspeechd`) y en
+ # Python (`speechd`, que es por donde hablará `orca`), y los sintetizadores
+ # detrás como módulos. `dotconf` es su parser de configuración, la única
+ # dep que faltaba
+ # `brltty` los DISPLAYS BRAILLE, con 50 drivers de hardware. No es una alternativa
+ # de gusto a la voz: braille es lo que permite leer código, ortografía y
+ # matemáticas —donde escuchar no alcanza— y el ÚNICO camino para quien
+ # además no oye. Con voz y sin braille se deja afuera a sordociegos por
+ # completo
+ # `liblouis` braille CONTRAÍDO, con 478 tablas. Sin esto brltty escribe una celda por
+ # carácter: legible, tres veces más largo, y nadie lee así un texto
+ #
+ # ⚠ PESO: 135 MB los cuatro (brltty 62, speech-dispatcher 58, liblouis 15, dotconf <1).
+ #
+ # ⚠ **LO QUE SIGUE FALTANDO, con nombre y causa**: `orca`, el lector de pantalla. Es la pieza que
+ # convierte «el sistema puede hablar» en «el sistema me cuenta qué hay en la pantalla», y está
+ # bloqueada por DOS cosas distintas:
+ # · para hablar necesita `at-spi2-core` y `py3-gobject`, que viven en el mundo paralelo
+ # dinámico+introspectado de `incoming-gnome`. Promover esa raíz cuesta 35 recetas de GNOME
+ # rearmando (medido promoviendo y revirtiendo); la alternativa es duplicar en el corpus.
+ # Es una decisión de operador, no algo que se hace de paso
+ # · para el BRAILLE necesita además el módulo Python `brlapi`, que exige **Cython** — no está
+ # en el corpus ni en ninguna cola. Ver la nota de `recipes/brltty.toml`
+ #
+ # ⚠ **Y UN HUECO QUE ESTA LÍNEA NO TAPA, dicho en vez de disimulado:** el perfil `cli` no lleva
+ # NINGUNA pieza de accesibilidad. Y `brltty` es justamente lo que lee la **consola de texto**, que
+ # es lo que ese perfil ES: alguien que no ve y instala la imagen `cli` no tiene forma de usar la
+ # máquina. No se declara acá por simetría —se sigue dónde estaba `espeak-ng`— pero es una decisión
+ # pendiente y no un olvido.
+ "speech-dispatcher", "dotconf", "brltty", "liblouis",
# `dejavu-fonts-nerd` es la MISMA DejaVu Sans Mono parcheada con Nerd Fonts (~10.000 glifos de
# iconos: Powerline, Font Awesome, Devicons…) que usan starship, lsd, eza, neovim y las
# statuslines; sin ellos dibujan cuadraditos. Se AÑADE, no reemplaza: la licencia de Bitstream
diff --git a/recipes/brltty.toml b/recipes/brltty.toml
new file mode 100644
index 00000000..fb551e8b
--- /dev/null
+++ b/recipes/brltty.toml
@@ -0,0 +1,175 @@
+# brltty 6.9.1 — el driver de DISPLAYS BRAILLE. Cierra el hueco de accesibilidad para quien no ve
+# y no usa voz.
+#
+# ══ POR QUÉ ES SU PROPIO FRENTE, Y NO «lo mismo que la voz» ════════════════════════════════════
+# La voz y el braille no son alternativas de gusto. Braille es lo que permite leer código,
+# ortografía, tablas y matemáticas —cosas donde escuchar no alcanza— y es el único camino para quien
+# además no oye. Una distro con voz y sin braille deja afuera a sordociegos por completo.
+#
+# brltty lee la pantalla (la consola de Linux y, vía BrlAPI, las apps) y la escribe en el display.
+# Trae **42 drivers de braille** para hardware de distintos fabricantes.
+#
+# ══ LAS PERILLAS QUE IMPORTAN ══════════════════════════════════════════════════════════════════
+# --enable-api **BrlAPI**: el protocolo por el que una aplicación —`orca`, un editor— toma
+# el display en vez de dejárselo a la lectura de pantalla. Sin esto brltty
+# sirve para la consola y nada más
+# --enable-liblouis braille CONTRAÍDO. Sin liblouis es una celda por carácter: legible, tres
+# veces más largo, y nadie lee así un texto. Ver `recipes/liblouis.toml`
+# --disable-x la distro es Wayland-only
+# --disable-icu `icu` no está en el corpus. Lo que se pierde es normalización Unicode fina
+# en la conversión de texto; `--enable-iconv` cubre el caso general
+# --disable-polkit no hay polkit en esta pila
+# --disable-gpm el ratón de consola; no está en el corpus
+#
+# ⚠ **`--disable-python-bindings`, Y ES UNA PÉRDIDA CONCRETA QUE SE DICE EN VEZ DE DISIMULARSE.**
+# El binding es un `.pyx` y necesita **Cython** (`Bindings/Python/bindings.m4:108`:
+# `AC_PATH_PROGS([CYTHON], [cython3 cython])`), que **no está en el corpus ni en ninguna cola**
+# (comprobado). Y el `configure` sólo emite un `AC_MSG_WARN` si falta: o sea que **sin apagarlo
+# explícitamente el binding se salta EN SILENCIO** y el artefacto sale igual.
+#
+# Lo que eso bloquea, concreto: `orca` importa el módulo `brlapi` de Python para manejar el
+# display. ⇒ con esta receta brltty funciona para la CONSOLA y para cualquier app que use BrlAPI en
+# C, pero **orca no podrá usar braille hasta que exista una receta de `cython`**. Se apaga a
+# propósito para que el día que alguien lo necesite el motivo esté escrito, en vez de descubrir un
+# hueco que nadie decidió.
+#
+# Los otros bindings (java, lisp, lua, ocaml, tcl, emacs) se apagan por lo mismo: sus lenguajes no
+# están en el corpus y el `configure` los saltaría sin avisar.
+#
+# ⚠ `--sysconfdir=/etc`: con `--prefix=/usr` a secas autotools deriva `/usr/etc` en SILENCIO, y el
+# binario hornea esa ruta — el artefacto queda auto-consistente y la configuración, donde nadie la
+# busca. Pasó con `speech-dispatcher` el mismo día; 31 recetas del repo ya lo pasan.
+#
+# ✅ **`--enable-i18n` CON las nueve traducciones, incluida `ar.po` — y eso es MEDIDO, no un
+# descuido.** Este repo tiene documentado que el `msgfmt` de `gettext-tiny` muere con el árabe
+# (SIGILL y un `.mo` de 0 bytes), así que iba a parchear `ar.po` fuera. Antes lo probé: el
+# `ar.po` de brltty **compila bien** (rc=0, `.mo` de 67 300 bytes), igual que `fr` y `zh`. La nota
+# de la memoria vale para un caso concreto de plurales que este fichero no dispara. *Una memoria
+# describe lo que era cierto cuando se escribió; se verifica antes de actuar sobre ella.*
+name = "brltty"
+version = "6.9.1"
+license = "LGPL-2.1-or-later"
+
+[source]
+# ⚠ El tarball OFICIAL de brltty.app, no el archive del tag de GitHub: el del tag trae sólo
+# `configure.ac` y el lab no tiene autoconf ni automake. Tercera vez que mirar si upstream publica
+# un tarball de release ahorra escribir un autoconf.
+tarball = "https://brltty.app/archive/brltty-6.9.1.tar.xz"
+sha256 = "cf4619ce1471690e4ecc5544ee6289cef6c5bdf5855a1ac8dcc63f5beedf108d"
+
+[build]
+compiler = "zig-cc"
+target = "x86_64-linux-musl"
+link = "dynamic"
+flags = []
+
+[build.phases]
+configure = '''
+set -e
+[ -f ./configure ] || { echo "brltty: el tarball no trae ./configure — ¿se cambió por el archive del tag?" >&2; exit 1; }
+# ⚠ **PARCHE: el `msgfmt` de `gettext-tiny` no soporta la opción LARGA `--output-file`.**
+# `Messages/Makefile.in:57` invoca así:
+#
+# $(MSGFMT) --output-file $@ -- <fichero>.po
+# ⇒ fopen: No such file or directory … y **sale con rc=0**
+#
+# Medido aislando cada parte: con `-o` el mismo `ar.po` compila (67 300 bytes); con `--output-file`
+# falla, y falla igual el francés. O sea que **no es el idioma, es la opción**, y `ar.po` sólo era
+# el primero por orden alfabético.
+#
+# ⚠ **Y me equivoqué antes de llegar acá, en dos pasos que vale anotar.** (1) Este repo tiene
+# documentado que el msgfmt de gettext-tiny muere con el árabe, así que iba a quitar `ar.po`; probé
+# primero y «compilaba bien» — cierto, pero con `-o`, que **no es la invocación que usa el build**.
+# Verifiqué el fichero correcto con el comando equivocado. (2) El tool devuelve **rc=0 mientras no
+# escribe nada**: si `make` no comprobara que el `.mo` existe, esto habría pasado en silencio. Es la
+# misma familia que el `INSTALL_ROOT` de abajo — *un fallo que sale con éxito.*
+#
+# ⇒ Se cambia a `-o`, que gettext-tiny sí entiende, y así entran **las nueve traducciones** en vez
+# de perderlas con `--disable-i18n`. El hueco de `gettext-tiny` es real y va a morder a otras
+# recetas; barrerlo es su propia unidad de trabajo, no algo que se hace de paso.
+MF=Messages/Makefile.in
+grep -q -- '--output-file' "$MF" || { echo "brltty: $MF ya no usa --output-file; el parche de gettext-tiny sobra o cambió de forma — REVISAR" >&2; exit 1; }
+sed -i 's/--output-file \$@ --/-o $@/' "$MF"
+grep -q -- '--output-file' "$MF" && { echo "brltty: el parche de --output-file no se aplicó" >&2; grep -n 'MSGFMT' "$MF" >&2; exit 1; }
+grep -q -- '-o \$@' "$MF" || { echo "brltty: tras el parche no aparece '-o \$@' en $MF" >&2; grep -n 'MSGFMT' "$MF" >&2; exit 1; }
+echo "brltty: msgfmt pasa a la opción corta (gettext-tiny no entiende --output-file)"
+
+# ⚠ `-Wno-error=date-time`: `Headers/spk_driver.h:52` y `scr_driver.h:46` usan `__DATE__`/`__TIME__`
+# y **el lab compila con `-Werror=date-time`** — su política de reproducibilidad, que acá hizo
+# exactamente su trabajo y paró el build en los drivers de voz y de pantalla.
+#
+# Desactivarlo es SEGURO **porque el sandbox exporta `SOURCE_DATE_EPOCH=1`**: con esa variable
+# definida clang hace que `__DATE__`/`__TIME__` rindan esa fecha fija en vez de la del reloj, así
+# que el binario sigue siendo determinista. Sin `SOURCE_DATE_EPOCH` esto sería hornear la hora del
+# build dentro del artefacto, que es justo lo que el warning previene. Mismo razonamiento y misma
+# cura que `fastfetch`, `cairo` y `appstream` en este repo — no es una excepción nueva.
+CFLAGS="${CFLAGS:-} -Wno-error=date-time" \
+./configure --build=$CBUILD --host=$CHOST --prefix=/usr --sysconfdir=/etc \
+ --enable-api --enable-liblouis --enable-i18n --enable-iconv --enable-expat \
+ --disable-x --disable-icu --disable-polkit --disable-gpm \
+ --disable-python-bindings --disable-java-bindings --disable-lisp-bindings \
+ --disable-lua-bindings --disable-ocaml-bindings --disable-tcl-bindings \
+ --disable-emacs-bindings
+'''
+compile = 'make -j"$(nproc)"'
+# El guardián se escribe contra lo que el `install` de upstream PROMETE —leído en
+# `Programs/Makefile.in:1199`: programas, tablas, drivers, cabeceras, mensajes, el `.pc` y la
+# API— y busca por PATRÓN y no por ruta exacta, para no rechazar un build correcto por haberme
+# equivocado de directorio. Es la lección de los cuatro guardianes demasiado estrictos del 20-oct.
+install = '''
+set -e
+# ⚠ **`INSTALL_ROOT` Y NO `DESTDIR`, Y ESTO ESTUVO A UN PASO DE SELLAR UN ARTEFACTO VACÍO.**
+# `common.mk` de brltty no menciona `DESTDIR` **ni una vez** (medido): usa su propia variable,
+# `INSTALL_ROOT`, para prefijar cada directorio de instalación. El sandbox exporta `DESTDIR=/out`
+# para todos, así que la forma habitual pasa inadvertida — y acá era un no-op:
+#
+# /usr/bin/install -c -d /usr/bin
+# /usr/bin/install -c -d /usr/libexec/brltty
+# install: cannot create directory '/usr/libexec/brltty': Value too large for data type
+#
+# Estaba instalando **en la raíz del sandbox**, no en `/out`. Falló sólo porque el overlay no lo
+# dejó escribir. **Si lo hubiera dejado, el build habría terminado con éxito y `/out` vacío** — y
+# un directorio vacío en el store no es un artefacto, es un nombre: se sella, es un cache-hit, y el
+# fallo llega hasta el final diciendo que todo fue bien. Es exactamente la regla 3 del repo, y la
+# única razón de que no pasara fue el permiso.
+#
+# ⇒ Corolario para cualquier receta nueva: **que `make install` no falle no prueba que haya
+# instalado donde creés.** El guardián de abajo lo comprueba igual, buscando los ficheros en `/out`.
+make install INSTALL_ROOT=/out
+b=$(find /out -name brltty -type f -perm -u+x 2>/dev/null | head -1)
+[ -n "$b" ] || { echo "brltty: no quedó el binario — NO sello" >&2; find /out -maxdepth 4 -type f -perm -u+x 2>/dev/null | head -20 >&2; exit 1; }
+
+# ⚠ EL CONTROL DECISIVO: que liblouis esté CABLEADA de verdad.
+# `--enable-liblouis` no garantiza nada por sí solo: brltty la busca por pkg-config y le pide la
+# variable `tablesdir` para hornearla como `LOUIS_TABLES_DIRECTORY` (`configure.ac:1214-1220`). Si
+# el `.pc` no definiera `tablesdir`, el valor quedaría VACÍO, el build saldría igual y brltty
+# tendría «soporte de braille contraído» sin saber dónde están las tablas — sellado, con contenido
+# y sin la función. Se comprueba en el BINARIO, que es donde el valor vive:
+strings "$b" 2>/dev/null | grep -q 'liblouis/tables' || {
+ echo "brltty: el binario no trae la ruta de tablas de liblouis — el braille CONTRAÍDO no funcionaría, y sin error" >&2
+ strings "$b" 2>/dev/null | grep -i louis | head -5 >&2
+ exit 1
+}
+
+# BrlAPI: sin esto ninguna aplicación puede tomar el display.
+api=$(find /out -name 'libbrlapi.so*' -o -name 'libbrlapi.a' 2>/dev/null | head -1)
+[ -n "$api" ] || { echo "brltty: no quedó libbrlapi — ninguna app podría manejar el display, sólo la consola" >&2; exit 1; }
+
+# Los drivers: 42 en el árbol. El piso es bajo a propósito —según cómo se enlacen pueden quedar
+# dentro del binario en vez de como módulos— pero CERO ficheros de datos de driver sí es un fallo.
+drv=$(find /out -name 'libbrltty*.so' 2>/dev/null | wc -l)
+# Las tablas de texto y de contracción propias de brltty.
+tbl=$(find /out -path '*brltty*' \( -name '*.ttb' -o -name '*.ctb' -o -name '*.ktb' -o -name '*.tti' \) 2>/dev/null | wc -l)
+[ "$tbl" -gt 50 ] || { echo "brltty: sólo $tbl tabla(s) propias — esperaba >50; sin ellas no sabe traducir a celdas" >&2; find /out -path '*brltty*' -name '*.ttb' 2>/dev/null | head -5 >&2; exit 1; }
+# La herramienta de tablas de contracción: es lo que prueba que ese camino se construyó.
+[ -n "$(find /out -name 'brltty-ctb' -type f 2>/dev/null | head -1)" ] || { echo "brltty: falta brltty-ctb — el compilador de tablas de contracción no se construyó" >&2; exit 1; }
+echo "brltty: binario + brlapi + $drv driver(s) externo(s) + $tbl tabla(s) + liblouis cableada"
+'''
+
+[deps]
+build = ["make", "pkgconf", "liblouis", "ncurses", "expat", "alsa-lib",
+ "gettext-tiny", "libtool", "python3",
+ # ⚠ `binutils` por el GUARDIÁN (aporta `strings`), no por el build: sin declararlo el
+ # control de liblouis fallaría por falta de herramienta y acusaría a la receta de algo que
+ # no hizo. Misma nota que `espeak-ng` y `speech-dispatcher`.
+ "binutils"]
diff --git a/recipes/liblouis.toml b/recipes/liblouis.toml
new file mode 100644
index 00000000..2936611a
--- /dev/null
+++ b/recipes/liblouis.toml
@@ -0,0 +1,70 @@
+# liblouis 3.39.0 — el traductor de braille LITERARIO. Es lo que separa «hay un display braille»
+# de «se puede leer cómodamente en braille».
+#
+# ══ QUÉ AÑADE, Y POR QUÉ NO ES UN LUJO ═════════════════════════════════════════════════════════
+# Sin liblouis, `brltty` escribe braille de ORDENADOR: una celda por carácter, uno a uno. Se puede
+# leer, pero ocupa el triple y nadie lee así un texto largo. El braille de verdad es **contraído**:
+# contracciones, signos compuestos y reglas que dependen del idioma y del contexto — «and» es una
+# celda, no tres. Eso son tablas, y liblouis es el motor que las aplica.
+#
+# ⇒ Entra junto con `brltty` y no después, por el mismo criterio que `libime-table` con el chino:
+# publicar el display sin el traductor sería dar la capacidad a medias y llamarla cerrada.
+#
+# ══ LO QUE CUESTA: NADA NUEVO ══════════════════════════════════════════════════════════════════
+# Medido sobre su `configure.ac`: su única dep externa es `yaml`, y va detrás de `--with-yaml`
+# (es para su suite de tests). ⇒ **cero deps nuevas en el corpus.**
+#
+# Se usa el tarball del RELEASE y no el archive del tag: trae `configure` ya generado y el lab no
+# tiene autoconf ni automake. Tercera vez que esa comprobación ahorra escribir un autoconf
+# (`pcaudiolib`, `speech-dispatcher`, ésta).
+#
+# `--enable-ucs4`: los `widechar` de 4 bytes. Con 2 bytes, los caracteres fuera del BMP —y buena
+# parte de las matemáticas y la notación musical braille están ahí— se truncan en silencio.
+name = "liblouis"
+version = "3.39.0"
+license = "LGPL-2.1-or-later"
+
+[source]
+tarball = "https://github.com/liblouis/liblouis/releases/download/v3.39.0/liblouis-3.39.0.tar.gz"
+sha256 = "629fa8cb0dfd9ad457c5bf47a42f0953b673e62c8ad6b1d03ddc4e2bd20008f1"
+
+[build]
+compiler = "zig-cc"
+target = "x86_64-linux-musl"
+link = "dynamic"
+flags = []
+
+[build.phases]
+configure = '''
+set -e
+[ -f ./configure ] || { echo "liblouis: el tarball no trae ./configure — ¿se cambió por el archive del tag?" >&2; exit 1; }
+./configure --build=$CBUILD --host=$CHOST --prefix=/usr --sysconfdir=/etc \
+ --enable-ucs4 --without-yaml --disable-static
+'''
+compile = 'make -j"$(nproc)"'
+# ⚠ EL GUARDIÁN DE LAS TABLAS ES EL PUNTO DE ESTA RECETA.
+#
+# `libllouis.so` sin sus tablas **carga, enlaza y traduce la identidad**: devolvería el texto sin
+# contraer y sin un solo error. Es exactamente la figura que este catálogo persigue —sellado, con
+# contenido, reproducible y sin la función que promete—, y acá es peor que en otros frentes porque
+# la promesa es que alguien que no ve pueda LEER.
+#
+# El piso de 100 no es un número inventado: upstream distribuye más de mil ficheros de tabla (una o
+# varias por idioma). Con una docena, algo se recortó; con cero, el motor es un `.so` mudo.
+install = '''
+set -e
+make install DESTDIR=/out
+so=$(find /out/usr/lib -name 'liblouis.so*' -type f 2>/dev/null | head -1)
+[ -n "$so" ] || { echo "liblouis: no quedó liblouis.so — brltty la carga dinámicamente" >&2; exit 1; }
+[ -f /out/usr/include/liblouis/liblouis.h ] || { echo "liblouis: falta include/liblouis/liblouis.h" >&2; find /out/usr/include -name 'liblouis*' >&2; exit 1; }
+t=$(find /out/usr/share/liblouis/tables -type f 2>/dev/null | wc -l)
+[ "$t" -gt 100 ] || { echo "liblouis: sólo $t fichero(s) de tabla — esperaba >100; sin tablas traduce la identidad EN SILENCIO" >&2; find /out/usr/share -maxdepth 3 2>/dev/null | head -20 >&2; exit 1; }
+# Y el `.pc`, que es como brltty la encuentra.
+[ -f /out/usr/lib/pkgconfig/liblouis.pc ] || { echo "liblouis: falta liblouis.pc — brltty no la detectaría" >&2; exit 1; }
+# `lou_translate` es la herramienta que permite comprobar a mano que traduce de verdad.
+[ -x /out/usr/bin/lou_translate ] || echo "liblouis: aviso — no quedó lou_translate; no es fatal, pero es la única forma de probar la traducción sin un display" >&2
+echo "liblouis: $(basename "$so") + $t tabla(s) + liblouis.pc"
+'''
+
+[deps]
+build = ["make", "pkgconf", "libtool"]