docs(sdd33): el §4 ya se ejecuta de punta a punta — y el bug que sólo apareció al hidratar de verdad
Las dos mitades están: el hash de la clausura se publica cada ciclo y la imagen lo declara en su `/etc/os-release`. Lo que queda no es una pieza que falte, es que no hay imágenes publicadas con las que comparar — y eso queda dicho así, acotado. Lo que entra al documento además del resultado es el tropiezo, porque es el que se repetiría: la primera hidratación real dio DOS números distintos para el mismo perfil con los cinco hashes idénticos uno a uno. La causa era el prefijo `b3:` —el grafo lo guarda, `takana hash` lo da pelado— y la lección general es que **una función compartida que exige un formato convenido de palabra ya son dos implementaciones otra vez**: normaliza adentro o no sirve. Y lo encontró hidratar de verdad y comparar, no leer el código. Quedan también escritos los dos controles de reproducibilidad, que son los que impiden que alguien «mejore» esto metiendo la fecha: con `--version` aparece la etiqueta y el digest NO cambia, y dos hidrataciones seguidas dan un `os-release` byte a byte idéntico. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Semántico sayk'uy
Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.
1 qillqakuna · +39 −7
diff --git a/docs/33-versionado-y-relojes.md b/docs/33-versionado-y-relojes.md
index c1e1cbf7..ad3756c8 100644
--- a/docs/33-versionado-y-relojes.md
+++ b/docs/33-versionado-y-relojes.md
@@ -47,7 +47,7 @@ que el sitio empiece a mentir — que es lo que ya pasó con un sha256 pegado a
|---|---|---|---|
| **cosecha** | 30 min | trae del worker lo que se selló, siembra recetas, regenera el estado y commitea | **anda** (`scripts/farm/cosecha-cron.sh`) |
| **río arriba** | 1 día | pregunta a cada fuente si hay algo más nuevo que el pin, y deja un informe | **la pieza implementada hoy** |
-| **medios** | cuando el cierre cambie | rearma imágenes e instaladores y publica | **la MITAD anda**: el hash de la clausura se calcula y se publica cada ciclo (§4); falta que la imagen publicada declare de cuál salió |
+| **medios** | cuando el cierre cambie | rearma imágenes e instaladores y publica | **las dos piezas andan** (§4): el hash de la clausura se publica cada ciclo y la imagen lo declara en su `os-release`. Falta que haya imágenes publicadas con las que comparar |
### 2.1 Río arriba — `scripts/fuentes/upstream-vigia.py`
@@ -183,12 +183,44 @@ Los hashes se indexan por **(cola, nombre)**, porque `openssl` es una receta dis
en `incoming-kde`; comprobado que los cinco grafos no se contradicen (1.288 pares, 0 conflictos), y
si algún día lo hacen el informe lo grita en vez de elegir uno.
-⚠ **Y el paso que SIGUE faltando: la imagen publicada tiene que declarar de qué clausura salió.**
-Medido el 2026-10-10: **ninguna imagen declara nada** — no se escribe ni un `/etc/os-release`; los
-aciertos de `grep os-release` en el repo son todos LECTURAS para detectar el anfitrión. Y además no
-hay imágenes publicadas (el sitio no ofrece descargas; lo que se publica en `/srv/repo` son
-paquetes `.tkn`). Así que lo que está listo es el lado nuestro, y el día que se publique una imagen
-basta con que lleve su entrada de `cierres.json` al lado.
+✅ **Y la segunda mitad también, el mismo día: la imagen DECLARA de qué clausura salió.**
+`hydrate-profile.py` escribe `/etc/os-release` con `TAKANA_PERFIL`, `TAKANA_NODOS` y
+`TAKANA_CIERRE`. Antes **ninguna imagen declaraba nada** — no se escribía ni un `os-release`; los
+aciertos de `grep` en el repo eran todos lecturas para detectar el anfitrión. Va en ese guion y no
+en cada script de imagen por el mismo motivo que el bit de privilegio: por ahí pasan TODAS las
+imágenes de perfil, y un paso que hay que acordarse de invocar se olvida.
+
+```
+NAME="takana"
+ID=takana
+PRETTY_NAME="takana · metal-tigerlake"
+HOME_URL="https://takana-os.net"
+TAKANA_PERFIL=metal-tigerlake
+TAKANA_NODOS=5
+TAKANA_CIERRE=sha256:5fed4348345bc86498f54c2266d3a7118e646c1bc1685d3baa5f0f822631cac9
+```
+
+**El digest lo calcula la MISMA función que la vigía** (`digest_de`), sobre lo que el guion acaba de
+proyectar. Y eso no es celo: la primera hidratación real dio **dos números distintos** —la vigía
+`5fed4348…`, la imagen `17de5d7e…`— con los cinco hashes idénticos uno a uno. La causa era el
+prefijo: el grafo guarda `b3:7373…` y `takana hash` lo da pelado. Mismo hash, cadena distinta,
+digest distinto. ⇒ **la función normaliza adentro**, porque una función compartida que exige un
+formato convenido de palabra ya son dos implementaciones otra vez. Lo encontró hidratar de verdad y
+comparar, no leer el código.
+
+⚠ **Nada derivado del reloj entra en el fichero**, o la imagen deja de ser reproducible: una fecha
+cambiaría sus bytes cada mes sin que la clausura se mueva — la mentira que el §1 señala. El nombre
+legible es una ETIQUETA y entra sólo con `--version`. Comprobado en los dos sentidos: con
+`--version 2026.10` aparecen `VERSION`/`VERSION_ID` y **el digest no cambia**; y dos hidrataciones
+seguidas dan un `os-release` **byte a byte idéntico**.
+
+⚠ **Un rootfs con artefactos faltantes NO declara digest**: pone `TAKANA_CIERRE=incompleto` y
+cuántos faltan. Un número con pinta de identidad sobre una imagen que no es la clausura que dice
+ser es lo peor de los dos mundos, y el §4 compararía contra una mentira.
+
+⇒ **El §4 ya se puede ejecutar de punta a punta.** Lo que queda no es una pieza que falte: es que
+no hay imágenes publicadas todavía (el sitio no ofrece descargas; en `/srv/repo` hay paquetes
+`.tkn`). El día que se publique una, su `os-release` ya trae con qué compararla.
## 5. Lo que este documento NO decide