diff --git a/.metodo/CHECKSUMS b/.metodo/CHECKSUMS index 0fd4ba0..6217c75 100644 --- a/.metodo/CHECKSUMS +++ b/.metodo/CHECKSUMS @@ -1,5 +1,5 @@ -fd01f9f41dfbe394fcf94dbda8f9fcaf35bae1368755b7e9c47a9c9c1d35f1ec metodo.md -c8b7746c26471b6b041abbea1583d2bf6e5b13020df63ca1b9503fa0b4757ec3 bin/metodo +5ff67472a10f22c9425a3d0a4395d32930010c65499a2a6ce4e958ea47311f6c metodo.md +d911bec6c3b969cce80d0a5c500780dde348eb3b0e8d15bb4ef61dbce5c8181e bin/metodo f89b71bdf31028b05075636c1763c964e379bc37a00e0357d1c23a4dd19c09aa workflows/README.md cc241bbecf9647c747b8525b3a6b527455cd50ca0312caa44bc3544342c8ac41 workflows/comunicacion-entre-sesiones.md 2b9037f6dd57b5598c70a676cb728556654f05d1c5b024e7b716864659f7b884 workflows/sesion-ejecucion.md diff --git a/.metodo/VERSION b/.metodo/VERSION index dbbc363..85a5543 100644 --- a/.metodo/VERSION +++ b/.metodo/VERSION @@ -3,3 +3,4 @@ master: metodo version: 2026-08-30 estado: vendored adopted_commit: 6a10eab342a477846ab5f3e67f8ab17169f12807 +sesion_desde: b1301bbc911b9dbf750654d841b56ca0de9ee615 diff --git a/.metodo/bin/metodo b/.metodo/bin/metodo index d7b81b6..9606154 100644 --- a/.metodo/bin/metodo +++ b/.metodo/bin/metodo @@ -111,6 +111,34 @@ _huella_reparto() { { tr -d '\r' < "$SELF_ROOT/metodo.md"; tr -d '\r' < "$SELF_R # ⚠️ Se ata lo decidible: que lo declarado EXISTA. ⛔ Lo contrario —cazar un fichero que está y no # está declarado— NO se ata, y se dice por qué: exigiría una definición de «línea base» del corpus # que hoy no existe, y un gate que la adivinara daría rojos crónicos. Queda como criterio humano. +# ⛔ C26 · QUIÉN LO HIZO (2026-08-31, idea de Xavier). `git` no tiene concepto de sesión: sólo ve un +# working tree y un usuario, el mismo para todas. **El mensaje sí lo controlamos.** Un trailer con el +# ROL —no el nombre de sesión, que rota en cada reinicio— convierte los cuatro filos del árbol +# compartido en algo VISIBLE: un arrastre, un --amend ajeno o un push de lo de otra se leen en el log. +# Sesion: / p.ej. Sesion: orquestadora/securizacion · Sesion: superadmin +# Se juzga desde `sesion_desde:` de .metodo/VERSION (sellado la primera vez que este check llega al +# repo) ⇒ la historia anterior queda FUERA POR CONSTRUCCIÓN, no por una lista de excepciones que +# crecería para siempre. +check_sesion_trailer() { + local md="$ROOT/.metodo" desde n mal + [ -d "$md" ] || md="$ROOT/.metodo" + git -C "$ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1 || { skip "no es repo git — sin commits que medir"; return 0; } + hdr "cada commit dice QUIÉN lo hizo (C26)" + desde=$(grep -E '^sesion_desde:' "$md/VERSION" 2>/dev/null | sed 's/sesion_desde:[[:space:]]*//') + [ -n "$desde" ] || { info "sin punto de adopción todavía — se sella en el próximo 'metodo update'"; return 0; } + git -C "$ROOT" cat-file -e "$desde^{commit}" 2>/dev/null || { skip "el punto de adopción $desde no existe en este repo"; return 0; } + n=0; mal=0 + while read -r sha; do + [ -z "$sha" ] && continue + n=$((n+1)) + git -C "$ROOT" log -1 --format='%B' "$sha" | grep -qE '^Sesion:[[:space:]]*\S' || { + bad "commit sin 'Sesion:' — $(git -C "$ROOT" log -1 --format='%h %s' "$sha" | cut -c1-70) (C26)"; mal=$((mal+1)); } + done </dev/null) +EOF + [ "$mal" -eq 0 ] && ok "$n commit(s) desde la adopción, todos dicen quién los hizo" +} + check_incremento() { local f="$ROOT/.metodo/INCREMENTO" hdr "incrementos declarados (lo que este repo tiene DE MÁS)" @@ -918,7 +946,11 @@ cmd_init() { 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" + [ -n "$adopt" ] && echo "adopted_commit: $adopt" + # C26: punto de adopción del trailer de sesión — se sella UNA vez y se preserva. + _sd=$(grep -E "^sesion_desde:" "$md/VERSION" 2>/dev/null | sed "s/sesion_desde:[[:space:]]*//") + [ -z "$_sd" ] && _sd=$(git -C "$tgt" rev-parse HEAD 2>/dev/null) + [ -n "$_sd" ] && echo "sesion_desde: $_sd"; } > "$md/VERSION.new" && mv "$md/VERSION.new" "$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" </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" + [ -n "$adopt" ] && echo "adopted_commit: $adopt" + # C26: punto de adopción del trailer de sesión — se sella UNA vez y se preserva. + _sd=$(grep -E "^sesion_desde:" "$md/VERSION" 2>/dev/null | sed "s/sesion_desde:[[:space:]]*//") + [ -z "$_sd" ] && _sd=$(git -C "$tgt" rev-parse HEAD 2>/dev/null) + [ -n "$_sd" ] && echo "sesion_desde: $_sd"; } > "$md/VERSION.new" && mv "$md/VERSION.new" "$md/VERSION" _write_checksums "$md" || echo " (aviso: sin sha256 → CHECKSUMS no escrito)" printf '* text eol=lf\n' > "$md/.gitattributes" _stamp_gitattributes "$tgt" @@ -999,6 +1035,7 @@ cmd_check() { printf 'metodo check — %s\n' "$ROOT" check_drift check_apendices_no_viajan + check_sesion_trailer check_incremento check_reparto_al_dia check_semaforo_forma diff --git a/.metodo/metodo.md b/.metodo/metodo.md index 5c658a6..d0ce672 100644 --- a/.metodo/metodo.md +++ b/.metodo/metodo.md @@ -1906,6 +1906,40 @@ pega tal cual. no ha salido** — y saberlo exige **mirar `git log -1` antes**, no suponer que HEAD es lo último que escribiste tú. ⛔ **Y el rechazo del `push` NO se resuelve forzando**: se resuelve con `git reset --soft @{u}` y recommiteando **lo tuyo solo**, que deja el commit ajeno intacto. + +⛔⛔ **Y LA PROPIEDAD QUE UNE A LOS CUATRO, Y DICE CUÁNDO DEJAR DE BUSCAR UN GATE: `git` ES LA ÚNICA +HERRAMIENTA COMPARTIDA DEL ÁRBOL QUE NO TIENE EL CONCEPTO DE SESIÓN.** El semáforo sabe quién eres. +El canal sabe quién eres. El linter sabe en qué repo estás. **`git` sólo ve un working tree y un +usuario** — y en este árbol **ese usuario es el mismo para todas**. +⇒ Por eso **todos los filos aparecen ahí**, y por eso **ninguno lo caza un gate: no hay dato con el +que un gate pudiera distinguirlos.** ⭐ Y eso **no es una excusa: es el criterio para parar** — +buscarle un gate a esto es construir un instrumento sin sujeto, que es la avería que este Método más +persigue. **Lo que sí se puede es avisar antes y decirlo después.** +⛔ **Corolario, y es el más traicionero: `--amend` en un árbol compartido NO es una operación local.** +Su único testigo es **un `push` rechazado**, o sea **un fallo que puede no llegar a producirse**. ⇒ +**`--amend` sólo es seguro sobre un `HEAD` que has VERIFICADO que sigue siendo tuyo** —`git log -1`—, +nunca sobre el que recuerdas haber hecho. + +⭐⭐ **Y LA MITAD QUE SÍ SE PUEDE ATAR, PORQUE EL MENSAJE SÍ LO CONTROLAMOS: CADA COMMIT DICE QUIÉN LO +HIZO.** `git` no tiene concepto de sesión — pero **el mensaje es nuestro**. Un *trailer* con el **ROL** +convierte los cuatro filos de arriba en algo **visible en el `log`**: un arrastre, un `--amend` ajeno +o un `push` que publica lo de otra **se leen**, en vez de descubrirse por un rechazo que puede no +llegar. + + Sesion: / p. ej. Sesion: orquestadora/securizacion + Sesion: ejecucion/hermes-infra + Sesion: superadmin + +⛔ **El ROL, no el nombre de sesión**: los nombres **rotan en cada reinicio** y el usuario **no los +ve** — un `log` lleno de identificadores caducados no identifica a nadie. El rol **sobrevive al +reinicio y significa algo dentro de seis meses**, que es cuando se lee un `log`. +⇒ Se ata (`C26`) **desde un punto de adopción sellado por repo**, así que **la historia anterior queda +fuera POR CONSTRUCCIÓN** — no por una lista de excepciones que crecería para siempre. *(Es la lección +del rojo crónico aplicada al diseñar el gate, no después de sufrirlo.)* +⭐ **Y lo que esto arregla de fondo**: hasta aquí, la única atribución que existía era **el mensaje**, +y por eso *«un commit ajeno bajo un mensaje que habla de otra cosa borra la única atribución que +hay»*. Con el trailer, **la atribución deja de depender de que el mensaje la mencione de pasada**: +está en un campo, es constante, y **se puede contar**. - La **representación en disco es parte del artefacto**: cuando un hash o un runtime consume el checkout, se fija con el repo — **eol** por `.gitattributes` y **bit de ejecución** — o el testigo mide el checkout, no el contenido (una DERIVA falsa en un clone sin tocar entrena a ignorar el