gioser

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 1572Shell 203Rust 109Markdown 993728 Definitions

⎇ main docs/adr/0019-dueno-del-formato-card.md Markdown · 127 lines · 8.2 KB History raw

ADR 0019: el dueño del formato Card — y takana lo escribe a mano en cinco sitios 447e15142 · Sergio ·

1# ADR 0019 — El dueño del formato Card: tawasuyu lo DEFINE, takana lo GENERA
2
3- **Estado:** PROPUESTO. La dirección se decide acá; **cuándo se paga la adopción** queda abierto y
4  depende de una medición concreta (§Precondiciones, el hash de Stage 1).
5- **Fecha:** 2026-09-14
6- **Frontera:** en takana, `crates/takana-bootstrap/src/lib.rs` (`STAGE1_SEED_CARD`,
7  `SSHD_SERVICE_CARD`), `scripts/mudanza/formatos/arje.py`, `scripts/mudanza/formatos/{systemd,openrc}.py`,
8  `scripts/mudanza/declarar.py`. En tawasuyu, `shared/card/card-core`,
9  `03_ukupacha/arje/arje-card{,-builder}`, `03_ukupacha/arje/init/arje-absorb`.
10- **Relacionado:** [SDD 29](../29-mudanza.md) (la mudanza), [SDD 30](../30-servicios-de-paquete.md)
11  (qué arranca), [ADR 0006](0006-pinned-commits.md) (dependencias por commit).
12
13## Contexto
14
15La mudanza de gioser (SDD 29) produce tarjetas de arje: es como un servicio ajeno —systemd, OpenRC,
16o un proceso que corre y nadie declaró— se convierte en algo que `arje-zero` sabe levantar. El
17producto instalado también las emite: su `seed.card.json` y la card de `sshd` salen de
18`takana-bootstrap`.
19
20Nunca se escribió **quién es dueño de ese formato**, y por omisión hoy son dos repos. Esto ya se pagó
21un piso más abajo: dentro de takana había **dos emisores** del mismo formato (`declarar.py` y
22`formatos/arje.py`) y **cada uno tenía un campo distinto mal en la raíz** — uno mandaba `provides`
23vacío, el otro `supervision: Restart` sobre una card `Virtual`. Ninguno de los dos coincidía con la
24semilla real del producto. Se consolidó en un solo emisor. Este ADR es el mismo problema un piso más
25arriba: **el segundo emisor está en el otro repo**.
26
27## Lo medido (2026-09-14)
28
29| qué | resultado |
30|---|---|
31| el tipo canónico existe, y está en tawasuyu | `shared/card/card-core::Card`. `arje-card` se describe a sí mismo: *«Alias histórico de card-core. Re-exporta tipos legacy (EntityCard ≡ Card)»* |
32| …y **valida** su versión | `pub const CARD_SCHEMA_VERSION: u16 = 1` + `if self.schema_version != CARD_SCHEMA_VERSION { … }` |
33| emisores de Card **dentro de takana** | **2**, en dos lenguajes: `takana-bootstrap` (JSON crudo en constantes `r#"…"#`) y `scripts/mudanza/formatos/arje.py` |
34| dependencias de `card-core` / `arje-card` en `crates/*/Cargo.toml` | **0** |
35| sitios donde takana escribe `"schema_version": 1` **a mano** | **5** |
36| `arje-absorb` (tawasuyu) | usa `card_core::Card` directamente y ya trae lectores `systemd.rs`, `openrc.rs`, `runit.rs`, `dinit.rs`, `sysvinit.rs` |
37| `scripts/mudanza/formatos/` (takana) | reimplementa en Python **dos de esos cinco** (`systemd.py`, `openrc.py`) y aporta uno que allá no existe: `proc.py` |
38| `card` como crate | **hoja**: sin deps internas, sólo crates.io ⇒ depender de él es barato |
39| `STAGE1_SEED_CARD` | **entra en `inputs` del hash** del bootstrap (`lib.rs:332`) |
40| el repo `card` | está entre los **26 que sólo existen en gioser**, la máquina que se borra |
41
42El dato que decide: **card-core valida la versión de esquema y takana la escribe a mano en cinco
43sitios.** El día que `CARD_SCHEMA_VERSION` pase a 2, arje rechazará las tarjetas de takana — y el
44fallo no aparece en un test: aparece **en el arranque de una caja recién instalada**, que es el peor
45sitio y el más tarde posible. Hoy coinciden por suerte, no por construcción.
46
47## La pregunta que este ADR contesta
48
49«Integrar tawasuyu» admite dos lecturas, y la respuesta **no es la misma para las dos**:
50
511. **usar piezas de tawasuyu dentro de esta herramienta**, o
522. **que la herramienta viva en tawasuyu y desde acá se invoque**.
53
54> **Para el FORMATO, (1): se adopta el tipo de tawasuyu.
55> Para la HERRAMIENTA, ninguna de las dos: la mudanza es de takana y se queda acá.**
56
57La mudanza habla de recetas, perfiles, store, hidratación y ArtifactHash: su vocabulario es el de
58takana, y mudarla a tawasuyu la dejaría hablando de cosas que ese repo no tiene. Lo que cruza la
59frontera es **el tipo**, no la herramienta.
60
61## Decisión
62
63**1. tawasuyu DEFINE el formato; takana lo GENERA.** `card-core` es la única definición. No se copia,
64no se reimplementa y no se documenta en paralelo. Una copia del tipo es la misma duplicación que este
65ADR cierra, sólo que con la palabra «vendor» delante.
66
67**2. Un solo emisor en takana, y que serialice con el TIPO, no con `format!`.** Un envoltorio fino
68(`takana-card`) sobre `card-core`, usado por `takana-bootstrap` **y** por la mudanza. El criterio de
69aceptación es negativo y comprobable: **cero literales `"schema_version"` en el árbol de takana**.
70
71**3. La mudanza no se muda.** Sigue en `scripts/mudanza/`, y su reescritura a subcomando (`takana
72migrate`) es una decisión de otro ADR — independiente de ésta.
73
74**4. Los lectores de init ajeno se retiran de takana, no se mejoran.** `arje-absorb` ya lee
75systemd/OpenRC/runit/dinit/sysvinit contra el tipo canónico; `formatos/systemd.py` y `openrc.py` son
76una segunda implementación de dos de ellos. Se retiran **cuando** absorb exponga su resultado de
77forma consumible por un tercero; hasta entonces conviven **con una prueba que compare las dos salidas
78sobre la misma entrada y falle si divergen** — porque dos traductores que nadie compara divergen sin
79que nada falle, que es exactamente cómo empezó esto.
80
81**5. Lo que takana aporta va HACIA tawasuyu, no en paralelo.** El lector `proc` —absorber del proceso
82VIVO y no de la declaración— es de takana y no existe allá. Y hace falta: en gioser `rc-status` decía
83`stopped` de cinco servicios **vivos**, así que absorb solo produciría una Semilla perfecta que no
84levanta el servidor. Se ofrece como aportación a `arje-absorb`; si tawasuyu no lo toma, se queda acá
85como lector propio **del tipo de allá**, que sigue siendo una sola definición.
86
87**6. La dirección de dependencia es única: takana → tawasuyu**, por commit pineado (ADR 0006).
88tawasuyu no depende de takana. No es una preferencia: **21 recetas del corpus ya construyen
89componentes de tawasuyu** (`arje-zero`, `mirada-*`, `netup`, `arje-absorb`, `arje-installer`,
90`cosmos-cli`, …). La dependencia inversa cerraría un ciclo entre dos repos que ya se necesitan en un
91sentido.
92
93## Precondiciones que no se saltan
94
95**a. `card` tiene que tener copia fuera de gioser antes de que takana dependa de él.** Es uno de los
9626 repos sin espejo. Hacer que el bootstrap de la distro dependa en tiempo de compilación de un repo
97que **sólo existe en la máquina que estamos por borrar** convierte un problema conocido en un
98bloqueo de arranque. Es el paso 1 del plan de mudanza, y es precondición de esta decisión.
99
100**b. Adoptar el tipo puede mover el baseline del selfhost, y hay que MEDIRLO antes.**
101`STAGE1_SEED_CARD` entra en los `inputs` del hash del bootstrap: si al serializar con `card-core` el
102JSON sale con otro orden de claves, otro espaciado u otro campo por defecto, **el hash de Stage 1 se
103mueve**. Se comprueba con `takana hash` antes y después; si se mueve, la adopción en `takana-bootstrap`
104es una decisión propia (con el selfhost delante) y **no se hace de paso**. La mudanza no tiene ese
105problema: sus cards no entran en ningún hash, así que puede adoptar el tipo primero.
106
107## Consecuencias
108
109**Se gana:** cuando el esquema suba, el fallo pasa de *«una caja recién instalada no arranca»* a
110*«no compila»*. Es el mismo cambio de lugar que buscan la regla 3 del `CLAUDE.md` y el ADR 0012: que
111lo que se rompe, se rompa ruidosamente y temprano.
112
113**Cuesta:** takana suma una dependencia de compilación sobre un repo privado. Es barata en lo técnico
114(`card` es hoja, sólo crates.io) y cara en lo operativo mientras la precondición (a) siga sin cumplir.
115
116**Rechazado — copiar el tipo a takana (vendor):** quita la dependencia y deja exactamente el problema
117que este ADR existe para cerrar, con la divergencia además invisible.
118
119**Rechazado — mover la mudanza a tawasuyu:** el vocabulario de la mudanza es el de takana.
120
121## Lo que NO decide este ADR
122
123- Dónde vive el `[[sitio]]` (la utilidad de vhosts y el versionado de datos por frentes). Es un ADR
124  propio, y la respuesta puede ser distinta que ésta.
125- Si `arje-absorb` incorpora el lector `proc`: es decisión de tawasuyu, no de este repo.
126- Cuándo se paga la adopción en `takana-bootstrap` — depende de la medición (b).
127- Si la mudanza se reescribe como subcomando de takana.

0019-dueno-del-formato-card.md

ADR 0019 — El dueño del formato Card: tawasuyu lo DEFINE, takana lo GENERA

Contexto

La mudanza de gioser (SDD 29) produce tarjetas de arje: es como un servicio ajeno —systemd, OpenRC, o un proceso que corre y nadie declaró— se convierte en algo que arje-zero sabe levantar. El producto instalado también las emite: su seed.card.json y la card de sshd salen de takana-bootstrap.

Nunca se escribió quién es dueño de ese formato, y por omisión hoy son dos repos. Esto ya se pagó un piso más abajo: dentro de takana había dos emisores del mismo formato (declarar.py y formatos/arje.py) y cada uno tenía un campo distinto mal en la raíz — uno mandaba provides vacío, el otro supervision: Restart sobre una card Virtual. Ninguno de los dos coincidía con la semilla real del producto. Se consolidó en un solo emisor. Este ADR es el mismo problema un piso más arriba: el segundo emisor está en el otro repo.

Lo medido (2026-09-14)

quéresultado
el tipo canónico existe, y está en tawasuyushared/card/card-core::Card. arje-card se describe a sí mismo: «Alias histórico de card-core. Re-exporta tipos legacy (EntityCard ≡ Card)»
…y valida su versiónpub const CARD_SCHEMA_VERSION: u16 = 1 + if self.schema_version != CARD_SCHEMA_VERSION { … }
emisores de Card dentro de takana2, en dos lenguajes: takana-bootstrap (JSON crudo en constantes r#"…"#) y scripts/mudanza/formatos/arje.py
dependencias de card-core / arje-card en crates/*/Cargo.toml0
sitios donde takana escribe "schema_version": 1 a mano5
arje-absorb (tawasuyu)usa card_core::Card directamente y ya trae lectores systemd.rs, openrc.rs, runit.rs, dinit.rs, sysvinit.rs
scripts/mudanza/formatos/ (takana)reimplementa en Python dos de esos cinco (systemd.py, openrc.py) y aporta uno que allá no existe: proc.py
card como cratehoja: sin deps internas, sólo crates.io ⇒ depender de él es barato
STAGE1_SEED_CARDentra en inputs del hash del bootstrap (lib.rs:332)
el repo cardestá entre los 26 que sólo existen en gioser, la máquina que se borra

El dato que decide: card-core valida la versión de esquema y takana la escribe a mano en cinco sitios. El día que CARD_SCHEMA_VERSION pase a 2, arje rechazará las tarjetas de takana — y el fallo no aparece en un test: aparece en el arranque de una caja recién instalada, que es el peor sitio y el más tarde posible. Hoy coinciden por suerte, no por construcción.

La pregunta que este ADR contesta

«Integrar tawasuyu» admite dos lecturas, y la respuesta no es la misma para las dos:

  1. usar piezas de tawasuyu dentro de esta herramienta, o
  2. que la herramienta viva en tawasuyu y desde acá se invoque.

Para el FORMATO, (1): se adopta el tipo de tawasuyu. Para la HERRAMIENTA, ninguna de las dos: la mudanza es de takana y se queda acá.

La mudanza habla de recetas, perfiles, store, hidratación y ArtifactHash: su vocabulario es el de takana, y mudarla a tawasuyu la dejaría hablando de cosas que ese repo no tiene. Lo que cruza la frontera es el tipo, no la herramienta.

Decisión

1. tawasuyu DEFINE el formato; takana lo GENERA. card-core es la única definición. No se copia, no se reimplementa y no se documenta en paralelo. Una copia del tipo es la misma duplicación que este ADR cierra, sólo que con la palabra «vendor» delante.

2. Un solo emisor en takana, y que serialice con el TIPO, no con format!. Un envoltorio fino (takana-card) sobre card-core, usado por takana-bootstrap y por la mudanza. El criterio de aceptación es negativo y comprobable: cero literales "schema_version" en el árbol de takana.

3. La mudanza no se muda. Sigue en scripts/mudanza/, y su reescritura a subcomando (takana migrate) es una decisión de otro ADR — independiente de ésta.

4. Los lectores de init ajeno se retiran de takana, no se mejoran. arje-absorb ya lee systemd/OpenRC/runit/dinit/sysvinit contra el tipo canónico; formatos/systemd.py y openrc.py son una segunda implementación de dos de ellos. Se retiran cuando absorb exponga su resultado de forma consumible por un tercero; hasta entonces conviven con una prueba que compare las dos salidas sobre la misma entrada y falle si divergen — porque dos traductores que nadie compara divergen sin que nada falle, que es exactamente cómo empezó esto.

5. Lo que takana aporta va HACIA tawasuyu, no en paralelo. El lector proc —absorber del proceso VIVO y no de la declaración— es de takana y no existe allá. Y hace falta: en gioser rc-status decía stopped de cinco servicios vivos, así que absorb solo produciría una Semilla perfecta que no levanta el servidor. Se ofrece como aportación a arje-absorb; si tawasuyu no lo toma, se queda acá como lector propio del tipo de allá, que sigue siendo una sola definición.

6. La dirección de dependencia es única: takana → tawasuyu, por commit pineado (ADR 0006). tawasuyu no depende de takana. No es una preferencia: 21 recetas del corpus ya construyen componentes de tawasuyu (arje-zero, mirada-*, netup, arje-absorb, arje-installer, cosmos-cli, …). La dependencia inversa cerraría un ciclo entre dos repos que ya se necesitan en un sentido.

Precondiciones que no se saltan

a. card tiene que tener copia fuera de gioser antes de que takana dependa de él. Es uno de los 26 repos sin espejo. Hacer que el bootstrap de la distro dependa en tiempo de compilación de un repo que sólo existe en la máquina que estamos por borrar convierte un problema conocido en un bloqueo de arranque. Es el paso 1 del plan de mudanza, y es precondición de esta decisión.

b. Adoptar el tipo puede mover el baseline del selfhost, y hay que MEDIRLO antes. STAGE1_SEED_CARD entra en los inputs del hash del bootstrap: si al serializar con card-core el JSON sale con otro orden de claves, otro espaciado u otro campo por defecto, el hash de Stage 1 se mueve. Se comprueba con takana hash antes y después; si se mueve, la adopción en takana-bootstrap es una decisión propia (con el selfhost delante) y no se hace de paso. La mudanza no tiene ese problema: sus cards no entran en ningún hash, así que puede adoptar el tipo primero.

Consecuencias

Se gana: cuando el esquema suba, el fallo pasa de «una caja recién instalada no arranca» a «no compila». Es el mismo cambio de lugar que buscan la regla 3 del CLAUDE.md y el ADR 0012: que lo que se rompe, se rompa ruidosamente y temprano.

Cuesta: takana suma una dependencia de compilación sobre un repo privado. Es barata en lo técnico (card es hoja, sólo crates.io) y cara en lo operativo mientras la precondición (a) siga sin cumplir.

Rechazado — copiar el tipo a takana (vendor): quita la dependencia y deja exactamente el problema que este ADR existe para cerrar, con la divergencia además invisible.

Rechazado — mover la mudanza a tawasuyu: el vocabulario de la mudanza es el de takana.

Lo que NO decide este ADR