docs(metodo): re-estampado al máster 2026-08-22 — cuarta capa del corpus

metodo.md describe las CUATRO capas (backlog · decisiones · AVISOS · bitácora)
y la regla de refinement de D7; el linter estampa doc/decisiones.md en los init
nuevos y gana tres checks de la capa de decisiones.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
sirxavor 2026-08-22 09:59:48 +02:00
parent db9d69d119
commit a901586bab
4 changed files with 177 additions and 17 deletions

View File

@ -1,2 +1,2 @@
37cd8e9792f5cad5e57827068fe6cb00ca5602eca7692ef2ca943a48806bc8a8 metodo.md 140cf44bc4d8e9eff08cd4f6466df22c897efa2c6c6f83662a747e3348a4f2bd metodo.md
22f810404cd02141177066918e66e98ba208c8529fe5ad92cdd66ccb98873ee5 bin/metodo bf2144db0bb987bd32ce7eb8d99021ca0d29cb7b6a48d30a8ffd0fe723ca2a3f bin/metodo

View File

@ -1,5 +1,5 @@
# Copia vendorizada del Método (raíl C11). Gestionada por 'metodo init/update'. # Copia vendorizada del Método (raíl C11). Gestionada por 'metodo init/update'.
master: metodo master: metodo
version: 2026-08-19 version: 2026-08-22
estado: vendored estado: vendored
adopted_commit: 6a10eab342a477846ab5f3e67f8ab17169f12807 adopted_commit: 6a10eab342a477846ab5f3e67f8ab17169f12807

View File

@ -16,6 +16,7 @@ set -uo pipefail # -u: variable sin definir es error · pipefail: un pipe here
ROOT="." ROOT="."
FAILS=0 FAILS=0
WARNS=0 # notas: se cuentan y se dicen, pero NO mueven el código de salida (ver check 8)
SELF_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/.." && pwd)" # raíz del repo del script 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) # sha256 portable (Git Bash trae sha256sum; si no, shasum -a 256; si ninguno, la deriva no se mide)
@ -34,11 +35,12 @@ _write_checksums() { # $1 = dir .metodo del destino
} }
_is_matriz() { [ -f "$SELF_ROOT/constitucion-de-facto.md" ]; } # marca inequívoca de la matriz _is_matriz() { [ -f "$SELF_ROOT/constitucion-de-facto.md" ]; } # marca inequívoca de la matriz
# --- veredictos (los cuatro colores del check) --- # --- veredictos (los cinco colores del check) ---
hdr() { printf '\n\033[1m== %s ==\033[0m\n' "$*"; } hdr() { printf '\n\033[1m== %s ==\033[0m\n' "$*"; }
ok() { printf ' \033[32mok \033[0m %s\n' "$*"; } ok() { printf ' \033[32mok \033[0m %s\n' "$*"; }
bad() { printf ' \033[31mAVISO\033[0m %s\n' "$*"; FAILS=$((FAILS+1)); } bad() { printf ' \033[31mAVISO\033[0m %s\n' "$*"; FAILS=$((FAILS+1)); }
skip() { printf ' \033[33mSKIP\033[0m %s\n' "$*"; } skip() { printf ' \033[33mSKIP\033[0m %s\n' "$*"; }
warn() { printf ' \033[33mnota\033[0m %s\n' "$*"; WARNS=$((WARNS+1)); }
# ── Check 1 · integridad de la copia vendorizada (C11) ─────────────────────── # ── 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 # El máster vive en OTRO repo (la matriz), así que el testigo no es el máster sino el
@ -177,6 +179,93 @@ check_pointer() {
[ "$broken" -eq 0 ] && ok "el destino del puntero resuelve ($found enlace(s) comprobado(s))" [ "$broken" -eq 0 ] && ok "el destino del puntero resuelve ($found enlace(s) comprobado(s))"
} }
# ── Check 7 · la capa de DECISIONES: ids únicos (rojo) y campos (nota) ───────
# Cuarta capa del corpus (`D6`, 2026-08-22). Existe porque una decisión guardada en el backlog se
# numera por FASE, y la numeración por fase colisiona: 18 decisiones distintas llamadas `D1` en un
# solo backlog.md. Por eso el id duplicado va en ROJO y no en nota — es AMBIGÜEDAD DE CITA sobre lo
# peligroso del repo: un nombre que apunta a dos decisiones distintas hace que citar una gobierne
# con la otra. Los CAMPOS van en nota: un repo que acaba de estrenar la capa no se pone rojo por
# terminar de rellenarla.
# Una entrada se DEFINE con un encabezado `### <id> — <título>`, y el id es el primer token: así una
# mención inline ("ver D107") no define nada y el testigo no puede dar falsos positivos.
check_decisiones() {
hdr "capa de decisiones · ids y campos (C17 · D6)"
local f="$ROOT/doc/decisiones.md"
[ -f "$f" ] || { skip "sin doc/decisiones.md — la cuarta capa se adopta cuando la orquestadora del repo migre sus 'D' (D6)"; return; }
local ids n dups id
ids=$(awk '/^### /{ s=$0; gsub(/\*/,"",s); split(s,a,"[ \t]+"); if (a[2]!="") print a[2] }' "$f")
n=$(printf '%s\n' "$ids" | grep -c '[^[:space:]]')
if [ "$n" -eq 0 ]; then
warn "doc/decisiones.md no tiene NINGUNA entrada '### <id> — <título>' — un registro vacío no gatea nada (D6)"
return
fi
dups=$(printf '%s\n' "$ids" | sort | uniq -d)
if [ -n "$dups" ]; then
while IFS= read -r id; do
[ -n "$id" ] && bad "id duplicado en decisiones.md: $id — dos decisiones con el mismo nombre: citar una gobierna con la otra (C17); renumera la NUEVA desde el contador del repo"
done <<< "$dups"
else
ok "decisiones.md: $n entrada(s), sin ids duplicados"
fi
# Campos obligatorios de cada entrada: dueño (Xavier / técnica — una técnica escalada como suya
# ya coló) y estado (vigente / sustituida — sin él, una decisión muerta sigue mandando).
local faltan miss
faltan=$(awk '
function flush( m){ if (id!="") { m=""; if(!hd) m=m" dueño"; if(!he) m=m" estado";
if (m!="") print id "\t" m } id=""; hd=0; he=0 }
/^### / { flush(); s=$0; gsub(/\*/,"",s); split(s,a,"[ \t]+"); id=a[2]; next }
/^#{1,2} / { flush(); next }
{ if ($0 ~ /\*\*due/) hd=1
if ($0 ~ /\*\*estado/) he=1 }
END { flush() }' "$f")
if [ -n "$faltan" ]; then
while IFS=$'\t' read -r id miss; do
[ -n "$id" ] && warn "decisiones.md · «$id» sin:$miss — el dueño y el estado son el gate de la entrada (D6)"
done <<< "$faltan"
else
ok "todas las entradas llevan dueño y estado"
fi
}
# ── Check 8 · textos de DECISIÓN dentro del backlog (nota) ───────────────────
# Invierte el gradiente de comodidad: escribir la decisión en el sitio equivocado pasa a ser lo que
# cuesta. Se mide el BLOQUE —la línea que define `Dn` más sus líneas de continuación indentadas—;
# más de 3 líneas ya es un cuerpo de decisión, no una referencia.
# ⚠️ CALIBRADO para no enrojecer a los backlogs legacy que aún no han migrado, y SÍ se pueden
# distinguir: los ids nuevos nacen en `D100` (rango limpio — ningún legacy del árbol llega a 100).
# ⇒ `Dn` con n>=100 y cuerpo = decisión escrita HOY en el sitio equivocado → nota nominal.
# ⇒ `Dn` con n<100 = deuda de migración de la orquestadora de ese repo → se CUENTA y se dice, no se
# juzga. Y ninguno de los dos mueve el código de salida a propósito: la capa se estrena hoy y
# romper el gate de 21 repos por deuda ajena es cómo se aprende a apagar el gate.
check_decisiones_en_backlog() {
hdr "textos de decisión dentro del backlog (D6)"
local f="$ROOT/doc/backlog.md"
[ -f "$f" ] || { skip "sin doc/backlog.md (repo sin corpus de estado)"; return; }
local out legacy=0 nuevos=0 id len num
out=$(awk '
function flush(){ if (id!="" && len>3) print id "\t" len; id=""; len=0 }
/^[[:space:]]*[-*|_]+[[:space:]]*[*~`]*D[0-9]+/ {
flush(); match($0, /D[0-9]+/); id=substr($0, RSTART, RLENGTH); len=1; next }
/^[[:space:]]+[^[:space:]]/ { if (id!="") { len++; next } }
{ flush() }
END { flush() }' "$f")
if [ -n "$out" ]; then
while IFS=$'\t' read -r id len; do
[ -n "$id" ] || continue
num=${id#D}
if [ "$num" -ge 100 ] 2>/dev/null; then
nuevos=$((nuevos+1))
warn "«$id» lleva $len líneas de texto de decisión dentro de backlog.md — las decisiones viven en doc/decisiones.md (D6)"
else
legacy=$((legacy+1))
fi
done <<< "$out"
fi
[ "$legacy" -gt 0 ] && ok "$legacy texto(s) de decisión con id legacy (<D100) en backlog.md: migran cuando su orquestadora migre (D6) — las NUEVAS ya no se escriben aquí"
[ "$nuevos" -eq 0 ] && [ "$legacy" -eq 0 ] && ok "ninguna decisión con cuerpo dentro de backlog.md"
return 0
}
# ── plantillas de doc/ (sólo se crean si faltan — brownfield: no pisar) ─────── # ── plantillas de doc/ (sólo se crean si faltan — brownfield: no pisar) ───────
_scaffold_docs() { # $1 = repo destino _scaffold_docs() { # $1 = repo destino
# La casa de docs es UNA y se llama doc/ (el linter ancla ahí el corpus de estado). # La casa de docs es UNA y se llama doc/ (el linter ancla ahí el corpus de estado).
@ -190,15 +279,55 @@ _scaffold_docs() { # $1 = repo destino
[ -f "$d/backlog.md" ] || cat > "$d/backlog.md" <<'EOF' [ -f "$d/backlog.md" ] || cat > "$d/backlog.md" <<'EOF'
# Backlog # Backlog
> Tres capas: **este backlog** = estado (metas + decisiones cerradas `D` + items) · > Cuatro capas: **este backlog** = el eje del TROCEO (metas + items; un item ≈ una sesión) ·
> AVISOS.md = bandera (se lee primero) · bitacora.md = relato append-only. > decisiones.md = lo peligroso, todo junto · AVISOS.md = bandera (se lee primero) ·
> Método vendorizado en ../.metodo/metodo.md. ⛔ Las `D` son inmutables. Un `##` no lleva marca de > bitacora.md = relato append-only. Método vendorizado en ../.metodo/metodo.md.
> ⛔ Las decisiones son **inmutables**: contradecir = PARAR y reportar. Un `##` no lleva marca de
> estado; abierto = tiene `[ ]`. > estado; abierto = tiene `[ ]`.
## Decisiones cerradas ## Decisiones
Viven en **[decisiones.md](decisiones.md)** (cuarta capa, `D6`). Aquí se **citan por id**, no se
copian: escribirlas en los dos sitios es crear la deriva que §7 existe para impedir.
## Abierto ## Abierto
- [ ] (siembra aquí el trabajo ABIERTO de hoy — brownfield: el pasado no se reconstruye) - [ ] (siembra aquí el trabajo ABIERTO de hoy — brownfield: el pasado no se reconstruye)
EOF
[ -f "$d/decisiones.md" ] || cat > "$d/decisiones.md" <<'EOF'
# Decisiones
> **Cuarta capa del corpus** (`D6`, Xavier 2026-08-22): backlog = el eje del troceo ·
> **este fichero = lo peligroso, TODO JUNTO** · AVISOS.md = bandera, se lee primero ·
> bitacora.md = relato append-only. Los raíles, en ../.metodo/metodo.md (§7: el destino se decide
> UNA vez).
>
> ⛔ **Una decisión `vigente` no se contradice**: o **PARAS y reportas**, o la **sustituyes**
> explícitamente — y la sustitución **edita la entrada vieja** (⚰️ + puntero a la nueva), nunca
> deja las dos vivas.
> ⛔ **Antes de cerrar nada, recorre el ámbito y mira QUÉ CONTRADICES.** Ésa es la fuerza que
> mantiene vivo este documento: no hay gate automático que la sustituya.
>
> **Orden: por ÁMBITO, no por fase** — numerar por fase es lo que fabricó **18 decisiones distintas
> llamadas `D1`** en un solo backlog. **Ids nuevos: contador único del repo desde `D100`**,
> monótono, jamás reiniciado por sección. Los legacy migran con su **id INTACTO**, citados
> `fase·Dn` (p. ej. `I11·D18`): C17 prohíbe renumerar, y renumerar rompe las citas cruzadas.
>
> **Formato**: una entrada = un `### <id> — <título>`. El id es el primer token (es lo que lee
> `metodo check`). Los campos **dueño** y **estado** son el gate de la entrada.
## Ámbito: (nombre del ámbito — p. ej. "transporte", "identidad", "panel")
### D100 — (título corto, sin punto final)
- **dueño**: Xavier · **fecha**: 2026-01-01 · **estado**: vigente
- **sustituye**: — · **testigo**: — *(si la decisión AFIRMA algo medible, el testigo y su condición
van AQUÍ, en esta misma línea: la clase de fallo del «VALIDADO» con su condición 116 líneas
más abajo)*
(Texto **ÍNTEGRO** de la decisión, en las palabras en que se tomó. **Nunca un resumen**: un resumen
adquiere invariantes que la decisión no tiene, y ya gobernó una sesión entera en lugar de la `D`.
Sustituye este ejemplo por tu primera decisión real — si dejas el `D100` puesto, la primera de
verdad colisiona con él y `metodo check` se pone rojo.)
EOF EOF
[ -f "$d/AVISOS.md" ] || cat > "$d/AVISOS.md" <<'EOF' [ -f "$d/AVISOS.md" ] || cat > "$d/AVISOS.md" <<'EOF'
# ⚠️ AVISOS # ⚠️ AVISOS
@ -306,7 +435,7 @@ Etiquetas: [NUEVA-REGLA] · [REFINA Cn] · [Cn-NO-ENCAJA] · [FALSA]. Se cosecha
EOF EOF
_scaffold_docs "$tgt" _scaffold_docs "$tgt"
_stamp_pointer "$tgt" _stamp_pointer "$tgt"
echo "metodo init: $tgt inicializado — .metodo/ (máster $mver) + doc/ de tres capas." echo "metodo init: $tgt inicializado — .metodo/ (máster $mver) + doc/ de CUATRO capas (backlog · decisiones · AVISOS · bitácora)."
echo " siguiente: siembra doc/backlog.md con el trabajo abierto de hoy y corre 'metodo check .'" echo " siguiente: siembra doc/backlog.md con el trabajo abierto de hoy y corre 'metodo check .'"
} }
@ -337,12 +466,16 @@ cmd_check() {
check_headings check_headings
check_links check_links
check_commits check_commits
check_decisiones
check_decisiones_en_backlog
check_pointer check_pointer
hdr "resultado" hdr "resultado"
local nota=""
[ "$WARNS" -gt 0 ] && nota=$(printf ' · %d nota(s) — no mueven el código de salida, pero se cuentan' "$WARNS")
if [ "$FAILS" -eq 0 ]; then if [ "$FAILS" -eq 0 ]; then
printf ' \033[32mPASA el Método\033[0m (0 avisos)\n'; exit 0 printf ' \033[32mPASA el Método\033[0m (0 avisos%s)\n' "$nota"; exit 0
else else
printf ' \033[31m%d aviso(s) — el Método NO pasa\033[0m\n' "$FAILS"; exit 1 printf ' \033[31m%d aviso(s) — el Método NO pasa\033[0m%s\n' "$FAILS" "$nota"; exit 1
fi fi
} }

View File

@ -4,7 +4,7 @@ Principios que valen para **cualquier** sesión (exploración, ejecución, despl
incidente, orquestación) y para cualquier backlog. Los workflows concretos referencian este incidente, orquestación) y para cualquier backlog. Los workflows concretos referencian este
doc en vez de repetirlo. No son teoría: cada uno lleva el caso real que lo enseñó. doc en vez de repetirlo. No son teoría: cada uno lleva el caso real que lo enseñó.
> **Versión 2026-08-19.** Documento **autocontenido**: es lo que se vendoriza, byte a byte, a cada > **Versión 2026-08-22.** 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). > 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 > 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 > enseñó, no a priori). Procedencia, fuerza (nº de repos) y partición linter/juicio: fichero
@ -105,6 +105,15 @@ foco**, que es el recurso escaso. ⇒ Lo que convierte esto en método y no en e
la mejora se **mide** y lo que queda fuera se **escribe** (un ⏸️ con su motivo, como el aplazamiento la mejora se **mide** y lo que queda fuera se **escribe** (un ⏸️ con su motivo, como el aplazamiento
de la captura de hub), para que nadie lo confunda con un olvido. de la captura de hub), para que nadie lo confunda con un olvido.
⛔ **Y el refinement que lo hace ejecutable: una decisión que acarrea trabajo FUERA del alcance del
producto o de la fase NO se queda como casilla.** Se convierte en **épica nueva** si es grande, o se
va **a su fase** si tiene una — y se prioriza como todo lo demás: **por valor de producto**, no por
lo bien argumentada que esté. *(Xavier, `D7`, 2026-08-22. Estrenada el mismo día: `I11.11` salió de
su ficha a una épica propia **con su valor declarado — cero, porque no hay nadie esperándola**, que
es justo el dato que una casilla escondida dentro de otra ficha no da.)*
⇒ Es el mismo movimiento que el del ámbito de amenaza, una capa más arriba: **lo que no es de aquí
no se descarta ni se cuela — se muda, con su valor escrito.**
## 1. Verifica el resultado, no la intención ## 1. Verifica el resultado, no la intención
Comprueba lo que el sistema **sirve/hace**, no lo que su fuente **dice** que hará. Una señal Comprueba lo que el sistema **sirve/hace**, no lo que su fuente **dice** que hará. Una señal
@ -920,11 +929,29 @@ copia pura.)*
`sesion-ordenacion-docs.md` (capa `workflows/`)— pero vivía **en el workflow de la sesión `sesion-ordenacion-docs.md` (capa `workflows/`)— pero vivía **en el workflow de la sesión
que LIMPIA, no en el de las que ESCRIBEN**, así que se incumplía sistemáticamente y se pagaba que LIMPIA, no en el de las que ESCRIBEN**, así que se incumplía sistemáticamente y se pagaba
después en una pasada. Por eso está aquí ahora. después en una pasada. Por eso está aquí ahora.
**Al escribir, decide el destino UNA vez**, con el test del workflow: *¿una sesión futura necesita **Al escribir, decide el destino UNA vez**, y los destinos son **CUATRO**: el corpus de un repo
esto para NO contradecir una decisión?* → **backlog**. *¿Es cómo nos enteramos —lo medido, las tiene **cuatro capas**, no tres. *¿Es una decisión cerrada, de las que otra sesión no puede
predicciones, los mutantes, las premisas falsas, el relato—?* → **bitácora**, y en el backlog **un contradecir sin PARAR?* → **`doc/decisiones.md`**, con su texto **íntegro**. *¿Es trabajo — un item
puntero fechado**. ⭐ Escribirlo en los dos **no es prudencia: es crear la deriva** que §7 existe que cabe en una sesión?* → **`doc/backlog.md`**, que es **el eje del troceo**. *¿Es lo que hay que
para impedir, porque el día que uno de los dos se corrija el otro seguirá ahí. saber el primer día?* → **`doc/AVISOS.md`**, que se lee antes que el backlog. *¿Es cómo nos
enteramos —lo medido, las predicciones, los mutantes, las premisas falsas, el relato—?* →
**`doc/bitacora.md`**, append-only, y en el backlog **un puntero fechado**. ⭐ Escribirlo en dos
**no es prudencia: es crear la deriva** que §7 existe para impedir, porque el día que uno de los dos
se corrija el otro seguirá ahí.
⛔ **Y la cuarta capa existe porque una decisión guardada en el backlog se numera POR FASE, y la
numeración por fase colisiona.** *(Cicatriz 2026-08-22, `D6` de Xavier: **18 decisiones distintas
llamadas `D1`** en un solo `backlog.md` — y cuatro fallos de una semana que salen de ahí: un
**resumen** que gobernó en lugar de la decisión, un «precio aceptado» que Xavier nunca pensó, dos
ids que colisionaban, y una sustitución con las **dos versiones vivas** a 90 líneas de distancia.
En sus palabras, para qué sirve la capa: «así tienes todo lo peligroso en un único documento».)*
⇒ Por eso se ordena **por ámbito, no por fase**; cada entrada lleva **texto íntegro** (nunca un
resumen — un resumen adquiere invariantes que la decisión no tiene), **dueño visible** (*Xavier* o
*técnica*: una técnica escalada como suya ya coló), **estado**, y el **testigo en la misma línea**
si afirma algo medible (§1). Una sustitución **edita la entrada vieja** —⚰️ y puntero—, jamás deja
las dos vivas.
⇒ Y «todo lo peligroso en un único documento» solo protege si **alguien lo recorre** (§8): toda
sesión de diseño mira ahí **qué contradice** antes de cerrar nada, y si contradice una `vigente`,
**PARA y reporta** (§6) o la sustituye explícitamente.
⛔ **Un valor por DEFECTO declarado como dato en la capa base solo es fuente única si TODOS los ⛔ **Un valor por DEFECTO declarado como dato en la capa base solo es fuente única si TODOS los
consumidores pasan por el merge.** El que se lo salta no es un descuido: es una **segunda fuente** consumidores pasan por el merge.** El que se lo salta no es un descuido: es una **segunda fuente**
—con su propia verdad— y hay que buscarlo **explícitamente al introducir el default**, no el día que —con su propia verdad— y hay que buscarlo **explícitamente al introducir el default**, no el día que