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>
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) yrunbooks/**/*.mdson los únicos documentos que llevan atributos de celda Runme.- Cada celda invoca exclusivamente
make,./bin/clusterlogo un script ya existente enscripts/. 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 — verinternal/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.mdconserva la agenda y el criterio de la reunión (prosa) y enlaza arunbooks/weekly-review.mdpara los comandos ejecutables, en vez de mantener los mismos bloques de shell en dos archivos.README.mdenlazaRUNBOOK.mdydocs/RUNME.mden 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:
- Identidad de contenido (Lifecycle Identity /
namede Runme): identifica documento y celda, no a quien ejecuta. - Evidencia local de sesión (Session Outputs, no habilitados aquí): auxiliar, nunca auditoría canónica.
- 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
- 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 querunbooks/fuera decontent/ya resuelve el conflicto sin ese riesgo. - Validación en un script Bash aparte en vez de
clusterlog validate. Descartada para las reglas estructurales: encajan limpio en el patrón existente deValidationReport/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. - 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 dev3.17.4se confirmó querunme run --tagsí existe y filtra correctamente (verdocs/RUNME.md). Se adoptó--tag ci-safe --all --skip-promptsporque 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 validateahora también protege contra celdas mutantes mal etiquetadas, nombres duplicados y fugas de Runme haciacontent/.- La instalación reproducible con checksum evita depender de
latesto decurl | 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 delMakefile.
Limitaciones
- Zola local (
0.22.1) no coincide con la versión que exigeREADME.mdy que instala la CI (0.23.3); no se intentó resolver esto en esta iteración. - No se ejecutó ninguna celda
risk-mutatingde 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.mdfuera de Runme.
Trabajo futuro
- Evaluar un comando
clusterlog runbook recordque capture, con su propio contrato JSON y modelo de amenazas, evidencia de que una celdaci-safecorrió — 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 --jsonexponga etiquetas en una versión futura de Runme, para poder validar la relación entre "celdas etiquetadas ci-safe" y "celdas querunme-cirealmente ejecutó" de forma automatizada en vez de manual.