diff --git a/.metodo/.gitattributes b/.metodo/.gitattributes new file mode 100644 index 0000000..fcadb2c --- /dev/null +++ b/.metodo/.gitattributes @@ -0,0 +1 @@ +* text eol=lf diff --git a/.metodo/CHECKSUMS b/.metodo/CHECKSUMS new file mode 100644 index 0000000..f75bca3 --- /dev/null +++ b/.metodo/CHECKSUMS @@ -0,0 +1,2 @@ +03db9775718f9529c2c59ea87ff6e109e77a6408e599755c9c5ee0c555466c54 metodo.md +3046e8f13185e4c6a97aaccedeb2f81a958bdc7ceb6b395d411b743c86950f50 bin/metodo diff --git a/.metodo/VERSION b/.metodo/VERSION new file mode 100644 index 0000000..261cd98 --- /dev/null +++ b/.metodo/VERSION @@ -0,0 +1,5 @@ +# Copia vendorizada del Método (raíl C11). Gestionada por 'metodo init/update'. +master: metodo +version: 2026-08-08 +estado: vendored +adopted_commit: 6a10eab342a477846ab5f3e67f8ab17169f12807 diff --git a/.metodo/bin/metodo b/.metodo/bin/metodo new file mode 100644 index 0000000..aced463 --- /dev/null +++ b/.metodo/bin/metodo @@ -0,0 +1,262 @@ +#!/usr/bin/env bash +# metodo — CLI del Método. v0: sólo el linter `metodo check`. +# +# Modelo: bin/sync.sh --check de manabo-ui (raíl C11). Su anatomía, que copiamos: +# - UN solo script (nada de dos implementaciones que diverjan). +# - El testigo es una comprobación CRUDA del fichero real, no un hash guardado. +# - Veredicto DISCRETO por check, con la reparación impresa al lado. +# - Exit-code apto para CI: 0 si pasa, 1 si algo incumple. +# - Fallo benigno EXPLÍCITO (raíl 8): lo que no aplica se SALTA (SKIP), no falla. +# +# Cada AVISO cita la regla Cn de constitucion-de-facto.md que lo respalda. +# Uso: metodo check [ruta] (por defecto, el repo actual) + +set -uo pipefail # -u: variable sin definir es error · pipefail: un pipe hereda el fallo. + # (NO ponemos -e: los grep sin match devuelven 1 y no queremos abortar por eso.) + +ROOT="." +FAILS=0 +SELF_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/.." && pwd)" # raíz del repo del script + +# sha256 portable (Git Bash trae sha256sum; si no, shasum -a 256; si ninguno, la deriva no se mide) +_hash() { + if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" 2>/dev/null | awk '{print $1}' + elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" 2>/dev/null | awk '{print $1}' + else return 127; fi +} +_vendored_files() { printf '%s\n' "metodo.md" "bin/metodo"; } # lo que se estampa y se vigila +_write_checksums() { # $1 = dir .metodo del destino + local md="$1" f h; : > "$md/CHECKSUMS" + for f in $(_vendored_files); do + h=$(_hash "$md/$f") || { rm -f "$md/CHECKSUMS"; return 1; } + printf '%s %s\n' "$h" "$f" >> "$md/CHECKSUMS" + done +} +_is_matriz() { [ -f "$SELF_ROOT/constitucion-de-facto.md" ]; } # marca inequívoca de la matriz + +# --- veredictos (los cuatro colores del check) --- +hdr() { printf '\n\033[1m== %s ==\033[0m\n' "$*"; } +ok() { printf ' \033[32mok \033[0m %s\n' "$*"; } +bad() { printf ' \033[31mAVISO\033[0m %s\n' "$*"; FAILS=$((FAILS+1)); } +skip() { printf ' \033[33mSKIP\033[0m %s\n' "$*"; } + +# ── Check 1 · integridad de la copia vendorizada (C11) ─────────────────────── +# El máster vive en OTRO repo (la matriz), así que el testigo no es el máster sino el +# CHECKSUM sellado al distribuir (metodo init/update). Caza si alguien editó la copia a mano. +check_drift() { + hdr "integridad de la copia vendorizada (C11)" + local md="$ROOT/.metodo" + [ -f "$md/VERSION" ] || { skip "no hay .metodo/VERSION — repo sin el Método vendorizado"; return; } + local ver estado + ver=$(grep -E '^version:' "$md/VERSION" | sed 's/version:[[:space:]]*//') + estado=$(grep -E '^estado:' "$md/VERSION" | sed 's/estado:[[:space:]]*//') + ok "VERSION presente (version: ${ver:-?}, estado: ${estado:-?})" + if [ ! -f "$md/CHECKSUMS" ]; then + skip "sin CHECKSUMS — corre 'metodo init|update' para sellar la línea base de integridad" + return + fi + if ! _hash "$md/CHECKSUMS" >/dev/null 2>&1; then + skip "sin sha256sum/shasum disponible → no se puede comprobar la deriva" + return + fi + local rec f cur drift=0 + while read -r rec f; do + [ -n "$f" ] || continue + cur=$(_hash "$md/$f") + if [ -z "$cur" ]; then bad "falta la copia vendorizada: $f (C11)"; drift=$((drift+1)) + elif [ "$cur" != "$rec" ]; then bad "DERIVA: .metodo/$f editado a mano — nunca se edita la copia (C11); 'metodo update' o revierte"; drift=$((drift+1)); fi + done < "$md/CHECKSUMS" + [ "$drift" -eq 0 ] && ok "copia vendorizada íntegra (coincide con lo distribuido)" +} + +# ── Check 2 · colisión de ids en los ficheros de estado (C17) ──────────────── +check_ids() { + hdr "colisión de ids en los ficheros de estado (C17)" + local statefiles=( "$ROOT/doc/AVISOS.md" "$ROOT/doc/backlog.md" ) + local any=0 f dups id + for f in "${statefiles[@]}"; do + [ -f "$f" ] || continue + any=1 + # id DEFINIDO = al principio de una fila de tabla (|) o item de lista (-*) en negrita. + # Así una MENCIÓN inline ("ver D5") no cuenta como definición (evita falsos positivos). + dups=$(grep -oE '^[[:space:]]*[|*_-]+[[:space:]]*\**~*\**(D|A|G|H|C|R|F|E|P)[0-9]+' "$f" \ + | grep -oE '(D|A|G|H|C|R|F|E|P)[0-9]+' | sort | uniq -d) + if [ -n "$dups" ]; then + while IFS= read -r id; do + [ -n "$id" ] && bad "id duplicado en $(basename "$f"): $id — renombra o fusiona (C17)" + done <<< "$dups" + else + ok "$(basename "$f"): sin ids duplicados" + fi + done + [ "$any" -eq 0 ] && skip "sin doc/AVISOS.md ni doc/backlog.md (repo sin corpus de estado)" +} + +# ── Check 3 · encabezados sin marca de estado (C16) ────────────────────────── +check_headings() { + hdr "encabezados sin marca de estado (C16)" + local f hits=0 line + for f in "$ROOT"/*.md "$ROOT"/doc/*.md; do + [ -f "$f" ] || continue + # Un encabezado ## no lleva emoji de ESTADO (el estado vive en las casillas). + # ⚠️ se excluye a propósito: en un título es "bandera/aviso", no estado de sección. + while IFS= read -r line; do + bad "encabezado con estado en $(basename "$f"): ${line} — el estado va en las casillas (C16)" + hits=$((hits+1)) + done < <(grep -nE '^#{2,6} .*(✅|🟢|🔴|🟠|🟡|❌)' "$f") + done + [ "$hits" -eq 0 ] && ok "ningún encabezado ##+ lleva marca de estado" +} + +# ── Check 4 · enlaces locales resuelven (integridad del corpus) ────────────── +check_links() { + hdr "enlaces locales resuelven (integridad)" + local f dir tgt found=0 broken=0 + for f in "$ROOT"/*.md "$ROOT"/doc/*.md; do + [ -f "$f" ] || continue + dir=$(dirname "$f") + # process substitution (< <(...)) para que el while corra en ESTE shell y bad() cuente + # (un `grep | while` correría en un subshell y perdería el contador — raíl 1: el testigo + # tiene que medir de verdad, no en un proceso que se traga el resultado). + while IFS= read -r tgt; do + case "$tgt" in http*|\#*|mailto:*|"") continue;; esac + tgt="${tgt%%#*}" # quita el ancla #seccion + [ -z "$tgt" ] && continue + found=$((found+1)) + [ -e "$dir/$tgt" ] || { bad "enlace roto en $(basename "$f"): $tgt"; broken=$((broken+1)); } + done < <(grep -oE '\]\([^)]+\)' "$f" | sed -E 's/^\]\(//; s/\)$//') + done + [ "$broken" -eq 0 ] && ok "$found enlaces locales, todos resuelven" +} + +# ── Check 5 · commits Conventional DESDE LA ADOPCIÓN (C25) ─────────────────── +# Sólo se juzga lo NUEVO: la adopción es brownfield HACIA ADELANTE, el historial previo no se +# reescribe → no se juzga (juzgarlo dejaría un repo brownfield rojo para siempre — raíl 5/C35). +# El punto de adopción se sella en .metodo/VERSION (adopted_commit); sin sello = greenfield → todo. +check_commits() { + hdr "commits Conventional desde la adopción (C25)" + git -C "$ROOT" rev-parse --git-dir >/dev/null 2>&1 || { skip "no es un repo git"; return; } + local since range n s bad_c=0 + since=$(grep -E '^adopted_commit:' "$ROOT/.metodo/VERSION" 2>/dev/null | sed 's/adopted_commit:[[:space:]]*//') + if [ -n "$since" ] && git -C "$ROOT" rev-parse --verify -q "$since^{commit}" >/dev/null 2>&1; then + range="${since}..HEAD" + else + range="HEAD"; since="" # sin marcador: repo nuevo → se juzga todo el historial + fi + n=$(git -C "$ROOT" rev-list --count $range 2>/dev/null || echo 0) + [ "${n:-0}" -eq 0 ] && { skip "sin commits nuevos desde la adopción (nada que revisar)"; return; } + while IFS= read -r s; do + printf '%s' "$s" | grep -qE '^(feat|fix|docs|ci|refactor|test|chore|build|perf)(\(.+\))?!?: .' \ + || { bad "commit no-Conventional: \"$s\" (C25)"; bad_c=$((bad_c+1)); } + done < <(git -C "$ROOT" log --format=%s $range 2>/dev/null) + [ "$bad_c" -eq 0 ] && ok "los ${n} commit(s) desde la adopción siguen Conventional" +} + +# ── plantillas de doc/ (sólo se crean si faltan — brownfield: no pisar) ─────── +_scaffold_docs() { # $1 = repo destino + # Si el repo ya tiene casa de docs propia (doc/ o docs/), NO scaffoldear: no duplicar estructura. + { [ -d "$1/doc" ] || [ -d "$1/docs" ]; } && { echo " (doc/ o docs/ ya existe → no scaffoldeo el corpus)"; return 0; } + local d="$1/doc"; mkdir -p "$d" + [ -f "$d/backlog.md" ] || cat > "$d/backlog.md" <<'EOF' +# Backlog + +> Tres capas: **este backlog** = estado (metas + decisiones cerradas `D` + items) · +> AVISOS.md = bandera (se lee primero) · bitacora.md = relato append-only. +> Método vendorizado en ../.metodo/metodo.md. ⛔ Las `D` son inmutables. Un `##` no lleva marca de +> estado; abierto = tiene `[ ]`. + +## Decisiones cerradas + +## Abierto +- [ ] (siembra aquí el trabajo ABIERTO de hoy — brownfield: el pasado no se reconstruye) +EOF + [ -f "$d/AVISOS.md" ] || cat > "$d/AVISOS.md" <<'EOF' +# ⚠️ AVISOS + +**Lo que hay que saber el primer día.** Se lee antes que el backlog. Cada aviso dice quién lo cierra. + +| # | Qué | Estado | Quién lo cierra | +|---|---|---|---| +EOF + [ -f "$d/bitacora.md" ] || cat > "$d/bitacora.md" <<'EOF' +# Bitácora + +*Relato append-only. Lo nuevo va ABAJO. Se lee por fecha.* + +--- +EOF +} + +# ── metodo init — vendoriza el Método a un repo por primera vez ──────── +cmd_init() { + local tgt="${1:-.}" + _is_matriz || { echo "metodo init: sólo se corre desde la MATRIZ, no desde una copia vendorizada."; exit 2; } + [ -d "$tgt" ] || { echo "metodo init: no existe el destino: $tgt"; exit 2; } + local md="$tgt/.metodo" + [ -e "$md" ] && { echo "metodo init: $tgt ya tiene .metodo/ — usa 'metodo update $tgt'."; exit 2; } + mkdir -p "$md/bin" + cp "$SELF_ROOT/metodo.md" "$md/metodo.md" + cp "$SELF_ROOT/bin/metodo" "$md/bin/metodo"; chmod +x "$md/bin/metodo" 2>/dev/null || true + local mver adopt + mver=$(grep -E '^version:' "$SELF_ROOT/.metodo/VERSION" 2>/dev/null | sed 's/version:[[:space:]]*//') + adopt=$(git -C "$tgt" rev-parse HEAD 2>/dev/null || echo "") # marca de adopción brownfield + { echo "# Copia vendorizada del Método (raíl C11). Gestionada por 'metodo init/update'." + echo "master: metodo"; echo "version: ${mver:-?}"; echo "estado: vendored" + [ -n "$adopt" ] && echo "adopted_commit: $adopt"; } > "$md/VERSION" + _write_checksums "$md" || echo " (aviso: sin sha256 → CHECKSUMS no escrito; la deriva no se medirá)" + printf '* text eol=lf\n' > "$md/.gitattributes" # un clone en Windows no mete CRLF ni falsea el checksum + [ -f "$md/realimentacion.md" ] || cat > "$md/realimentacion.md" < — re-estampa el máster+linter en un repo ya iniciado ─ +cmd_update() { + local tgt="${1:-.}" + _is_matriz || { echo "metodo update: sólo se corre desde la MATRIZ."; exit 2; } + local md="$tgt/.metodo" + [ -d "$md" ] || { echo "metodo update: $tgt no tiene .metodo/ — usa 'metodo init $tgt'."; exit 2; } + cp "$SELF_ROOT/metodo.md" "$md/metodo.md" + cp "$SELF_ROOT/bin/metodo" "$md/bin/metodo"; chmod +x "$md/bin/metodo" 2>/dev/null || true + local mver adopt + mver=$(grep -E '^version:' "$SELF_ROOT/.metodo/VERSION" 2>/dev/null | sed 's/version:[[:space:]]*//') + adopt=$(grep -E '^adopted_commit:' "$md/VERSION" 2>/dev/null | sed 's/adopted_commit:[[:space:]]*//') # se preserva + { echo "# Copia vendorizada del Método (raíl C11). Gestionada por 'metodo init/update'." + echo "master: metodo"; echo "version: ${mver:-?}"; echo "estado: vendored" + [ -n "$adopt" ] && echo "adopted_commit: $adopt"; } > "$md/VERSION" + _write_checksums "$md" || echo " (aviso: sin sha256 → CHECKSUMS no escrito)" + printf '* text eol=lf\n' > "$md/.gitattributes" + echo "metodo update: $tgt actualizado al máster $mver (metodo.md + linter, byte a byte); realimentacion.md y adopted_commit preservados." +} + +cmd_check() { + printf 'metodo check — %s\n' "$ROOT" + check_drift + check_ids + check_headings + check_links + check_commits + hdr "resultado" + if [ "$FAILS" -eq 0 ]; then + printf ' \033[32mPASA el Método\033[0m (0 avisos)\n'; exit 0 + else + printf ' \033[31m%d aviso(s) — el Método NO pasa\033[0m\n' "$FAILS"; exit 1 + fi +} + +case "${1:-}" in + check) ROOT="${2:-.}"; cmd_check ;; + init) cmd_init "${2:-.}" ;; + update) cmd_update "${2:-.}" ;; + harvest) printf "metodo harvest: aún no implementado (recogerá los .metodo/realimentacion.md al buzón de la matriz).\n"; exit 2 ;; + ""|help|-h|--help) printf 'uso: metodo [ruta]\n check linter · init vendoriza a un repo nuevo · update re-estampa (desde la matriz)\n'; exit 2 ;; + *) printf "metodo: subcomando desconocido: %s\n" "$1"; exit 2 ;; +esac diff --git a/.metodo/metodo.md b/.metodo/metodo.md new file mode 100644 index 0000000..1efafbf --- /dev/null +++ b/.metodo/metodo.md @@ -0,0 +1,106 @@ +# El Método — constitución + +> **Versión 2026-08-08.** Documento **autocontenido**: es lo que se vendoriza, byte a byte, a cada +> repo (no lleva enlaces a rutas de un árbol concreto, para que funcione clonado en solitario). +> Cada regla lleva su **cicatriz** (meta-principio: un raíl se escribe con el incidente que lo +> enseñó, no a priori). Procedencia, fuerza (nº de repos) y partición linter/juicio: fichero +> `constitucion-de-facto.md` de la matriz. +> +> ℹ️ Los 10 raíles base van hoy **en una línea** (su texto completo con cicatrices está pendiente de +> inlinar); lo promovido de la cosecha 2026-08-08 va **completo**. El árbol `redes` conserva su copia +> `redes/workflows/metodo.md` hasta que se desmantele. + +## Raíles base (1–10) + +1. Verifica el **RESULTADO**, no la intención (testigo independiente que sabe ponerse rojo). +2. El dato **CRUDO**, no el cocinado (backend mide, consumidor pinta). +3. **Mutation testing**: ataca el CABLEADO, no las funciones puras. +4. Ejercita el **FLUJO REAL** (abre el navegador, toca el device). +5. No inventes estados ni fallos **no medidos** ("sin dato", degrada a gris no a rojo). +6. Verifica la **PREMISA** del prompt (enmendar es SUSTITUIR; premisa falsa = PARAR). **← +C34** +7. **Fuente única** + copias vendidas + red anti-deriva (`--check`). +8. **Fallo benigno EXPLÍCITO**, nunca degradación silenciosa. +9. **Sesiones frescas** por stack; la continuidad la lleva el orquestador. +10. Lo que **"funciona por casualidad"** está roto. + +--- + +## Raíles nuevos (promovidos 2026-08-08) + +### 11. El orden de lo irreversible: añadir antes de quitar · *C14 (4 fuentes)* + +Todo lo verificable ocurre **antes** del paso que no se deshace. Se **añade antes de quitar**, se +crea antes de borrar; cada paso de una release se revierte por separado y ninguno depende de dos +cosas nuevas a la vez. + +- Un tercer nodo de etcd entra **antes** de que salga el que se retira — al revés, el quórum se + queda en uno. +- "Nunca borres el `IPAddressPool` viejo hasta tener el nuevo sirviendo" (MetalLB). +- El cutover de un servicio con nombre: primero responde el nuevo, luego se retira el viejo. + +### 12. Exploración solo-lectura, sin autoengaño · *C35 (Solidaria)* + +Explorar es **medir sin tocar, y sin creerte tu propia medida**. + +- `curl` **con control**: mira `/api/health`/`http_code`, no "parece que responde". +- **"Aquí no hay nada" es un RESULTADO**, no un fracaso — se anota igual que un hallazgo. +- Los **falsos positivos de `grep` son el riesgo nº1**: una lista larga de "roto" delata TU método + (un patrón demasiado amplio), no el sistema. Verifica cada hit contra la fuente antes de declararlo. +- Medir, no opinar. Sondear una escritura **ES** escribir (no es exploración). + +--- + +## Entrega — la parte de "sacar el trabajo" (promovido 2026-08-08) + +El método nació de verificación/UI; la cicatriz de **entrega** (git, secretos, naming, deploy) vivía +fuera, repetida en varios repos. Aquí se consolida. + +### Naming · *C17, C32* +- **Ids con prefijo por ámbito**, **no se renumeran** (romperían citas cruzadas), y **antes de + añadir uno se corre el grep de colisión** — no basta con mirar. +- El nombre nombra **DESTINOS, no rutas**: ni el host, ni la IP, ni activo/reserva entran en un FQDN. + Se escribe como **regla**, no como observación. +- **Renombrar sin CNAME de cortesía**: que lo viejo rompa a la vista, no en silencio. + +### Secretos · *C20, C21, C22* +- **Una frontera de credenciales por dominio**; aislamiento por **ServiceAccount** (ninguno con + acceso a secretos ajenos). +- **Capacidad > credencial**: se **actúa** con el secreto, no se **revela** (revelar lo deja en el + transcript); el estado raro se dice con la URL, nunca con el secreto. +- **Nunca en git**: imperativos / GPG / PushSecret, forzado por `.gitignore`; se documenta **cómo + recrearlos**, no el valor. + +### Git · *C24, C25, C38* +- **Git canónico único y MEDIDO**: se empuja a un remoto y solo ahí; un fallo se **mide** + (dry-run/reintento) antes de concluir "no hay credenciales"; el remoto muerto se elimina localmente. +- **Commits Conventional** (`feat/fix/docs/ci` + scope); el `doc:` registra el **porqué**. +- **Working-tree compartido**: `git add` con ruta explícita (**nunca `-A`**), `git pull` antes de + deploy y antes de commitear docs, una tarea = una sesión/worktree. + +### Deploy · *C12, C13* +- **Un solo flujo de build**, disparado **deliberadamente**, no por `git push`; el legacy es + **lápida** (código muerto conservado hasta MEDIR qué lo ejecuta, no borrado a ciegas). +- Todo cambio **por git → deploy declarativo**; imagen pineada por **DIGEST inmutable commiteado**; + el historial de commits ES el registro de auditoría. +- El **orden de lo irreversible** aplica aquí → **raíl 11**. + +--- + +## Adiciones a raíles base (pendientes de inlinar en su raíl) + +- **R6 (verifica la premisa) += C34**: las premisas del encargo se **numeran** (`P1`,`P2`…) con + veredicto explícito (verdadera / falsa / matizada + `fichero:línea`); el **ratio de premisas + falsas** es el termómetro de "escribo de memoria"; **"el doc lo dice" = premisa a verificar**. +- **Diseño de herramientas (R1/R8) += C39**: la **ausencia de una capacidad es un guardarraíl**. El + poder peligroso se **retiene estructuralmente** (no se expone porque no se construye): vocabulario + cerrado, sin `exec`/SQL/comando libre. +- **Docs / R7 += C30**: el **docstring/manual ES la interfaz**. Cambia el comportamiento → cambia el + docstring en el **mismo commit**; un doc que miente **ES el bug**. + +--- + +## La estructura de sesión no vive aquí + +Los dos ejes **fase × persona** y el **handoff automático** entre fases son **procedimiento**, no +principio → viven en la capa `workflows/`, con la capa **persona opcional** (se activa solo con +interlocutor no-técnico; en infra en solitario, inactiva pero documentada). diff --git a/.metodo/realimentacion.md b/.metodo/realimentacion.md new file mode 100644 index 0000000..1585f3b --- /dev/null +++ b/.metodo/realimentacion.md @@ -0,0 +1,6 @@ +# Realimentación del Método — repo: smtp-relay + +*Log append-only local, viaja con el repo. Anota cuando trabajar aquí enseñe algo del Método. +Etiquetas: [NUEVA-REGLA] · [REFINA Cn] · [Cn-NO-ENCAJA] · [FALSA]. Se cosecha con 'metodo harvest'.* + +--- diff --git a/doc/AVISOS.md b/doc/AVISOS.md new file mode 100644 index 0000000..a95184b --- /dev/null +++ b/doc/AVISOS.md @@ -0,0 +1,6 @@ +# ⚠️ AVISOS + +**Lo que hay que saber el primer día.** Se lee antes que el backlog. Cada aviso dice quién lo cierra. + +| # | Qué | Estado | Quién lo cierra | +|---|---|---|---| diff --git a/doc/backlog.md b/doc/backlog.md new file mode 100644 index 0000000..ce9d5ae --- /dev/null +++ b/doc/backlog.md @@ -0,0 +1,11 @@ +# Backlog + +> Tres capas: **este backlog** = estado (metas + decisiones cerradas `D` + items) · +> AVISOS.md = bandera (se lee primero) · bitacora.md = relato append-only. +> Método vendorizado en ../.metodo/metodo.md. ⛔ Las `D` son inmutables. Un `##` no lleva marca de +> estado; abierto = tiene `[ ]`. + +## Decisiones cerradas + +## Abierto +- [ ] (siembra aquí el trabajo ABIERTO de hoy — brownfield: el pasado no se reconstruye) diff --git a/doc/bitacora.md b/doc/bitacora.md new file mode 100644 index 0000000..032c41d --- /dev/null +++ b/doc/bitacora.md @@ -0,0 +1,5 @@ +# Bitácora + +*Relato append-only. Lo nuevo va ABAJO. Se lee por fecha.* + +---