Files
clusterlogs/docs/adr/0004-runme-sidecar.md
T
fernandoandClaude Sonnet 5 c06037fa3b feat(runme): integrar Runme como sidecar de documentación ejecutable
Agrega RUNBOOK.md y runbooks/ (fuera de content/, para no interferir con
la publicación de Zola), con reglas de nombres/etiquetas/aislamiento de
celdas mutantes verificadas por clusterlog validate. Instalación pinneada
a v3.17.4 con checksum verificado (scripts/runme-install.sh), un job de
CI separado (.gitea/workflows/ci.yml) que sólo corre celdas ci-safe, y la
decisión documentada en docs/adr/0004-runme-sidecar.md y docs/RUNME.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 00:06:01 -06:00

10 KiB

ADR-0004: Runme como sidecar de documentación ejecutable

  • Estado: aceptada
  • Fecha: 2026-08-26

Contexto

Los procedimientos frecuentes de clusterlog (compilar, probar, sincronizar, validar, la revisión semanal) viven como bloques de comando dentro de Markdown que hay que copiar y pegar a mano. Se evaluó integrar Runme (v3.17.4, fijado) para poder abrir esos mismos procedimientos como notebooks ejecutables desde VS Code o su CLI, sin introducir un segundo lugar donde vivan los mismos comandos ni debilitar el modelo de identidad/auditoría existente (huella SSH + commit firmado + clusterlog verify commit).

Decisión

Se integra Runme como una capa opcional y adyacente:

  • RUNBOOK.md (raíz) y runbooks/**/*.md son los únicos documentos que llevan atributos de celda Runme.
  • Cada celda invoca exclusivamente make, ./bin/clusterlog o un script ya existente en scripts/. Ninguna celda reimplementa validación, autorización, resolución de identidad, rollback o gestión de secretos.
  • clusterlog validate (no un script aparte) hace cumplir las reglas de nombres, etiquetas y aislamiento de celdas mutantes — ver internal/clusterlog/runbooks.go.
  • La instalación de Runme es opcional y reproducible (scripts/runme-install.sh, checksum verificado); ningún flujo existente (make build, make test, clusterlog sync/validate, zola build) depende de que Runme esté instalado.

Significado de "sidecar"

No es un sidecar de Kubernetes ni un servicio adicional. Es un envoltorio: el humano o agente interactúa con Markdown+Runme, que a su vez llama make, clusterlog o un script — nunca al revés, y nunca con lógica propia.

Ubicación de los runbooks

RUNBOOK.md en la raíz (índice) y runbooks/ como carpeta dedicada, fuera de content/. Se confirmó en zola.toml que Zola no tiene un content_dir alterno: sólo construye desde content/. Poner los runbooks fuera de esa carpeta significa que Zola nunca los toca — cero riesgo de que un atributo de celda {"name":...} rompa el front matter TOML o el resaltado de sintaxis de una página publicada. clusterlog validate además hace cumplir esto activamente: si una celda de Runme aparece dentro de content/, es un error (runme_cell_in_zola_content), no sólo una recomendación.

Relación con content/manuales

content/manuales/ no se toca. Sigue siendo prosa + comandos de ejemplo para lectura humana en el sitio Zola, sin atributos de celda ni ninguna dependencia de Runme. Los runbooks nuevos no son una migración de los manuales existentes: son procedimientos de desarrollo/operación de clusterlog mismo que hoy no existían como documento ejecutable.

Fuente de verdad

Opción elegida de las tres consideradas (sección 4.2 del pedido original): un archivo raíz delgado que enlaza procedimientos canónicos, aplicada así:

  • docs/WEEKLY_REVIEW.md conserva la agenda y el criterio de la reunión (prosa) y enlaza a runbooks/weekly-review.md para los comandos ejecutables, en vez de mantener los mismos bloques de shell en dos archivos.
  • README.md enlaza RUNBOOK.md y docs/RUNME.md en vez de duplicar ejemplos de comandos.

No se copian bloques de comando manualmente entre dos documentos en ningún punto de esta integración.

Convención de metadatos

Atributos de celda como objeto JSON de una sola línea después del lenguaje del fence: ```sh {"name":"...","tag":"...","interactive":"...", "excludeFromRunAll":true}. clusterlog validate decodifica ese objeto con encoding/json (no confía en una regex para interpretar el contenido; la regex sólo reconoce la forma de la línea de fence).

Taxonomía de etiquetas

Cuatro categorías, tres obligatorias por celda:

Categoría Valores Obligatoria
Audiencia role-developer, role-operator, role-reviewer, role-owner sí
Entorno env-local, env-ci, env-staging, env-production sí
Riesgo risk-read-only, risk-mutating, risk-destructive sí
Ejecución automatizada ci-safe, manual-only no, mutuamente excluyente con riesgo mutante

Modelo de identidad

Tres capas, documentadas en detalle en docs/RUNME.md:

  1. Identidad de contenido (Lifecycle Identity / name de Runme): identifica documento y celda, no a quien ejecuta.
  2. Evidencia local de sesión (Session Outputs, no habilitados aquí): auxiliar, nunca auditoría canónica.
  3. Auditoría canónica de Clusterlog: huella SSH + data/admins.json + commit firmado + clusterlog verify commit, sin cambios.

No se afirma ni se implementa ningún enlace criptográfico entre Runme y un commit. Un futuro clusterlog runbook record queda explícitamente fuera de esta iteración (ver "Trabajo futuro").

Modelo de evidencia

Session Outputs permanecen desactivados por defecto en esta integración. Si se habilitan más adelante, deben quedar fuera de Git, del sitio Zola y de Gists, y tratarse como evidencia auxiliar redactable, nunca como prueba de autoría.

Limitación reconocida: la documentación pública de Runme para v3.17.4 no fija el nombre exacto de archivo de un Session Output (docs.runme.dev/usage/auto-save/ sólo recomienda "agréguelo a .gitignore" sin dar el patrón). Se optó explícitamente por no agregar un glob amplio e inseguro a .gitignore que pudiera ocultar Markdown legítimo. En su lugar, clusterlog validate aplica dos señales concretas (directorio .runme/ versionado; archivos en runbooks/ con "output" en el nombre) como defensa best-effort, documentada como tal.

CI/CD

.gitea/workflows/ci.yml (Gitea Actions, no GitHub Actions — este repo no tiene .github/) gana un job nuevo y separado que no reemplaza test-and-build: instala Runme fijado en v3.17.4 vía scripts/runme-install.sh (checksum verificado), corre make runme-check (reglas de clusterlog validate, obligatorias, más un chequeo blando de compatibilidad de Runme) y make runme-ci (runme run --filename RUNBOOK.md --tag ci-safe --all --skip-prompts). Permisos mínimos (contents: read), DO_NOT_TRACK=true, SCARF_NO_ANALYTICS=true, sin llaves ni acceso a producción.

La CI original de Zola instala herramientas fijadas por curl + tar en vez de una Action de marketplace (ver el paso "Instalar Zola fijado" ya existente); Runme sigue el mismo patrón por consistencia, no por elección nueva.

Seguridad

Ver el nuevo apartado en SECURITY.md. En resumen: una celda Runme equivale a ejecutar comandos locales con los permisos de quien la corre; una etiqueta no es control de acceso, excludeFromRunAll/interactive son defensas adicionales; Runme no lee llaves privadas; Runme Cloud, Gists y runme open expuesto a 0.0.0.0 quedan fuera de alcance.

Alternativas consideradas

  1. Celdas dentro de content/manuales/. Descartada: arriesga el front matter TOML y el resaltado de Zola, y mezcla contenido para lectores con contenido para ejecución; no encaja con la opción 1 de fuente única (Markdown único compatible con ambos) porque el pedido de Zola y el de Runme divergen (TOML +++ vs. atributos JSON de celda) sin un beneficio real, dado que runbooks/ fuera de content/ ya resuelve el conflicto sin ese riesgo.
  2. Validación en un script Bash aparte en vez de clusterlog validate. Descartada para las reglas estructurales: encajan limpio en el patrón existente de ValidationReport/report.add(...) y ya tenían pruebas unitarias equivalentes para páginas/eventos. Sí se mantiene un script (scripts/runme-check.sh) para el chequeo blando que sólo tiene sentido con el binario real de Runme instalado.
  3. Selección de celdas CI-safe con una lista de nombres en el Makefile en vez del flag --tag. Fue el plan inicial, basado en documentación pública de Runme que no mostraba un flag de filtrado por etiqueta. Al instalar el binario real de v3.17.4 se confirmó que runme run --tag sí existe y filtra correctamente (ver docs/RUNME.md). Se adoptó --tag ci-safe --all --skip-prompts porque es la forma más simple que sigue siendo una allowlist explícita por etiqueta de seguridad, no un "correr todo" sin filtro.

Consecuencias

Positivas:

  • Los procedimientos de desarrollo se pueden ejecutar como notebook sin perder la posibilidad de copiar/pegar el comando a mano.
  • clusterlog validate ahora también protege contra celdas mutantes mal etiquetadas, nombres duplicados y fugas de Runme hacia content/.
  • La instalación reproducible con checksum evita depender de latest o de curl | sh.

Negativas / riesgos aceptados:

  • El analizador de runbooks es un escáner de líneas, no un parser CommonMark completo; hay casos borde documentados en docs/RUNME.md.
  • La detección de Session Outputs es best-effort, no una garantía.
  • Dos documentos nuevos (RUNBOOK.md, docs/RUNME.md) que hay que mantener sincronizados con la realidad del Makefile.

Limitaciones

  • Zola local (0.22.1) no coincide con la versión que exige README.md y que instala la CI (0.23.3); no se intentó resolver esto en esta iteración.
  • No se ejecutó ninguna celda risk-mutating de verdad durante la implementación (por diseño: requieren identidad real y quedan fuera del alcance de una demostración automatizada).
  • No existe hoy un enlace criptográfico entre una ejecución de Runme y un commit; quien necesite esa garantía debe seguir el flujo completo de AGENTS.md fuera de Runme.

Trabajo futuro

  • Evaluar un comando clusterlog runbook record que capture, con su propio contrato JSON y modelo de amenazas, evidencia de que una celda ci-safe corrió — sólo si el equipo decide que Session Outputs no bastan como evidencia auxiliar.
  • Migrar manuales existentes a runbooks ejecutables uno por uno, sólo cuando exista infraestructura real que respalde cada procedimiento (no antes).
  • Revisar si vale la pena que runme ls --json exponga etiquetas en una versión futura de Runme, para poder validar la relación entre "celdas etiquetadas ci-safe" y "celdas que runme-ci realmente ejecutó" de forma automatizada en vez de manual.