simi: la receta pineada y el veredicto en el SDD — sin construir todavía, y dicho así

recipes/simi.toml sigue el patrón de hammerd/netup: crate del workspace a un
commit fijado, zig cc, musl estático, strip_debug. SIN [build.phases]: el
BuildSys::Cargo instala el binario, y el symlink `sh` NO lo pone la receta a
propósito — quién gana como /bin/sh lo decide USERLAND_COMPONENTS en
takana-bootstrap, que es frente C y su propia unidad de trabajo.

El SDD queda con el veredicto (simi 0 divergencias, bash 8, brush 35), el alcance
por consumidor, los siete bugs del camino y las seis cosas que faltan. Con dos
avisos explícitos: lo medido es el build NATIVO del hub (glibc, dinámico) y el
estático musl no se construyó, así que simi no entra en ningún perfil de
targets.toml; y la shell interactiva de getty no está cubierta.
This commit is contained in:
Sergio
2026-09-21 20:24:24 +00:00
parent 6809ef4be2
commit 136594828c
2 changed files with 123 additions and 0 deletions
+83
View File
@@ -906,3 +906,86 @@ tokenizador viejo se comía, y desapareció `i+1`.
corre en el hub; el que corre dentro de la imagen es `takana-live-install.sh`, y ése es POSIX. corre en el hub; el que corre dentro de la imagen es `takana-live-install.sh`, y ése es POSIX.
La lista de «quién corre en la imagen» sigue sin estar medida, salvo los 21 empotrados, que lo son La lista de «quién corre en la imagen» sigue sin estar medida, salvo los 21 empotrados, que lo son
por construcción. por construcción.
---
# `simi` — el `/bin/sh` propio, ESCRITO y MEDIDO (2026-09-21)
**Crate:** `crates/simi/` (4448 líneas, `libc` y nada más) · **receta:** `recipes/simi.toml`
(pineada, **sin construir todavía**) · **nombre:** *simi* es «boca, lengua» en quechua — es cómo se
le habla a la máquina. `rimay` estaba tomado (la familia de embeddings de tawasuyu).
## El veredicto, primero
Banco diferencial contra el ash de busybox 1.36.1, **159 casos** — 10 fixtures de regresión, 107 de
tortura de comillas, los 23 fragmentos de las cards y los 19 `/init` empotrados:
| shell | divergencias duras | binario | latencia |
|---|---|---|---|
| **simi** | **0** | **0,48 MB** | **1,4×** el ash |
| bash 5.3.0 | 8 | 1,57 MB | 1,3× |
| brush 0.4.0 | 35 | 6,92 MB | 4,1× |
Incluye el **bucle real de `config.status`** que genera cualquier `configure` de autotools — el que
tumbó a brush. Y 47 pruebas propias del crate en verde.
⚠ **Lo medido es el build NATIVO del hub** (glibc, dinámico). El estático musl con zig cc —que es
como viajaría— **no se construyó**: falta la corrida en el lab y su sello. Hasta entonces, `simi` no
va en ningún perfil de `docs/state/targets.toml`.
## El alcance salió de la medición, no de POSIX
| consumidor | qué necesita | estado |
|---|---|---|
| las 23 cards de arje con shell | 8 builtins, 13 construcciones, ningún `if`, ninguna función | ✅ |
| los 21 `/init` empotrados | + `if`, funciones, `case`, aquí-documentos, `${…}`, posicionales, `&` | ✅ |
| `takana-live-install.sh` (corre en la imagen viva) | POSIX entero | ✅ parsea; sin correr |
| la shell interactiva de `getty` | edición de línea, historial, control de trabajos | ❌ **no está** |
Lo que **no** trae, dicho en vez de fingido: control de trabajos, edición de línea, historial,
`alias`, `getopts`, `times`, `[[ ]]`, arrays. El shell de `getty` es otro consumidor y sigue
pendiente.
## Los siete bugs — que es la parte que enseña
Ninguno se veía en la salida. Todos daban resultados plausibles, y **los seis primeros los destapó
el banco diferencial**, no el juicio:
1. **El lexer fundía lo entrecomillado con lo desnudo.** `x="a b"` quedaba como UN seg
entrecomillado, el parser ya no veía la asignación —el prefijo antes del `=` tiene que ser
desnudo— y **la ejecutaba como una orden**. El error decía «no encontrado», que manda a buscar al
lugar equivocado. Ahora el comillado es por carácter.
2. **La marca de escape interna chocaba con las barras reales.** La primera versión usaba `\` como
marca y la quitaba al final, así que `$(echo \\$V)` perdía la barra que la sustitución había
PRODUCIDO (ash: `\hola`, simi: `hola`). **La quita de comillas es sobre las comillas de la
palabra, nunca sobre el resultado de una expansión** — y eso no se representa con un escape
dentro del texto: hace falta una máscara de protección por carácter.
3. **`set -e` no se suspendía dentro de una función llamada desde una condición.** Ahora es un
contador DINÁMICO, y por eso entra solo donde tiene que entrar.
4. **Los delimitadores de `$( )` y `${ }` no respetaban la barra invertida**: `$(echo \)` cerraba la
sustitución cuando el paréntesis estaba escapado y no debía cerrar nada.
5. **Las clases `[…]` no entendían `\`** — y `*[\\\`\"\$]*` es literalmente el patrón del
`config.status` de libtool.
6. **Dentro de comillas dobles, el cuerpo de un backtick sigue entre comillas**, así que la barra
también se elimina ante `"`. Sin eso, dos casos de la tortura divergían en los dos sentidos.
7. **Y uno que el banco NO vio:** el `trap EXIT` **heredado** se disparaba en el subshell. POSIX
§2.12 manda resetearlo al entrar — es la mitad complementaria de brush#1396, y al revés. Lo
encontró la prueba de regresión del crate, porque ningún caso del banco lo probaba. **Los dos
oráculos hacen falta**, y el caso quedó agregado al fixture 08.
Las dos invariantes que el fichero de ejecución sostiene a propósito, las dos porque se midieron
rotas en el candidato ajeno: **el `trap EXIT` de un subshell corre** (brush#1396) y **`exec`
reemplaza el proceso** (si el shell forkea, arje supervisa al shell y no al daemon).
## Lo que queda, en orden
1. **Construir la receta en el lab** (zig cc, musl estático) y sellarla. Hasta ahí no hay artefacto.
2. **ADR** — escribir un shell propio y meterlo en `ATTEST_PATHS` es una decisión de frontera, y el
nombre `simi` también. Este documento la registra; el ADR la decide.
3. **Cablear `/bin/sh`**: `USERLAND_COMPONENTS` + el retiro del homónimo (`lib.rs:676`). Es frente C
y toca la raíz de confianza del arranque.
4. **Medir EJECUTANDO**, no sólo parseando: los `/init` en un arranque de QEMU
(`product-boot-test.sh`) y `takana-live-install.sh` en la imagen viva.
5. **La shell interactiva de `getty`**, que es el consumidor que falta entero.
6. **El sandbox de build sigue con busybox**, y con razón: eso es otro planeta y no está prometido.
+40
View File
@@ -0,0 +1,40 @@
# simi — el `/bin/sh` del producto. Código takana-PROPIO, no una adopción.
#
# Mismo patrón de receta que netup/hammerd/arje-zero: construye un crate del workspace a un commit
# fijado, deps vendoreadas para build hermético `--offline`, link estático con zig cc. Sólo depende
# de libc ⇒ vendor mínimo.
#
# Por qué propio y no adoptado (ver `docs/plan-botar-busybox.md`): el único candidato Rust maduro es
# brush, y se midió — 6,9 MB contra 1,17 del busybox entero, 4,1× la latencia de arranque, y dos
# divergencias POSIX medidas (reubeno/brush#1394 y #1396, las dos reportadas por nosotros). simi son
# 4448 líneas con CERO divergencias contra el ash de busybox en los 159 casos del banco diferencial.
#
# ⚠ Esta receta todavía NO se construyó: falta la corrida en el lab (zig cc, musl estático) y su
# sello. Lo medido hasta acá es el build nativo del hub (glibc dinámico, 499 KB) más las 47 pruebas
# del crate. No declarar `simi` en ningún perfil de `docs/state/targets.toml` antes de sellarla.
name = "simi"
version = "0.0.1"
license = "MIT"
[source]
# El propio takana. El commit es el identificador inmutable; subirlo cuando simi avance.
repo = "https://git.gioser.net/sergio/takana.git"
commit = "6809ef4be28f606aae856890a74667187ff75b05"
[build]
compiler = "zig-cc"
target = "x86_64-linux-musl"
link = "static"
# Split de la info de depuración, igual que hammerd. Entra en `hash_inputs`.
strip_debug = true
# Sólo el binario `simi` del workspace.
flags = ["-p", "simi"]
# Sin `[build.phases]`: igual que hammerd y netup, el BuildSys::Cargo del lab instala el binario del
# crate. **El symlink `sh` NO lo pone esta receta**, y eso es a propósito: quién gana como `/bin/sh`
# lo decide el ensamblado del producto (`USERLAND_COMPONENTS` en `crates/takana-bootstrap/src/lib.rs`,
# que hidrata el userland Rust DESPUÉS de busybox para que ensombrezca sus applets, y `lib.rs:676`,
# que retira el homónimo de los otros directorios). Ese cableado es frente C y es su propia unidad de
# trabajo: mete a simi en la raíz de confianza del arranque, y eso no se hace de paso.