768 lines
60 KiB
Markdown
768 lines
60 KiB
Markdown
el tema con cargo xtask
|
||
tal y como lo tenemos @.coder/2026-05-26-deps-sync.info.md
|
||
es ¿ cómo actualizamos los crated a nuevas versiones sin romper el sync de dependencias ?
|
||
⎿ Read .coder/2026-05-26-deps-sync.info.md (232 lines)
|
||
|
||
⏺ Voy a verificar el estado actual del sistema xtask y la registry antes de responder.
|
||
|
||
Searched for 1 pattern, listed 1 directory (ctrl+o to expand)
|
||
|
||
⏺ ★ Insight ─────────────────────────────────────
|
||
- El sistema invierte el flujo habitual de Cargo: en vez de cargo update actualizando Cargo.lock, aquí los bumps de versión son
|
||
cambios source-controlled sobre registry/dependencies.toml, y el xtask los propaga determinísticamente a cada Cargo.toml.
|
||
- El overlay (rustelo-deps-overlay.toml) rechaza por diseño los overrides de versión — esto convierte cada bump en una decisión
|
||
global y atómica, no en una negociación per-impl.
|
||
- El --check reporta drift por conteo de líneas, no diff semántico: es un gate de "estado convergido", no un explicador del cambio.
|
||
─────────────────────────────────────────────────
|
||
|
||
El flujo para actualizar versiones
|
||
|
||
El sistema no impide actualizar — al contrario, lo canaliza por un único punto. La pregunta correcta no es "cómo actualizo sin
|
||
romper el sync", sino "el sync convierte el bump en una decisión global; ¿estoy listo a propagar?".
|
||
|
||
Workflow concreto
|
||
|
||
1. Editar registry/dependencies.toml ← única fuente de verdad
|
||
[deps.NAME]
|
||
version = "X.Y.Z"
|
||
|
||
2. cargo xtask sync-deps ← propaga a rustelo/Cargo.toml
|
||
3. cargo xtask sync-deps --target ../jpl-website
|
||
(y a cada impl conocida)
|
||
|
||
4. cargo build / cargo test ← validar en CADA workspace
|
||
(rustelo + cada impl)
|
||
|
||
5. Si rompe en impl:
|
||
a) FORWARD → adaptar código del impl al nuevo API
|
||
b) REGRESS → bajar la versión en registry/dependencies.toml
|
||
y volver a sincronizar
|
||
|
||
Lo que no se puede hacer (y es intencional)
|
||
|
||
┌─────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────┐
|
||
│ Intento │ Por qué falla │
|
||
├─────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────┤
|
||
│ [overlay.sqlx] version = "..." en impl │ El validador rechaza override de versión (línea 90-93 del │
|
||
│ │ .info) │
|
||
├─────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────┤
|
||
│ Editar a mano dentro del bloque # >>> │ El siguiente --check lo detecta como drift y sync-deps lo │
|
||
│ rustelo-sync:workspace-deps │ sobreescribe │
|
||
├─────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────┤
|
||
│ Per-impl version pin │ "Per-impl version overrides are intentionally NOT supported" │
|
||
│ │ (línea 189) │
|
||
└─────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────┘
|
||
|
||
Esto es el corazón del invariante de unificación cross-workspace: si dos workspaces pueden tener versiones distintas, no hay
|
||
garantía de unificación; el sistema entero pierde su razón de existir.
|
||
|
||
Qué hacer con bumps que rompen impl
|
||
|
||
El caso ya documentado: sqlx 0.8 → 0.9 en jpl-website tras un sync.
|
||
|
||
★ Insight ─────────────────────────────────────
|
||
- La "convergencia forzada" no es un bug sino el contrato. El xtask te empuja a tomar la decisión arquitectónica: el registry
|
||
refleja lo que el framework dice que es canónico, y los impls deben alinearse o el registry retrocede.
|
||
- La opción "Regress registry" es legítima pero costosa: significa que el framework no puede avanzar hasta que el impl bloqueante
|
||
se adapte. Útil como freno cuando hay deadline en impl.
|
||
- No existe punto medio. Si necesitas el punto medio, el sistema te está diciendo que el registry está mal-versionado:
|
||
probablemente la nueva versión no debió subir al registry todavía.
|
||
─────────────────────────────────────────────────
|
||
|
||
Patrón recomendado para bumps frágiles (leptos, axum, sqlx)
|
||
|
||
1. Crear branch en rustelo: bump/leptos-0.X
|
||
2. Editar registry/dependencies.toml
|
||
3. cargo xtask sync-deps # rustelo
|
||
4. cargo build --workspace # ¿compila rustelo?
|
||
5. cargo xtask sync-deps --target ../jpl-website
|
||
6. cd ../jpl-website && cargo build # ¿compila impl?
|
||
7. Si 4 o 6 falla:
|
||
- Fix forward → commit a rustelo (si es framework code)
|
||
→ commit a impl (si es código de impl)
|
||
- O regress registry y volver al paso 2
|
||
8. Commit registry + Cargo.toml sincronizados JUNTOS (atomicidad de bump)
|
||
|
||
El paso 8 es importante: el pre-commit hook (rustelo-deps-sync con --check) bloquea cualquier commit donde
|
||
registry/dependencies.toml esté desincronizado de Cargo.toml. Esto fuerza que el bump y su propagación sean un solo commit, no dos.
|
||
|
||
Gap actual del sistema (línea 217 del .info)
|
||
|
||
▎ Auto-discovery of impls via rustelo.manifest.toml [sync] section (currently impls are reached via explicit --target).
|
||
|
||
Hoy debes recordar a mano qué impls existen y pasarles --target uno a uno. Esto es un riesgo operacional real: si añades un impl
|
||
nuevo y olvidas sincronizarlo, drifta silenciosamente hasta que alguien corra el hook en ese repo. Si quieres, esto sería el
|
||
siguiente xtask útil de implementar (sync-deps --all leyendo de un manifiesto).
|
||
|
||
¿Quieres que profundice en alguno de los flujos — por ejemplo el patrón forward vs regress para un bump concreto que tengas
|
||
pendiente — o que mire el código del xtask para confirmar el comportamiento de algún caso edge?
|
||
|
||
✻ Churned for 55s
|
||
|
||
❯ zed nos muestra versiones actualizables sobre Cargo.toml pero es sobreescrito por xtask
|
||
|
||
⏺ ★ Insight ─────────────────────────────────────
|
||
- Zed (vía rust-analyzer + crates lsp) detecta updates leyendo tablas [dependencies] o [workspace.dependencies] en archivos
|
||
Cargo.toml. No reconoce registry/dependencies.toml porque no es un manifiesto Cargo válido — es un schema ad-hoc del xtask.
|
||
- El conflicto es estructural: el sistema xtask deliberadamente coloca la fuente de verdad fuera del sitio donde las herramientas
|
||
Cargo-native esperan encontrarla. Esto es bueno para el invariante cross-workspace pero rompe el flujo editor-driven que ofrecen
|
||
Zed/rust-analyzer/crates-tooling.
|
||
─────────────────────────────────────────────────
|
||
|
||
Esto es una fricción real, no un mal uso. Tienes tres caminos. Te los presento con los tradeoffs.
|
||
|
||
Opción A — Reverse-sync (ingest)
|
||
|
||
Añadir un modo al xtask que lee Cargo.toml y actualiza registry/dependencies.toml (dirección inversa):
|
||
|
||
1. En Zed, click en el upgrade inline sobre Cargo.toml
|
||
2. cargo xtask sync-deps --ingest ← lee Cargo.toml → escribe registry
|
||
3. cargo xtask sync-deps --target ../jpl-website
|
||
4. Commit los tres archivos juntos
|
||
|
||
Pros: mínimo cambio estructural, conserva el schema actual del registry (con rebase, etc).
|
||
Contras: dos comandos por bump, fácil olvidar el --ingest y commitear Cargo.toml drifteado → el pre-commit hook lo detecta pero
|
||
solo si está activo.
|
||
|
||
Opción B — Convertir el registry en Cargo.toml ficticio (recomendada)
|
||
|
||
Reemplazar registry/dependencies.toml por registry/Cargo.toml:
|
||
|
||
# registry/Cargo.toml
|
||
[workspace]
|
||
members = []
|
||
exclude = []
|
||
|
||
[workspace.dependencies]
|
||
leptos = { version = "0.8" }
|
||
axum = { version = "0.8" }
|
||
sqlx = { version = "0.9", features = ["postgres"], default-features = false }
|
||
rustelo_utils = { path = "../crates/foundation/crates/rustelo_utils" }
|
||
# ...
|
||
|
||
Y en rustelo/Cargo.toml:
|
||
exclude = ["xtask", "registry"]
|
||
|
||
Workflow:
|
||
1. Abrir registry/Cargo.toml en Zed
|
||
2. Click inline-upgrade — Zed actualiza directamente la fuente de verdad
|
||
3. cargo xtask sync-deps ← propaga a rustelo/Cargo.toml
|
||
4. cargo xtask sync-deps --target ../jpl-website
|
||
|
||
★ Insight ─────────────────────────────────────
|
||
- Truco clave: registry/Cargo.toml con members = [] es un workspace válido pero vacío — Cargo no compila nada ahí, pero
|
||
rust-analyzer sí parsea sus deps y Zed muestra los upgrades disponibles.
|
||
- Las paths necesitan re-prefix con ../ (porque la base cambia), pero el xtask ya tiene la lógica de pathdiff para rebase, así que
|
||
solo invierte el cálculo en una dirección.
|
||
- La metadata rebase = false no cabe en el formato Cargo nativo. Solución: moverla a un sidecar registry/sync.toml o inferirla
|
||
(cualquier path que sale del workspace = no-rebase por defecto).
|
||
─────────────────────────────────────────────────
|
||
|
||
Pros: Zed funciona nativamente, sin pasos extra. Bump y sync siguen siendo dos operaciones explícitas pero la primera ya no
|
||
requiere herramienta especial.
|
||
Contras: migración del schema, hay que decidir dónde vive rebase (sidecar o convención). Cargo carga un workspace extra en cada
|
||
operación (impacto mínimo).
|
||
|
||
Opción C — Aceptar la fricción
|
||
|
||
Documentar que Zed no debe usarse para upgrades en Cargo.toml, solo para descubrir versiones disponibles. El bump real se hace a
|
||
mano en registry/dependencies.toml. Cero código nuevo, máxima fricción humana.
|
||
|
||
Recomendación
|
||
|
||
Opción B. Es la única que elimina la fricción en lugar de añadirle un workaround. El xtask sigue siendo necesario (para el splice
|
||
cross-workspace), pero deja de ser el único punto de entrada — Zed se convierte en una interfaz válida porque la fuente de verdad
|
||
ahora habla el dialecto que Zed entiende.
|
||
|
||
La migración es mecánica:
|
||
1. Renombrar registry/dependencies.toml → registry/Cargo.toml con schema [workspace.dependencies]
|
||
2. Re-prefix de paths con ../
|
||
3. Mover rebase a registry/sync.toml (o inferirlo)
|
||
4. Adaptar xtask/src/registry.rs para leer el nuevo formato
|
||
5. Añadir exclude = ["registry"] a rustelo/Cargo.toml
|
||
6. Validar cargo xtask sync-deps --check con la nueva fuente
|
||
|
||
¿Quieres que lo implemente, o prefieres ver primero el código actual de xtask/src/registry.rs para evaluar el alcance del cambio?
|
||
|
||
✻ Baked for 1m 1s
|
||
|
||
※ recap: Goal: resolve friction where Zed shows Cargo.toml upgrades but xtask overwrites them. Current task: choose between
|
||
reverse-sync (ingest), converting registry to a fake Cargo.toml (recommended), or accepting the friction. Next: decide which option
|
||
to implement. (disable recaps in /config)
|
||
por qué implementar la opción B es más recomentable que A ?
|
||
|
||
⏺ ★ Insight ─────────────────────────────────────
|
||
- La pregunta no es trivial: A es menos invasiva (no migra schema, no toca paths) y resuelve el síntoma. B requiere migración. Si
|
||
solo miráramos esfuerzo, A gana. La elección depende de qué propiedad valoramos más: minimizar el cambio (A) o minimizar la
|
||
complejidad sistémica resultante (B).
|
||
─────────────────────────────────────────────────
|
||
|
||
Te lo desgloso en ejes concretos. Voy del más decisivo al menos.
|
||
|
||
1. Direccionalidad del flujo
|
||
|
||
A introduce bidirección. El sync deja de ser una operación push (registry → Cargo.toml) y pasa a tener dos modos: push y pull.
|
||
Bidirectional syncs son históricamente fuente de bugs sutiles — son el problema clásico de réplica master-master.
|
||
|
||
B preserva la unidireccionalidad. Siempre registry/Cargo.toml → todos los demás. El xtask no necesita resolver conflictos porque no
|
||
puede haberlos: solo hay un origen.
|
||
|
||
2. Idempotencia y orden de operaciones
|
||
|
||
A pierde idempotencia parcial. El resultado de cargo xtask sync-deps depende del estado relativo de dos archivos: si Cargo.toml
|
||
tiene un upgrade que registry no, el sync lo borra. Esto significa que el orden importa: primero --ingest, después sync-deps.
|
||
Olvidar el orden = trabajo perdido silenciosamente.
|
||
|
||
B es idempotente. Corre sync-deps N veces sobre el mismo registry: resultado idéntico. El estado en disco de Cargo.toml es
|
||
irrelevante para el resultado, porque siempre es destino, nunca origen.
|
||
|
||
3. Coherencia con el modelo de "fuente de verdad"
|
||
|
||
★ Insight ─────────────────────────────────────
|
||
- El diseño actual ya tiene "DO NOT EDIT" en los marcadores del bloque managed. Es una declaración explícita: este texto es
|
||
derivado, no lo toques. La opción A contradice esa declaración: pide al usuario que edite el bloque managed (vía Zed) como si fuera
|
||
fuente, y luego ingiera el cambio. Confunde el contrato.
|
||
─────────────────────────────────────────────────
|
||
|
||
A rompe el invariante semántico del marker # >>> rustelo-sync:workspace-deps (managed by xtask — DO NOT EDIT). Con --ingest, el
|
||
bloque managed pasa a ser editable bajo ciertas condiciones. El marker miente.
|
||
|
||
B preserva el invariante. El bloque managed sigue siendo solo-escritura-por-xtask. La edición humana ocurre en otro archivo,
|
||
claramente identificado como fuente.
|
||
|
||
4. Composabilidad con ecosistema Cargo
|
||
|
||
Esto es el argumento más fuerte a medio plazo.
|
||
|
||
A solo resuelve Zed. ¿Y cargo upgrade de cargo-edit? ¿Y Dependabot, que mira Cargo.toml? ¿Y cargo outdated? Cada herramienta que en
|
||
el futuro quieras integrar requerirá su propio adaptador --ingest-from-X, o un paso manual.
|
||
|
||
B resuelve toda la familia de una sola vez. Cualquier herramienta que sepa leer [workspace.dependencies] (es decir, todas las
|
||
herramientas Cargo-native) opera sobre la fuente sin saber que existe el sync.
|
||
|
||
```text
|
||
A B
|
||
│ │
|
||
┌────────┼────────┐ ┌────────┼────────┐
|
||
│ │ │ │ │ │
|
||
Zed cargo-up Depbot Zed cargo-up Depbot
|
||
│ │ │ │ │ │
|
||
▼ ▼ ▼ ▼ ▼ ▼
|
||
Cargo (custom) (custom) registry/Cargo.toml
|
||
│ │ │ │
|
||
└───────┼────────┘ ▼
|
||
▼ xtask sync
|
||
xtask --ingest
|
||
▼
|
||
registry
|
||
▼
|
||
xtask sync
|
||
```
|
||
|
||
En A cada herramienta nueva requiere un adaptador o un paso manual. En B la herramienta apunta directamente a la fuente.
|
||
|
||
5. Superficie de código en el xtask
|
||
|
||
A añade código: parser inverso (Cargo.toml → dep map), detección de conflicto (¿qué pasa si registry y Cargo.toml difieren en N
|
||
deps a la vez?), validación de overlay contra ingest, lógica de "qué dirección priorizar".
|
||
|
||
B reduce código: desaparece el parser custom de dependencies.toml, se reemplaza por el parser de Cargo.toml que el xtask ya tiene
|
||
(o toml_edit directo). El schema custom se va.
|
||
|
||
6. Modo de fallo y recuperación
|
||
|
||
┌───────────────────────────┬───────────────────────────────────────────────────────┬──────────────────────────────────────────┐
|
||
│ Escenario │ A │ B │
|
||
├───────────────────────────┼───────────────────────────────────────────────────────┼──────────────────────────────────────────┤
|
||
│ Editor con upgrade inline │ flujo natural pero peligroso (olvido de --ingest) │ flujo natural y seguro │
|
||
├───────────────────────────┼───────────────────────────────────────────────────────┼──────────────────────────────────────────┤
|
||
│ Edit manual en archivo │ difícil distinguir error de uso correcto (Cargo.toml │ error claro: editaste un bloque marcado │
|
||
│ equivocado │ es editable a veces) │ "DO NOT EDIT" │
|
||
├───────────────────────────┼───────────────────────────────────────────────────────┼──────────────────────────────────────────┤
|
||
│ Dos personas suben bumps │ merge complejo: ambos editaron Cargo.toml + ambos │ merge normal en registry/Cargo.toml, │
|
||
│ distintos en paralelo │ --ingest-aron, registry tiene un orden ganador │ conflicto de Git visible │
|
||
│ │ silencioso │ │
|
||
├───────────────────────────┼───────────────────────────────────────────────────────┼──────────────────────────────────────────┤
|
||
│ Pre-commit detecta drift │ sí, pero el mensaje confunde (¿debía hacer --ingest?) │ sí, mensaje claro: "registry y │
|
||
│ │ │ Cargo.toml difieren, corre sync-deps" │
|
||
└───────────────────────────┴───────────────────────────────────────────────────────┴──────────────────────────────────────────┘
|
||
|
||
7. Curva de aprendizaje para nuevos contribuidores
|
||
|
||
A: dos archivos parecidos (Cargo.toml y dependencies.toml con schema custom), una operación que depende de la dirección. Necesita
|
||
documentación y disciplina humana.
|
||
|
||
B: un archivo (registry/Cargo.toml) que cualquier rustáceo entiende a primera vista, un comando para propagar (sync-deps). La
|
||
documentación se reduce a "el registry es un workspace Cargo vacío cuya única función es declarar dependencies".
|
||
|
||
El caso donde A gana
|
||
|
||
Hay uno: si los rebase/features/metadata custom del schema actual son suficientemente ricos para que migrarlos a formato Cargo
|
||
nativo pierda información, A preserva la flexibilidad. Mirando el schema actual:
|
||
|
||
[deps.NAME]
|
||
version = "..."
|
||
path = "..."
|
||
features = ["..."]
|
||
default_features = false
|
||
rebase = false
|
||
|
||
Solo rebase no tiene correspondencia directa en formato Cargo. El resto (version, path, features, default-features) es exactamente
|
||
la sintaxis de Cargo workspace.dependencies. Así que la "pérdida" se limita a un campo, que se resuelve con un sidecar
|
||
registry/sync.toml muy pequeño:
|
||
|
||
[paths]
|
||
"platform-nats" = { rebase = false }
|
||
|
||
★ Insight ─────────────────────────────────────
|
||
- En el fondo, A trata el síntoma ("Zed no ve el registry"); B trata la causa ("la fuente de verdad usa un dialecto que el
|
||
ecosistema no entiende"). La cura siempre es preferible al alivio cuando el coste de la cura es proporcional al problema — y aquí
|
||
lo es: una migración mecánica de un archivo a otro formato.
|
||
─────────────────────────────────────────────────
|
||
|
||
Resumen ejecutivo
|
||
|
||
A resuelve Zed con un comando nuevo, manteniendo un schema custom y añadiendo bidireccionalidad. B resuelve Zed (y cualquier futura
|
||
herramienta Cargo-native) eliminando el schema custom y manteniendo la unidireccionalidad del sync. El coste es una migración
|
||
única; el beneficio es un sistema más simple y más componible para siempre.
|
||
|
||
A es la respuesta correcta si la migración es prohibitiva o si hay restricciones que aún no me has contado (p.ej. impls que ya usan
|
||
el formato dependencies.toml directamente). B es la respuesta correcta en cualquier otro escenario.
|
||
|
||
¿Hay alguna restricción del lado de jpl-website u otros impls que me esté perdiendo y que pudiera hacer la migración más costosa de
|
||
lo que asumo?
|
||
|
||
✻ Brewed for 1m 12s
|
||
|
||
❯ implementa la opción B
|
||
|
||
★ Insight ─────────────────────────────────────
|
||
- El sync reproduce bit-a-bit la región managed previa — confirma que el cambio de schema preserva la semántica. Para platform-nats
|
||
(no-rebase) el path se canonicaliza a ../stratumiops/crates/platform-nats igual que antes, pero ahora viene de
|
||
../../stratumiops/... en registry/Cargo.toml (registry-relative).
|
||
- pathdiff opera textualmente sobre componentes, así que canonicalize antes del diff evita que .. segmentos contaminen el output.
|
||
Sin canonicalize, el path emitido sería registry/../crates/... — válido pero feo. Con canonicalize, es limpio.
|
||
- [workspace.dependencies] con members = [] es un workspace Cargo legítimo: cargo metadata retorna packages: [] sin error. Cargo
|
||
solo valida sintaxis del manifest, no intenta resolver paths (no hay miembros que las usen). Esto es justo lo que necesitamos:
|
||
Zed/rust-analyzer escanean la tabla, Cargo no compila nada.
|
||
─────────────────────────────────────────────────
|
||
|
||
GUIDELINE: rust.md (no unwrap, idiomatic, ?, expect, clippy -D warnings clean)
|
||
IMPLEMENTING: opción B — registry como workspace Cargo vacío editable nativamente por Zed
|
||
EXCLUDED: documentación nueva, commits, actualización de .coder/2026-05-26-deps-sync.info.md
|
||
(queda stale; pendiente de tu confirmación para refrescarlo)
|
||
|
||
Cambios aplicados
|
||
|
||
┌────────────────────────────┬───────────────┬─────────────────────────────────────────────────────────────────────────────────┐
|
||
│ Archivo │ Acción │ Por qué │
|
||
├────────────────────────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
|
||
│ registry/Cargo.toml │ nuevo │ Workspace Cargo vacío con [workspace.dependencies] — fuente de verdad │
|
||
├────────────────────────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
|
||
│ registry/sync.toml │ nuevo │ Sidecar: no_rebase = ["platform-nats"] (única metadata sin equivalente Cargo) │
|
||
├────────────────────────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
|
||
│ registry/dependencies.toml │ borrado │ Schema custom reemplazado │
|
||
├────────────────────────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
|
||
│ xtask/src/registry.rs │ reescrito │ Parsea Cargo.toml (untagged enum string-or-table) + lee sidecar; Dep con │
|
||
│ │ │ Deserialize manual │
|
||
├────────────────────────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
|
||
│ xtask/src/render.rs │ reescrito │ Rebase con origen en registry/, canonicalize antes de diff_paths, propaga Result │
|
||
├────────────────────────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
|
||
│ xtask/src/sync.rs │ edición │ Propaga ? desde render_region; fix clippy pendiente │
|
||
│ │ mínima │ (manual_pattern_char_comparison) │
|
||
├────────────────────────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
|
||
│ xtask/src/overlay.rs │ edición │ Mensaje de error apunta a registry/Cargo.toml │
|
||
│ │ mínima │ │
|
||
├────────────────────────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
|
||
│ xtask/src/main.rs │ edición │ Comentarios docs actualizados │
|
||
│ │ mínima │ │
|
||
├────────────────────────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
|
||
│ Cargo.toml (raíz) │ edición │ exclude = ["xtask", "registry"] para que la sub-workspace no contamine │
|
||
├────────────────────────────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────┤
|
||
│ .pre-commit-config.yaml │ edición │ Regex ^(Cargo\.toml|registry/(Cargo|sync)\.toml)$ │
|
||
└────────────────────────────┴──────────────┴──────────────────────────────────────────────────────────────────────────────────┘
|
||
|
||
Verificación ejecutada
|
||
|
||
$ cargo build --manifest-path xtask/Cargo.toml → OK
|
||
$ cargo clippy --manifest-path xtask/Cargo.toml -D warnings → OK
|
||
$ cargo xtask sync-deps --check → "up to date" (exit 0)
|
||
$ cargo metadata --no-deps --manifest-path Cargo.toml → OK
|
||
$ cargo metadata --no-deps --manifest-path registry/Cargo.toml → OK (packages=[])
|
||
|
||
La región managed en rustelo/Cargo.toml quedó idéntica al estado previo a la migración — convergencia bit-a-bit valida que la
|
||
traducción del schema preserva semántica.
|
||
|
||
Flujo nuevo para Zed
|
||
|
||
1. Abrir registry/Cargo.toml en Zed → rust-analyzer detecta [workspace.dependencies] y muestra upgrades inline.
|
||
2. Click en el upgrade → Zed escribe directamente en la fuente de verdad.
|
||
3. cargo xtask sync-deps → propaga a rustelo/Cargo.toml.
|
||
4. cargo xtask sync-deps --target ../jpl-website → propaga a cada impl.
|
||
5. Pre-commit hook bloquea commits desincronizados (fires en cambios a Cargo.toml, registry/Cargo.toml o registry/sync.toml).
|
||
|
||
Pendientes (no implementados, por si los quieres)
|
||
|
||
- Actualizar .coder/2026-05-26-deps-sync.info.md: el doc describe el formato viejo (dependencies.toml, schema custom). Su modelo
|
||
conceptual sigue siendo correcto pero los detalles concretos están stale.
|
||
- Sincronizar jpl-website u otros impls: no he tocado nada fuera de rustelo. Cuando ejecutes cargo xtask sync-deps --target
|
||
../jpl-website la primera vez producirá output equivalente al anterior (la región managed allí seguirá la misma forma).
|
||
- Auto-discovery de impls: sigue pendiente del .info original; no es regresión sino limitación heredada.
|
||
|
||
STATUS: Complete
|
||
|
||
⏺ ★ Insight ─────────────────────────────────────
|
||
- En rustelo las recetas viven en el top-level (justfiles/build.just importado con import), así que se invocan planas (just
|
||
deps-sync). En jpl-website el justfile usa mod build "just/build.just", así que se invocan con namespace (just build deps-sync).
|
||
Misma funcionalidad, dos convenciones distintas según el patrón del proyecto.
|
||
- Para el impl, la receta encapsula tres detalles operacionales molestos: la ruta al xtask (../rustelo/xtask/Cargo.toml), las flags
|
||
--rustelo-root ../rustelo --target ., y el override de CARGO_TARGET_DIR para no contaminar el target volume-pinned del framework.
|
||
El humano nunca tiene que recordarlos.
|
||
─────────────────────────────────────────────────
|
||
|
||
Resumen de cambios
|
||
|
||
Documentación
|
||
- .coder/2026-05-26-deps-sync.info.md — actualizado: schema nuevo (registry/Cargo.toml + sync.toml), workflow editor-driven con
|
||
Zed, rationale de la migración, verificación de bit-identical reproduction.
|
||
- rustelo/reflection/qa.ncl — deps-sync-workflow reescrito: bump editor-driven como flujo recomendado, schema Cargo-native, sidecar
|
||
no_rebase, sección "why a fake Cargo.toml".
|
||
- jpl-website/reflection/qa.ncl — deps-sync-impl-workflow actualizado: referencia a ../rustelo/registry/Cargo.toml, mención de Zed
|
||
inline upgrades para descubrir versiones disponibles.
|
||
|
||
Recetas just
|
||
│ rustelo │ just deps-sync │ propagar registry → rustelo/Cargo.toml │
|
||
├─────────────┼──────────────────────────────────────────┼──────────────────────────────────────────────┤
|
||
│ rustelo │ just deps-sync-check │ verificar drift sin escribir │
|
||
├─────────────┼──────────────────────────────────────────┼──────────────────────────────────────────────┤
|
||
│ rustelo │ just deps-sync-impl ../jpl-website │ propagar a impl arbitrario │
|
||
├─────────────┼──────────────────────────────────────────┼──────────────────────────────────────────────┤
|
||
│ rustelo │ just deps-sync-check-impl ../jpl-website │ verificar drift en impl │
|
||
├─────────────┼──────────────────────────────────────────┼──────────────────────────────────────────────┤
|
||
│ rustelo │ just deps-sync-all │ rustelo + impls conocidos en un solo comando │
|
||
├─────────────┼──────────────────────────────────────────┼──────────────────────────────────────────────┤
|
||
│ jpl-website │ just build deps-sync │ pull desde ../rustelo/registry/Cargo.toml │
|
||
├─────────────┼──────────────────────────────────────────┼──────────────────────────────────────────────┤
|
||
│ jpl-website │ just build deps-sync-check │ verificar drift contra rustelo │
|
||
└─────────────┴──────────────────────────────────────────┴──────────────────────────────────────────────┘
|
||
|
||
Verificado
|
||
$ just deps-sync-check → ./Cargo.toml is up to date (exit 0)
|
||
$ just --list (rustelo) → 5 recipes deps-sync* visibles
|
||
$ just --list build (jpl) → deps-sync, deps-sync-check visibles bajo build::
|
||
|
||
STATUS: Complete
|
||
|
||
escribe un doc en .coder
|
||
para pedir en una sesión claude code
|
||
que se implemente este mismo mecanismo tal cual (opción B e incluir las recetas just) de sync de crates en un proyecto nuevo
|
||
|
||
|
||
⏺ ★ Insight ─────────────────────────────────────
|
||
- El doc está estructurado como prompt para una sesión Claude Code, no como referencia general. Cada task usa el formato
|
||
obligatorio del proyecto (COMPLEXITY/DEPENDENCIES/RISKS/SUCCESS_CRITERIA) sin estimaciones temporales, lo que evita que la sesión
|
||
receptora "improvise" pasos.
|
||
- Las tasks marcan dependencias explícitas (1→3→4→5→6→[7,8,9]→10), así que si la sesión receptora se interrumpe a la mitad, puede
|
||
retomar desde un estado parcial seguro sin reconstruir contexto.
|
||
- El doc separa lo que se debe copiar tal cual (xtask source, markers # >>> rustelo-sync) de lo que hay que adaptar (paths
|
||
registry-relativos, lista de deps, hook id). Esa separación es lo que distingue una replicación correcta de una "reescritura
|
||
libre".
|
||
─────────────────────────────────────────────────
|
||
|
||
Resumen del doc
|
||
|
||
Ruta: .coder/2026-05-26-deps-sync-replication.plan.md
|
||
|
||
Es un brief auto-contenido para entregar a otra sesión Claude Code. Estructura:
|
||
|
||
1. Why / Reference implementation — contexto mínimo + apuntadores a la doc canónica de rustelo
|
||
2. Preconditions — qué debe tener el proyecto destino
|
||
3. Open questions — 5 preguntas que la sesión receptora debe hacer al usuario antes de empezar (project root, project name,
|
||
framework-vs-impl, no-rebase deps, downstream impls)
|
||
4. 10 tasks ordenadas con COMPLEXITY/DEPENDENCIES/RISKS/SUCCESS_CRITERIA cada una:
|
||
- 1: crear registry/Cargo.toml
|
||
- 2: crear registry/sync.toml
|
||
- 3: copiar xtask
|
||
- 4: editar root Cargo.toml (exclude)
|
||
- 5: añadir alias .cargo/config.toml
|
||
- 6: primer sync + commit
|
||
- 7: pre-commit hook
|
||
- 8: recetas just (framework)
|
||
- 9: recetas just (impl) — opcional
|
||
- 10: validación end-to-end
|
||
5. Adaptations checklist — qué cambiar vs rustelo (paths, nombre, lista de deps, etc.)
|
||
6. Things to leave untouched — marker text, standalone-workspace de xtask, deserialización dual de Dep
|
||
7. Order of independence — qué subconjuntos de tasks son estados estables si se quiere parar a mitad
|
||
8. See also — los tres docs/QA de referencia en el repo rustelo
|
||
|
||
El doc asume que rustelo está accesible para cp -r ../rustelo/xtask .; si no lo está, la sesión receptora debe preguntar dónde vive
|
||
(mencionado explícitamente en la Open question #1 y Task 3 NOTES).
|
||
|
||
STATUS: Complete
|
||
|
||
exlica lo de Auto-discovery de impls: sigue pendiente del .info original; no es regresión sino limitación heredada.
|
||
|
||
⏺ ★ Insight ─────────────────────────────────────
|
||
- "Limitación heredada" significa que existía en el diseño original con dependencies.toml y la migración a opción B no la introdujo
|
||
ni la resolvió. Decir "no es regresión" es importante: si el usuario migrara con la expectativa de que B arreglase TODO lo del
|
||
.info original, este punto le decepcionaría sin esta aclaración.
|
||
─────────────────────────────────────────────────
|
||
|
||
Qué significa "auto-discovery de impls"
|
||
|
||
Hoy, cuando ejecutas cargo xtask sync-deps, el xtask sincroniza un solo target — el que recibe por CLI. Hay tres modos posibles:
|
||
|
||
cargo xtask sync-deps → rustelo/Cargo.toml
|
||
cargo xtask sync-deps --target ../jpl-website → jpl-website/Cargo.toml
|
||
cargo xtask sync-deps --target ../otra-impl → otra-impl/Cargo.toml
|
||
|
||
El xtask no sabe qué impls existen en el ecosistema. Si añades ../proyecto-z mañana como consumidor del registry, el xtask no se
|
||
entera hasta que alguien le pase --target ../proyecto-z explícitamente.
|
||
|
||
Auto-discovery sería el mecanismo opuesto: el xtask conoce la lista completa de impls y puede iterar sobre ellos sin que un humano
|
||
se la recuerde:
|
||
|
||
cargo xtask sync-deps --all
|
||
→ sincroniza rustelo + cada impl declarada en algún lugar
|
||
|
||
Por qué importa operacionalmente
|
||
|
||
Hoy hay tres puntos de fricción:
|
||
|
||
1. Memoria humana: para mantener el ecosistema en sync, alguien debe recordar la lista de impls. En mi receta deps-sync-all añadí
|
||
../jpl-website hardcoded — si mañana aparece ../jpl-blog, hay que editar la receta. Y nadie lo recordará hasta que rompa algo.
|
||
2. Drift silencioso: si una impl nueva no se añade a la lista, los pre-commit hooks de cada repo seguirán fallando
|
||
independientemente (eso protege la integridad de cada repo aisladamente), pero no hay un sitio donde rustelo te diga "atención, hay
|
||
3 impls drifteadas". Cada repo es una isla hasta que alguien hace commit allí.
|
||
3. Onboarding: un colaborador nuevo en rustelo no sabe automáticamente qué impls hay que sincronizar tras un bump de versión. Tiene
|
||
que preguntar o leer el README/justfile.
|
||
|
||
Por qué no es regresión de la migración a opción B
|
||
|
||
El diseño original (registry/dependencies.toml schema custom) tampoco tenía auto-discovery. El .info original lo lista en su
|
||
sección Open items:
|
||
|
||
- Auto-discovery of impls via `rustelo.manifest.toml` `[sync]` section
|
||
(currently impls are reached via explicit `--target`).
|
||
|
||
La migración a opción B preservó todo el comportamiento del CLI: --target sigue funcionando idéntico, los markers son los mismos,
|
||
el splice es el mismo. Solo cambió el formato de la fuente de verdad, no la topología del sync. Por eso uso "limitación heredada" —
|
||
viaja con el sistema desde el diseño original.
|
||
|
||
★ Insight ─────────────────────────────────────
|
||
- En realidad la migración a opción B hace este open item más fácil de resolver, no más difícil. Antes había que extender el schema
|
||
custom de dependencies.toml ([meta], [deps.X], y ahora añadir [sync.impls]). Con opción B, ese tipo de metadata no-Cargo ya tiene
|
||
un sitio natural: registry/sync.toml. Solo hay que añadir un campo y una iteración en sync.rs.
|
||
─────────────────────────────────────────────────
|
||
|
||
Cómo se resolvería (boceto, no implementado)
|
||
|
||
Extender registry/sync.toml:
|
||
|
||
no_rebase = ["platform-nats"]
|
||
|
||
[impls]
|
||
known = [
|
||
"../jpl-website",
|
||
"../jpl-blog", # ejemplo futuro
|
||
]
|
||
|
||
En el xtask, añadir un flag --all:
|
||
|
||
// sync.rs (boceto)
|
||
if args.all {
|
||
let sidecar = registry::load_sidecar(&args.rustelo_root)?;
|
||
// sync rustelo
|
||
sync_target(&args.rustelo_root, /*target=*/ &args.rustelo_root, …)?;
|
||
for impl_path in sidecar.impls.known {
|
||
let target = args.rustelo_root.join(&impl_path);
|
||
sync_target(&args.rustelo_root, &target, …)?;
|
||
}
|
||
}
|
||
|
||
Y la receta just simplificaría:
|
||
|
||
# antes
|
||
deps-sync-all:
|
||
@just deps-sync
|
||
@just deps-sync-impl ../jpl-website # hardcoded
|
||
|
||
# después
|
||
deps-sync-all:
|
||
cargo xtask sync-deps --all # lista vive en sync.toml
|
||
|
||
Por qué sigue pendiente
|
||
|
||
Es un trade-off consciente, no un olvido:
|
||
|
||
┌───────────────────────────────────────┬──────────────────────────────────────────────────────────────┐
|
||
│ Argumento a favor de implementarlo │ Argumento a favor de aplazarlo │
|
||
├───────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
|
||
│ Elimina memoria humana del flujo │ Hoy solo hay 1 impl (jpl-website); sobre-ingeniería para N=1 │
|
||
├───────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
|
||
│ Drift detection centralizado │ Cada impl ya tiene su pre-commit hook independiente │
|
||
├───────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
|
||
│ Onboarding más simple │ El README/justfile ya documenta el comando manual │
|
||
├───────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
|
||
│ sync.toml ya tiene sitio para meterlo │ Es código nuevo en xtask, validación, tests │
|
||
└───────────────────────────────────────┴──────────────────────────────────────────────────────────────┘
|
||
|
||
El veredicto del .info original fue "no urgente, pero está identificado". Coincido. Si alguna vez aparece la segunda impl, vale la
|
||
pena cerrarlo; con una sola impl, el --target manual es perfectamente operativo.
|
||
|
||
Resumen en una línea
|
||
|
||
▎ Auto-discovery = el xtask conoce la lista de impls que consumen el registry y puede sincronizarlas en bloque sin que un humano se
|
||
▎ las recuerde. Sigue pendiente porque con una sola impl no compensa el código adicional, pero la opción B (sidecar sync.toml)
|
||
▎ deja el camino más despejado para resolverlo el día que haga falta.
|
||
|
||
✻ Churned for 59s
|
||
|
||
❯ implementalo, en cuanto verifiquemos jpl-website vamos a crear más sites
|
||
|
||
Plan en orden de dependencias:
|
||
|
||
TASK 1: Extender sidecar con [impls.known] en registry.rs
|
||
COMPLEXITY: simple
|
||
DEPENDENCIES: none
|
||
RISKS: typos en sync.toml apuntan a paths que no existen → mitigación: validar existencia al cargar
|
||
SUCCESS_CRITERIA: load_sidecar() retorna no_rebase + impls
|
||
|
||
TASK 2: Refactorizar sync.rs para extraer sync_one() y añadir driver --all
|
||
COMPLEXITY: simple
|
||
DEPENDENCIES: TASK 1
|
||
RISKS: en --check --all, error en target N no debe abortar el chequeo de N+1
|
||
SUCCESS_CRITERIA: sync_one por target, drift de cualquier target → exit 1 al final
|
||
|
||
TASK 3: Añadir flag --all en CLI (conflicts_with --target y --overlay)
|
||
COMPLEXITY: trivial
|
||
DEPENDENCIES: TASK 2
|
||
SUCCESS_CRITERIA: --all sin --target ni --overlay funciona; combinarlos error
|
||
|
||
TASK 4: Declarar ../jpl-website en registry/sync.toml [impls.known]
|
||
COMPLEXITY: trivial
|
||
DEPENDENCIES: TASK 3
|
||
SUCCESS_CRITERIA: cargo xtask sync-deps --all itera 2 targets
|
||
|
||
TASK 5: Sustituir deps-sync-all en justfile + añadir deps-sync-check-all
|
||
COMPLEXITY: trivial
|
||
DEPENDENCIES: TASK 4
|
||
SUCCESS_CRITERIA: just deps-sync-check-all exit 0
|
||
|
||
TASK 6: Documentación (info, QA, replication plan)
|
||
COMPLEXITY: simple
|
||
DEPENDENCIES: TASK 5
|
||
SUCCESS_CRITERIA: auto-discovery sale de Open Items y aparece en Architecture
|
||
|
||
Empiezo.
|
||
|
||
|
||
⏺ ★ Insight ─────────────────────────────────────
|
||
|
||
- El refactor importante fue normalizar paths registry-relative →
|
||
rustelo-root-relative en el loader, en lugar de cambiar la lógica del render.
|
||
Eso preservó el contrato de Rebase::translate_path y evitó romper el
|
||
rustelo-root-relativos, no registry-relativos). Una capa de traducción
|
||
centralizada en un solo punto = un solo sitio que mantener.
|
||
- sync_one retorna un enum SyncOutcome en vez de un ExitCode. Eso permite al
|
||
driver de --all agregar drift de N targets sin perder información: cada target
|
||
imprime su outcome, y solo al final el run agregado decide el código de
|
||
salida. Si hubiera usado ExitCode, habrías tenido que return-early en el
|
||
primer drift.
|
||
- Adding una impl ahora es exactamente una línea en registry/sync.toml. No
|
||
xtask, no recetas. Esa es la propiedad clave para tu plan de crear más sites:
|
||
la fricción operacional al escalar de N=1 a N=K se mantiene constante (O(1)
|
||
por impl).
|
||
─────────────────────────────────────────────────
|
||
|
||
GUIDELINE: rust.md (no unwrap, idiomatic, ?, expect, clippy -D warnings clean)
|
||
IMPLEMENTING: auto-discovery de impls vía [impls.known] en sync.toml + --all
|
||
en xtask
|
||
EXCLUDED: ondaod evaluation (sin solicitud de adr?), commits
|
||
|
||
Cambios
|
||
|
||
┌───────────────────────┬──────────────────────────────────────────────────┐
|
||
│ Archivo │ Acción │
|
||
├───────────────────────┼──────────────────────────────────────────────────┤
|
||
│ xtask/src/registry.rs │ Sidecar struct, load_sidecar() público, │
|
||
│ │ normalize_registry_path() (strip ..) │
|
||
├───────────────────────┼──────────────────────────────────────────────────┤
|
||
│ xtask/src/sync.rs │ refactor en sync_one() + driver de --all; agrega │
|
||
│ xtask/src/sync.rs │ refactor en sync_one() + driver de │
|
||
│ │ --all; agrega drift sin fail-fast │
|
||
├─────────────────────────────────────┼────────────────────────────────────┤
|
||
│ │ revert al contrato original │
|
||
│ xtask/src/render.rs │ (Dep.path rustelo-root-relative, │
|
||
│ │ sin canonicalize) │
|
||
├─────────────────────────────────────┼────────────────────────────────────┤
|
||
│ xtask/src/main.rs │ flag --all, --target ahora │
|
||
│ │ Option<PathBuf> │
|
||
├─────────────────────────────────────┼────────────────────────────────────┤
|
||
│ registry/sync.toml │ añadido [impls] known = │
|
||
│ │ ["../jpl-website"] │
|
||
├─────────────────────────────────────┼────────────────────────────────────┤
|
||
────────────────────────────────────────
|
||
Archivo: .coder/2026-05-26-deps-sync.info.md
|
||
Acción: [impls] documentado, --all documentado, auto-discovery movida de Open
|
||
→
|
||
Closed
|
||
────────────────────────────────────────
|
||
Archivo: reflection/qa.ncl (rustelo)
|
||
Acción: sección PROPAGATE TO IMPLEMENTATIONS actualizada con --all
|
||
────────────────────────────────────────
|
||
Archivo: .coder/2026-05-26-deps-sync-replication.plan.md
|
||
Acción: Task 2 y Task 8 actualizadas con [impls.known] y --all
|
||
|
||
Verificación
|
||
|
||
$ cargo xtask sync-deps --check → exit 0
|
||
$ cargo xtask sync-deps --all --check → 2 targets, exit 0
|
||
$ cargo xtask sync-deps --all --target X → rejected (exit 2)
|
||
$ cargo xtask sync-deps --all --overlay Y → rejected (exit 2)
|
||
$ cargo clippy --manifest-path xtask/Cargo.toml -- -D warnings → exit 0
|
||
$ just deps-sync-check-all → 2 targets, exit 0
|
||
|
||
Para los próximos sites
|
||
|
||
Cuando crees ../site-nueva, el workflow es:
|
||
|
||
1. Crear el workspace ../site-nueva con [workspace.dependencies] (vacío o
|
||
pobladio).
|
||
|
||
Para los próximos sites
|
||
|
||
Cuando crees ../site-nueva, el workflow es:
|
||
|
||
1. Crear el workspace ../site-nueva con [workspace.dependencies] (vacío o pobladio).
|
||
2. Opcional: crear ../site-nueva/rustelo-deps-overlay.toml con [overlay.*] y [extra.*].
|
||
3. Editar registry/sync.toml: añadir "../site-nueva" al array [impls.known].
|
||
4. cargo xtask sync-deps --all — itera rustelo + jpl-website + site-nueva.
|
||
5. (Opcional) Replicar el hook pre-commit en ../site-nueva/.pre-commit-config.yaml apuntando al xtask de rustelo.
|
||
|
||
No hay cambios al xtask ni a las recetas just. La operación es O(1) por impl añadida.
|
||
|
||
STATUS: Complete
|
||
|
||
|
||
⏺ Confirmado. El doc ahora refleja el modelo [impls.known] + --all en cuatro puntos:
|
||
|
||
┌──────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────────────────┐
|
||
│ Sección │ Contenido │
|
||
├──────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
|
||
│ Open questions (línea 67) │ Pregunta sobre impls indica que se encodean en registry/sync.toml [impls.known], no │
|
||
│ │ en la receta │
|
||
├──────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
|
||
│ Task 2 RISKS (línea 104) │ Añade riesgo de impl path sin Cargo.toml │
|
||
├──────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
|
||
│ Task 2 SUCCESS_CRITERIA (líneas │ Schema de sync.toml incluye [impls] known = [...] │
|
||
│ 113–114) │ │
|
||
├──────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
|
||
│ Task 2 NOTES (línea 121) │ Documenta que [impls.known] habilita --all y que añadir impls es one-line edit │
|
||
├──────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
|
||
│ Task 8 recipes (líneas 269–278) │ deps-sync-all y deps-sync-check-all usan cargo xtask sync-deps --all │
|
||
├──────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────┤
|
||
│ Adaptations checklist (línea 354) │ Señala que la lista de impls va en sync.toml, no en la receta │
|
||
└──────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────┘
|
||
|
||
STATUS: Complete
|