tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1182Shell 87Rust 63Markdown 541592 Sutichaykuna

⎇ 42325dd1ca2db1bc36613dbfeedd28d079155eab docs/24-arbol-de-fuentes.md Markdown · 269 siq'ikuna · 16.1 KB Ñawpa willay ch'uya

renombre: los instaladores pasan a `takana-*` — y el peligro no era el fichero, era el PROTOCOLO 008dd3925 · Sergio ·

1# SDD 24 — El árbol de fuentes: caché sellada + workspace efímero (v2)
2
3> **Resuelve ADR 0012.** Estado propuesto: ACEPTADO al cerrar F2.5.
4> **v2, 2026-08-28:** incorpora la verificación contra el código (§8): R1 e IF-1/IF-2 corregidos
5> por medición, segunda carrera nombrada, Q1 cerrada por precedente, tensión NG-4/Q3 resuelta.
6
7---
8
9## 0. El problema, medido — son DOS carreras
10
11`work/sources/…` cumple dos papeles que se contradicen bajo concurrencia:
12
131. **Caché direccionada por contenido**: dos builds de la misma dep resuelven al MISMO directorio.
14   (El nombre es `<nombre>-<sha16>` para tarballs y `<nombre>-<commit-completo>` para git —
15   `fetch.rs:234` / `fetch.rs:73`.)
162. **Workspace mutable de build**: patches, `cargo vendor`, `go mod vendor` (minutos, decenas de
17   GB), y los residuos que las recetas dejan EN EL ÁRBOL — `.zwrap/cc` lo escriben **131 recetas**
18   *(medido)*, casi todas de la cola KDE.
19
20Las dos carreras concretas:
21
22- **Carrera A — fetch vs build**: un `remove_dir_all` de re-materialización barre un árbol que un
23  `bwrap` está compilando. Medida a escala: al invalidar `libdrm`, ~93/205 recetas KDE murieron con
24  `/src/.zwrap/cc is not a full path…`.
25- **Carrera B — build vs build**: dos recetas DISTINTAS que comparten una dep transitiva no sellada
26  la construyen en el MISMO `sources/<dep>/output` ⇒ `Some other Meson process is already using
27  this build directory`. Documentada en `build-farm.sh:82-99`; hoy se concilia con un **reintento
28  serial** (Fase 1b) que existe sólo por esto. Un lock por árbol (opción A del ADR) NO la resuelve
29  sin serializar.
30
31Mitigación vigente: `flock` global + `JOBS=1` ⇒ la granja entera corre serializada.
32
33**La tesis:** los dos papeles se **separan por capa**, con el corte puesto para que lo caro
34(vendor) caiga del lado inmutable. Es el mismo mecanismo que el sandbox ya usa para las build-deps
35(`--overlay-src` RO apiladas), extendido a `/src`. Mata las dos carreras: A porque lo sellado es
36inmutable y la re-materialización es imposible (mismo `fuente_id` ⇒ ya existe); B porque cada
37constructor tiene su propio `output/` en su upper privado.
38
39---
40
41## 1. Diseño
42
43### 1.1 Las dos capas
44
45```
46MATERIALIZACIÓN (host, una vez por fuente_id, lock corto por-hash)
47  fetch (red) → patch → vendor (cargo/go)      ← YA ocurre todo en host hoy (lib.rs:224,248,258);
48  → sellar árbol en  work/sources-cas/<fuente_id>-<nombre>/     este SDD lo nombra como capa
49       fuente_id = blake3( source_id ⧺ Σ blake3(patch_i) ⧺ marca_vendor )
50       source_id cubre ambos modos: "git:<commit>" XOR "tarball:<sha256>"
51
52BUILD (sandbox, N en paralelo, cero locks)
53  bwrap monta /src = overlay( lower: fuente sellada, upper+work: work/ws/<uuid>/ )
54  configure → compile → install → seal artefacto → destruir ws/<uuid>
55```
56
57- **El vendoreo se paga una vez** y queda del lado inmutable: `marca_vendor` deriva de los
58  lockfiles que ya viven en el `source_id` ⇒ la fuente sellada es determinista dado el pin.
59- **`.zwrap` y todo residuo viven en el upper privado.** Las 131 recetas quedan estructuralmente
60  a salvo.
61- **La fase `patch` NO migra: ya corre en host** desde el día 1. Este SDD nombra como contrato lo
62  que hoy es de facto. La tabla de fases del **SDD 02 §4 está desactualizada** (dice "patch en
63  sandbox") y corregirla es entregable de F1.
64
65### 1.2 Gotchas de bwrap verificados (citar al implementar)
66
67- `--overlay` exige ≥1 `--overlay-src` previo, y el acumulador **se consume por operación**. Como
68  `sandbox.rs` ya emite `--overlay-src <rootfs> [deps…] --tmp-overlay /`, el grupo de `/src` va
69  **después** y no debe filtrar srcs al overlay del root. Orden de argumentos, no diseño.
70  **Verificado end-to-end** (§8.2): la composición real de los dos grupos funciona y no filtra.
71- El límite de una página del `lowerdir` (`sandbox.rs:347-357`, el pre-merge `cp -al`) **no
72  aplica**: `/src` es un mount aparte con su propia lowerdir.
73- `--overlay RWSRC WORKDIR DEST`: exige `upper` **y** `work` vacíos en el mismo filesystem.
74
75### 1.3 Por qué no las otras opciones del ADR
76
77- **(A)** lock por árbol durante todo el build: serializa el diamante qtbase y **no resuelve la
78  carrera B**.
79- **(B)** árbol privado por constructor: paga el vendor completo en cada build.
80- **(C)** caché inmutable + copia: paga la copia por build. Este diseño es (C) con overlay en vez
81  de copia y el vendor movido al lado sellado.
82
83---
84
85## 2. Reglas
86
87- **R1** — La fuente sellada es inmutable y direccionada: `fuente_id = blake3(source_id ⧺
88  Σ blake3(patch) ⧺ marca_vendor)`. Vive en `work/sources-cas/`. **La inmutabilidad la da el
89  mount, no los bits de modo**: a través del overlay es estructuralmente imposible escribir el
90  lower. Los modos del árbol se dejan **tal como los trae upstream** — sellarlos a `a-w` los
91  hereda el copy-up y **rompe el build** (medido: el sandbox corre uid 1001 sin
92  `CAP_DAC_OVERRIDE`; EACCES al reescribir un fichero existente y al crear dentro de un subdir;
93  el fallo es parcial-por-profundidad porque el modo del upper root gana en la raíz). Blindaje
94  contra procesos del host: proteger el **directorio contenedor** `sources-cas/`, nunca el árbol.
95  La detección de corrupción es `blake3_arbol` (R10).
96- **R2** — Sellado atómico: materializar en `tmp/`, escribir marker, `rename`. Jamás visible un
97  árbol parcial bajo su nombre final.
98- **R3** — Marker `.sellada` = `{fuente_id, blake3_arbol, ts}`. Directorio sin marker = no sellado
99  (lección del vacío-como-cache-hit: *un ausente falla ruidosamente; un vacío llega hasta el final
100  diciendo que todo fue bien* — CLAUDE.md regla 3).
101- **R4** — Nada escribe en una fuente sellada. El build la recibe sólo como lower del overlay.
102- **R5** — Workspace efímero por constructor: `work/ws/<uuid>/{upper,work}` (los dos directorios
103  que `--overlay` exige, mismo filesystem), destruido tras el seal. `--keep-ws` lo conserva.
104- **R6** — Lock **sólo** durante la materialización, por `fuente_id`
105  (`flock work/locks/<fuente_id>`). El segundo llegador espera y encuentra sellado. Los builds no
106  toman locks.
107- **R7** — Fallo o kill durante materialización ⇒ sólo queda `tmp/`; la corrida siguiente lo barre
108  y re-materializa. Nunca se sella parcial.
109- **R8** — La adopción **no mueve ningún `artifact_hash`**: `fuente_id` no entra en `hash_inputs`.
110  Verificación pre-merge: `takana hash --check` sobre el corpus, cero movidos.
111- **R9** — GC de `sources-cas` por alcanzabilidad desde recetas activas; lo no alcanzable es
112  podable (regenerable desde el mirror ADR 0013). Medir por contenido, nunca con `du`.
113- **R10** — Re-materializar un `fuente_id` existente debe reproducir el `blake3_arbol` del marker
114  o fallar ruidoso (detecta vendor no determinista o mirror envenenado).
115- **R11** — El `flock` global (CLAUDE.md regla 1) y el `JOBS=1` se retiran **sólo** tras F2.5
116  verde, en el mismo commit.
117
118## 3. Invariantes (mecánicamente verificables)
119
120- **IF-1** — Tras N builds concurrentes sobre la misma fuente, el `blake3_arbol` del árbol sellado
121  casa con el del marker. Verificable **sin fanotify** (que exige `CAP_SYS_ADMIN` y complica la
122  granja): hashear antes/después es más barato y más fuerte.
123- **IF-2** — Canario deliberado (estilo D9 de harkaq): un build de test **escribe** en `/src`
124  (debe FUNCIONAR, vía copy-up al upper) y el árbol sellado **sigue idéntico** por `blake3_arbol`.
125  Si el hash se movió, alguien montó el lower como bind RW. No se cree: se gana.
126- **IF-3** — `takana hash --check` corpus completo: hash vigente idéntico pre/post migración.
127- **IF-4** — `kill -9` en medio de la materialización ⇒ no existe marker; la corrida siguiente
128  sella limpio y `blake3_arbol` casa con una materialización sin interrupción.
129- **IF-5** — Artefacto sobre overlay ≡ bit-a-bit al del modelo viejo, **una receta por clase y
130  por build-system**: autotools-en-raíz (zlib), **meson y cmake (escriben en subdirs — la clase
131  que un spike sólo-zlib dejaría sin cubrir)**, cargo, go.
132- **IF-6** — Tras F3, `work/sources/` no existe, la **Fase 1b de `build-farm.sh` está borrada**, y
133  ningún script referencia el esquema viejo — verificado reconstruyendo, no con `grep` (en este
134  repo `grep` sobre ficheros con comentarios densos no es una medición).
135
136## 4. Fases
137
138- **F0 — gate residual**: el mecanismo overlay ya está verde (verificado 2026-08-28 con bwrap
139  0.11.2: lower legible, escritura al upper, lower intacto, y la composición con el
140  `--tmp-overlay /` del root — §8). Queda **sólo IF-5**, y con el corpus de clases completo — el
141  caso que muerde es meson/cmake en subdirs, no zlib.
142- **F1 — materialización**: `fetch.rs` produce fuentes selladas (R1–R3, R6–R7, R10). El build
143  sigue en el modelo viejo (puente). **Incluye corregir la tabla de fases del SDD 02 §4.**
144- **F2 — build sobre overlay**: `/src` = lower sellado + `ws/<uuid>/{upper,work}` (R4–R5),
145  respetando §1.2. Detrás de `SOURCES_CAS=1`, **inerte sin él**. IF-3 + IF-5.
146- **F2.5 — vertical slice**: worker real con `JOBS=4` drena una onda KDE que atraviesa el diamante
147  qtbase. Todo `✓ REPRODUCIBLE`, IF-1 e IF-2 corridos. **Gate del ADR:** 0012 pasa a ACEPTADO aquí
148  o el diseño vuelve.
149- **F3 — migración**: borrar `work/sources/`; **borrar la Fase 1b de `build-farm.sh`**
150  (entregable medible: la carrera B ya no existe); **reescribir — no re-apuntar — el watchdog de
151  disco de `farm-worker-loop.sh:43`** (queda más simple: lo sellado es inmutable, cada `ws/<uuid>`
152  tiene un solo dueño); actualizar `reconstruir-corpus.sh:206`; retirar flock global + `JOBS=1`
153  (R11); GC (R9) al latido.
154- **F4 — cosecha**: medir el speedup real de la campaña de reconstrucción con JOBS=N. El número
155  cierra el informe del ADR.
156
157## 5. No-objetivos
158
159- **NG-1** — No cambia el sandbox de build ni sus fases internas.
160- **NG-2** — No toca `hash_inputs` ni re-hashea nada (R8 lo verifica).
161- **NG-3** — No resuelve la red del vendor de Go en host (→ D1).
162- **NG-4** — `sources-cas` NO es procedencia ni parte del store: es caché **podable**. Q3 cita
163  `fuente_id` en el log de transparencia, y eso es compatible **porque la cita es a un hash cuya
164  materialización es regenerable desde el mirror (ADR 0013)**: podar no rompe la cita, la vuelve
165  un puntero frío. Lo que NUNCA se cita es una ruta de `sources-cas`.
166- **NG-5** — No introduce dependencia de reflink ni de overlayfs del host: el mecanismo es el
167  `--overlay` de bwrap ya en uso para deps.
168
169## 6. Deudas nombradas
170
171- **D1** — `go mod vendor` sigue en host con red. Acotado a materialización; R10 lo hace
172  detectable. Cierre futuro: materialización en sandbox con red controlada.
173- **D2** — Confirmar en F2.5 que los kernels propios de granja (SDD 22) montan el `--overlay` de
174  bwrap con lower en `sources-cas`. Fallback: copia completa — correcta, lenta, **ruidosa en el
175  log**, nunca silenciosa.
176- **D3** — `--keep-ws` acumula workspaces; podarlos en el GC de R9.
177- **D4** — El snapshot golden debe hornear `sources-cas` o los primeros builds pagan
178  materialización fría. **Choca con el test de `takana-bootstrap` (`lib.rs:2659`) que afirma que
179  `work/sources/` no viaja en el builder rootfs**: la decisión (¿el builder hereda `sources-cas`?)
180  se toma en F2.5 con el dato del slice, y si es sí, ese test se actualiza a propósito — no se
181  deja fallar por sorpresa.
182
183## 7. Preguntas — estado
184
185- **Q1 — CERRADA por precedente**: fetch+patch+vendor corren en host desde el día 1
186  (`lib.rs:224,248,258`). El riesgo ya se corre hoy; este SDD sólo lo pone bajo contrato (R10 lo
187  hace además detectable). No es una apuesta nueva.
188- **Q2 — abierta**: retención de `sources-cas` con vendors grandes: ¿tope GB con evicción LRU o
189  sólo alcanzabilidad? Decide el operador con el dato de F4.
190- **Q3 — DECIDIDA: sí** — `fuente_id` entra en `bootstrap.json` como campo opcional. Ver NG-4
191  para por qué no contradice la podabilidad.
192
193---
194
195*Cruces: ADR 0012 (resuelve) · ADR 0013 (regenerabilidad que sostiene NG-4/Q3) · SDD 02 §4
196(overlay-src de deps: el mecanismo reutilizado; tabla de fases a corregir en F1) · SDD 16 (patrón
197"inerte sin flag"; canario D9 → IF-2) · CLAUDE.md regla 3 (marker contra vacíos) · CLAUDE.md
198regla 1 (retiro del flock en R11) · `build-farm.sh` Fase 1b (carrera B; se borra en F3) ·
199`docs/BRIEFING-takana.md` §14.4 (los modos de fallo estructurales que R3 e IF-6 citan).*
200
201---
202
203# 8. Registro de evidencia — mediciones contra el código (2026-08-28)
204
205> Las correcciones que salieron de aquí **ya están incorporadas en el cuerpo de la v2**. Esta
206> sección queda como registro reproducible: qué se midió, con qué comando y con qué salida, para
207> que nadie tenga que volver a descubrirlo. Método del SDD 22: un diseño entrante se contesta con
208> evidencia, y si un dato de takana lo contradice, manda takana.
209
210## 8.1 El `chmod a-w` recursivo rompe el build (origen de R1)
211
212El sandbox corre como **uid 1001 sin `CAP_DAC_OVERRIDE`** (`id` dentro de bwrap lo confirma) y
213**overlayfs preserva el modo del lower al hacer copy-up**. Con el árbol sellado a `a-w`
214(dirs 0555, ficheros 0444), sobre patrones reales de fase:
215
216| patrón de build | resultado |
217|---|---|
218| `echo >> /mnt/configure` (reescribir fichero existente) | **FALLA** — `Permission denied` |
219| `echo y > /mnt/sub/nuevo.o` (crear dentro de un subdir del lower) | **FALLA** — `Permission denied` |
220| `sed -i` en la **raíz** del overlay | OK |
221| `mkdir /mnt/output` (patrón meson) en la **raíz** | OK |
222
223Las dos últimas pasan porque el upper root ya existe con modo 0755 y su modo gana sobre el del
224lower root ⇒ **el fallo es parcial y por profundidad**: funciona arriba y falla adentro. Un spike
225sobre `zlib` (autotools que configura en la raíz) habría pasado en verde y el diseño habría
226reventado después en meson/cmake/cargo. Es la forma del fallo *sellado ≠ arranca*. De ahí R1
227(inmutabilidad por mount, modos upstream) e IF-5 (una receta por build-system, no sólo zlib).
228
229## 8.2 La composición real con el sandbox actual funciona (origen de §1.2 y del gate de F0)
230
231Reproduciendo el orden de argumentos que `sandbox.rs` ya emite, más el grupo de `/src`:
232
233```sh
234bwrap --unshare-all \
235  --overlay-src .dev-fs/alpine        --tmp-overlay /            \
236  --overlay-src $S/lower  --overlay $S/upper $S/work  /src       \
237  --proc /proc --dev /dev -- /bin/sh -c '…'
238```
239```
240root = rootfs alpine: bin dev etc home lib media mnt musl256 opt proc root run sbi
241/src = fuente sellada: SOY-LA-FUENTE-SELLADA
242escritura en SUBDIR de /src: OK          ← /src/sub/output/build.ninja (el caso meson)
243raiz sigue escribible (tmp-overlay): OK
244lower intacto: marca.txt, sub/f.c        ← el árbol sellado no se tocó
245upper del /src: sub/output/build.ninja   ← el residuo quedó aislado
246```
247
248Cuatro hechos de una sola corrida: (1) el acumulador de `--overlay-src` **se consume por operación
249y no filtra** — el root sigue siendo el rootfs Alpine y `/src` la fuente sellada; (2) el
250`--tmp-overlay /` del root sigue funcionando junto al `/src`; (3) con modos upstream, **escribir en
251un subdirectorio de `/src` funciona** — exactamente el caso que §8.1 rompía; (4) el lower queda
252byte-intacto y el residuo aislado en el upper.
253
254⇒ El gate de *mecanismo* de F0 está verde. Lo que queda de F0 es IF-5 (bit-a-bit real).
255
256## 8.3 Referencias de código verificadas
257
258| Afirmación del SDD | Dónde | Estado |
259|---|---|---|
260| `<nombre>-<sha16>` (tarball) / `<nombre>-<commit>` (git) | `fetch.rs:234` / `fetch.rs:73` | ✅ |
261| fetch+patch+vendor corren en host, antes de bwrap | `lib.rs:224,248,258` | ✅ (cierra Q1) |
262| Límite de página del `lowerdir` y pre-merge `cp -al` | `sandbox.rs:347-357` | ✅ |
263| Carrera B y su reintento serial (Fase 1b) | `build-farm.sh:82-99` | ✅ |
264| Watchdog acoplado al esquema de rutas | `farm-worker-loop.sh:43` | ✅ |
265| Purga del árbol viejo | `reconstruir-corpus.sh:206` | ✅ |
266| `work/sources/` no viaja en el builder rootfs (test) | `takana-bootstrap/src/lib.rs:2659` | ✅ (origen de D4) |
267| `.zwrap` escrito en el árbol de fuentes | 131 recetas *(medido)* | ✅ |
268| `--overlay RWSRC WORKDIR DEST` disponible | bubblewrap 0.11.2 | ✅ |
269| La tabla de fases dice "patch en sandbox" | `docs/02-build-lab.md` §4 | ⚠️ desactualizada — F1 |

24-arbol-de-fuentes.md

SDD 24 — El árbol de fuentes: caché sellada + workspace efímero (v2)

Resuelve ADR 0012. Estado propuesto: ACEPTADO al cerrar F2.5. v2, 2026-08-28: incorpora la verificación contra el código (§8): R1 e IF-1/IF-2 corregidos por medición, segunda carrera nombrada, Q1 cerrada por precedente, tensión NG-4/Q3 resuelta.


0. El problema, medido — son DOS carreras

work/sources/… cumple dos papeles que se contradicen bajo concurrencia:

  1. Caché direccionada por contenido: dos builds de la misma dep resuelven al MISMO directorio. (El nombre es <nombre>-<sha16> para tarballs y <nombre>-<commit-completo> para git — fetch.rs:234 / fetch.rs:73.)
  2. Workspace mutable de build: patches, cargo vendor, go mod vendor (minutos, decenas de GB), y los residuos que las recetas dejan EN EL ÁRBOL — .zwrap/cc lo escriben 131 recetas (medido), casi todas de la cola KDE.

Las dos carreras concretas:

Mitigación vigente: flock global + JOBS=1 ⇒ la granja entera corre serializada.

La tesis: los dos papeles se separan por capa, con el corte puesto para que lo caro (vendor) caiga del lado inmutable. Es el mismo mecanismo que el sandbox ya usa para las build-deps (--overlay-src RO apiladas), extendido a /src. Mata las dos carreras: A porque lo sellado es inmutable y la re-materialización es imposible (mismo fuente_id ⇒ ya existe); B porque cada constructor tiene su propio output/ en su upper privado.


1. Diseño

1.1 Las dos capas

MATERIALIZACIÓN (host, una vez por fuente_id, lock corto por-hash)
  fetch (red) → patch → vendor (cargo/go)      ← YA ocurre todo en host hoy (lib.rs:224,248,258);
  → sellar árbol en  work/sources-cas/<fuente_id>-<nombre>/     este SDD lo nombra como capa
       fuente_id = blake3( source_id ⧺ Σ blake3(patch_i) ⧺ marca_vendor )
       source_id cubre ambos modos: "git:<commit>" XOR "tarball:<sha256>"

BUILD (sandbox, N en paralelo, cero locks)
  bwrap monta /src = overlay( lower: fuente sellada, upper+work: work/ws/<uuid>/ )
  configure → compile → install → seal artefacto → destruir ws/<uuid>

1.2 Gotchas de bwrap verificados (citar al implementar)

1.3 Por qué no las otras opciones del ADR


2. Reglas

3. Invariantes (mecánicamente verificables)

4. Fases

5. No-objetivos

6. Deudas nombradas

7. Preguntas — estado


Cruces: ADR 0012 (resuelve) · ADR 0013 (regenerabilidad que sostiene NG-4/Q3) · SDD 02 §4 (overlay-src de deps: el mecanismo reutilizado; tabla de fases a corregir en F1) · SDD 16 (patrón "inerte sin flag"; canario D9 → IF-2) · CLAUDE.md regla 3 (marker contra vacíos) · CLAUDE.md regla 1 (retiro del flock en R11) · build-farm.sh Fase 1b (carrera B; se borra en F3) · docs/BRIEFING-takana.md §14.4 (los modos de fallo estructurales que R3 e IF-6 citan).


8. Registro de evidencia — mediciones contra el código (2026-08-28)

Las correcciones que salieron de aquí ya están incorporadas en el cuerpo de la v2. Esta sección queda como registro reproducible: qué se midió, con qué comando y con qué salida, para que nadie tenga que volver a descubrirlo. Método del SDD 22: un diseño entrante se contesta con evidencia, y si un dato de takana lo contradice, manda takana.

8.1 El chmod a-w recursivo rompe el build (origen de R1)

El sandbox corre como uid 1001 sin CAP_DAC_OVERRIDE (id dentro de bwrap lo confirma) y overlayfs preserva el modo del lower al hacer copy-up. Con el árbol sellado a a-w (dirs 0555, ficheros 0444), sobre patrones reales de fase:

patrón de buildresultado
echo >> /mnt/configure (reescribir fichero existente)FALLA — Permission denied
echo y > /mnt/sub/nuevo.o (crear dentro de un subdir del lower)FALLA — Permission denied
sed -i en la raíz del overlayOK
mkdir /mnt/output (patrón meson) en la raízOK

Las dos últimas pasan porque el upper root ya existe con modo 0755 y su modo gana sobre el del lower root ⇒ el fallo es parcial y por profundidad: funciona arriba y falla adentro. Un spike sobre zlib (autotools que configura en la raíz) habría pasado en verde y el diseño habría reventado después en meson/cmake/cargo. Es la forma del fallo sellado ≠ arranca. De ahí R1 (inmutabilidad por mount, modos upstream) e IF-5 (una receta por build-system, no sólo zlib).

8.2 La composición real con el sandbox actual funciona (origen de §1.2 y del gate de F0)

Reproduciendo el orden de argumentos que sandbox.rs ya emite, más el grupo de /src:

bwrap --unshare-all \
  --overlay-src .dev-fs/alpine        --tmp-overlay /            \
  --overlay-src $S/lower  --overlay $S/upper $S/work  /src       \
  --proc /proc --dev /dev -- /bin/sh -c '…'
root = rootfs alpine: bin dev etc home lib media mnt musl256 opt proc root run sbi
/src = fuente sellada: SOY-LA-FUENTE-SELLADA
escritura en SUBDIR de /src: OK          ← /src/sub/output/build.ninja (el caso meson)
raiz sigue escribible (tmp-overlay): OK
lower intacto: marca.txt, sub/f.c        ← el árbol sellado no se tocó
upper del /src: sub/output/build.ninja   ← el residuo quedó aislado

Cuatro hechos de una sola corrida: (1) el acumulador de --overlay-src se consume por operación y no filtra — el root sigue siendo el rootfs Alpine y /src la fuente sellada; (2) el --tmp-overlay / del root sigue funcionando junto al /src; (3) con modos upstream, escribir en un subdirectorio de /src funciona — exactamente el caso que §8.1 rompía; (4) el lower queda byte-intacto y el residuo aislado en el upper.

⇒ El gate de mecanismo de F0 está verde. Lo que queda de F0 es IF-5 (bit-a-bit real).

8.3 Referencias de código verificadas

Afirmación del SDDDóndeEstado
<nombre>-<sha16> (tarball) / <nombre>-<commit> (git)fetch.rs:234 / fetch.rs:73✅
fetch+patch+vendor corren en host, antes de bwraplib.rs:224,248,258✅ (cierra Q1)
Límite de página del lowerdir y pre-merge cp -alsandbox.rs:347-357✅
Carrera B y su reintento serial (Fase 1b)build-farm.sh:82-99✅
Watchdog acoplado al esquema de rutasfarm-worker-loop.sh:43✅
Purga del árbol viejoreconstruir-corpus.sh:206✅
work/sources/ no viaja en el builder rootfs (test)takana-bootstrap/src/lib.rs:2659✅ (origen de D4)
.zwrap escrito en el árbol de fuentes131 recetas (medido)✅
--overlay RWSRC WORKDIR DEST disponiblebubblewrap 0.11.2✅
La tabla de fases dice "patch en sandbox"docs/02-build-lab.md §4⚠️ desactualizada — F1