Una metodología spec-driven, tool-agnóstica, que empieza antes de la SPEC: de una idea vaga + material de referencia, el asistente propone la forma del sistema, vos decidís, y recién ahí se especifica, se construye y se avanza solo con evidencia. Este doc cubre la instanciación en Claude Code y Codex con el uscha-kit.
Convención de este doc: en el kit ya está implementado · nuevo incorporado en esta versión · propuesta diseño acordado, todavía no en código · evaluado — diferido diseño registrado, descartado por ahora (fechado en ISSUES-DEFERRED.md). Sin humo: lo que no existe se marca.
Todo el territorio en un diagrama: la línea del flujo, su loop interno (converger sin perseguir el cero), la frontera del loop externo, y la CONSTITUTION que ninguna estación puede violar. Primero el mapa; después el doc lo recorre estación por estación.
El motor de evidencia al centro, las fases del flujo a la izquierda, las seis capas de verificación a la derecha, los principios en la franja, y las piezas del agente que lo sostienen abajo.
El ciclo completo es una sola línea, ejecutable por una persona, un agente o el CI. La herramienta ejecuta; la metodología gobierna; la evidencia decide; el humano aprueba.
Dos cosas que el flujo garantiza: bajar el costo de entrada (no tenés que diseñar el sistema para poder empezar — eso lo propone el Discovery) y subir la certeza de salida (un estado honesto, con evidencia y blockers, dice cuán listo está de verdad). En Claude Code todo esto vive en nueve skills invocables (kit 1.65.0) y un motor de medición en Python.
El error clásico es pedirte que escribas entidades, endpoints y casos de error desde el inicio. Pero un sistema nuevo arranca antes: solo sabés qué querés lograr. Por eso hay dos frentes, cada uno con su paquete: /uscha-discovery escribe el paquete completo (CONTEXT, DOMAIN-MODEL/CONSTITUTION, SPEC, docs/adr, ACCEPTANCE, RISKS/HANDOFF); /uscha-adr-refine emite docs/adr/*.md + ACCEPTANCE.md (y opcionalmente extiende la CONSTITUTION). Elegís por punto de partida.
/uscha-discovery — "no sé el cómo"Greenfield. Traés idea + restricciones + material de referencia. El asistente propone dominio, superficie de API y opciones de arquitectura; vos reaccionás y decidís. La inversión clave: vos no autorás la estructura, la aprobás. Una pregunta a la vez, cada una con su respuesta recomendada.
/uscha-adr-refine — "sé el qué, falta precisión"Feature dentro de un sistema existente. La forma ya se conoce; la entrevista adversarial pinta los bordes: casos sucios, errores, idempotencia, restricciones inviolables, out-of-scope. No emite artefactos hasta converger.
Ambos skills tienen allowed-tools: Read, Write, Glob, Grep (discovery suma WebFetch para leer manuales/PDFs/URLs). Ninguno de los dos toca código: generan claridad, no implementación.
Grill, don't agree. Un discovery donde estuviste de acuerdo con todo, falló — un entrevistador complaciente hereda tus puntos ciegos y te los devuelve formateados como plan. El valor está en las preguntas — contradicciones, términos difusos, modos de falla faltantes, restricciones no dichas — no en validar. La falla cara que esto evita: un build confiado de la forma equivocada.
El frente termina escribiendo el paquete directo en el repo a medida que las decisiones se cristalizan — no junta todo para el final. El build lo lee de los archivos, no del chat.
| Archivo | Qué contiene |
|---|---|
| CONTEXT.md | Glosario del dominio. Se crea con el primer término resuelto; afila lenguaje difuso ("¿'cuenta' es Customer o User?"). |
| DOMAIN-MODEL.md | Las entidades núcleo propuestas y sus relaciones — la "forma" que propusiste y el humano aprobó. |
| SPEC.md | Objetivo/valor, riesgo, scope/out-of-scope, comportamiento, entradas/salidas/errores, acceptance, test plan, operación, rollback. |
| docs/adr/*.md | Una por decisión durable. Incluye Implementation Plan + Verification (checkboxes) → es una spec ejecutable. |
| ACCEPTANCE.md | Definition of Done como - [ ] + métricas de éxito. Es el archivo que el readiness KPI mide río abajo. |
| RISKS.md | Riesgos residuales, supuestos, puntos que necesitan aprobación humana. |
| HANDOFF.md | Qué leer antes de codear + reglas duras de "no hacer" + evidencia requerida. |
Por qué importa aunque sea todo Claude Code: el contexto se resetea (una sesión nueva, un /clear o una compactación no tienen el chat), los sub-agentes leen archivos, un compañero o el CI construyen desde el repo, y la auditoría queda durable. La verdad vive en archivos versionados.
Si describe comportamiento observable, va en SPEC. Si justifica una elección entre alternativas, va en ADR. Casi todo cambio no trivial necesita SPEC; solo algunos necesitan ADR formal.
Comportamiento, reglas, entradas/salidas/errores, criterios de aceptación, tests, operación. Es el contrato de construcción y verificación.
Una decisión técnica, las alternativas descartadas y las consecuencias. Memoria arquitectónica, no lista de tareas.
Solo si la decisión cumple las tres: difícil de revertir, sorprendente sin contexto, y fruto de un trade-off real. Si no, es ruido que entierra a los importantes.
# docs/adr/ADR-NNN-<slug>.md ## Estado: Aceptado # proposed/accepted/deprecated/superseded ## Contexto / Fuerzas ## Alternativas: A) … B) … C) … ## Decisión ## Razones ## Consecuencias (+ / -) ## Implementation Plan # affected paths · patrones · tests ## Verification - [ ] <criterio chequeable por un agente>
El Implementation Plan es lo que vuelve al ADR ejecutable: Claude Code lo lee y lo implementa sin volver a preguntar. Durante el build, el código linkea: // ADR: <slug> — see docs/adr/ADR-NNN-<slug>.md, lo que hace seguro hacer supersede (encontrás todo lo que el ADR gobierna).
El ADR elige entre alternativas. La CONSTITUTION prohíbe: registra los invariantes que ningún ADR ni SPEC puede violar, gane el trade-off que gane. Es la jerarquía de la verdad del proyecto.
SPEC dice qué debe pasar. ADR dice por qué se eligió esta forma. CONSTITUTION dice qué nunca es aceptable. Un ADR puede elegir Postgres vs Redis para cachear un token de sesión; un ADR no puede elegir "guardar el certificado en texto plano" — eso lo veta la CONSTITUTION antes de discutir alternativas.
## CONSTITUTION.md — invariantes del proyecto # Seguridad (no-negociable) - Secretos nunca en logs ni en repo # CWE-532 / CWE-798 - Toda entrada externa validada # CWE-20 - Solo SQL parametrizado, nunca concat # CWE-89 # Dominio (ejemplo — completar por proyecto) - Idempotencia obligatoria en operaciones críticas - Invariantes de negocio que no se rompen jamás - Nunca dos efectos por un mismo requestId # Operación - Sin migration destructiva sin rollback explícito - Ningún merge sin human gate
Es una capa propia: templates/CONSTITUTION.md versionada con invariantes mapeados a CWE. /uscha-discovery y /uscha-adr-refine la escriben/extienden (un ADR no puede contradecirla: se escala, no se aprueba); /uscha-devloop la lee en Fase 0 y la consulta antes de tocar áreas gobernadas. El motor no lee CONSTITUTION.md solo: detectar la violación es deber del agente/humano, que debe loguearla vía flag-blocker --kind constitution; una vez logueada, capea readiness ≤65 y bloquea convergencia hasta --resolve (humano).
No es un agregado suelto: es donde aterrizó el SOTA. Spec Kit tiene /speckit.constitution; el OSS dpolivaev/Uscha (mismo nombre que el tuyo) define sus reglas en CONSTITUTION.md con gates de aprobación humana. Formalizarlo te alinea con la convergencia del campo.
Pregunta inicial: ¿qué puede romper esto y cuánto cuesta si se rompe? El riesgo escala el aparato — el perfil A no arrastra toda la maquinaria. Es una heurística de proceso: no existe un concepto de "perfil" en código ni config, y ningún perfil selecciona gates automáticamente — lo aplicás vos al decidir qué documentos y gates exigirle al cambio.
| Perfil | Ejemplo | Documentos | Gates mínimos |
|---|---|---|---|
| A · Bajo | UI menor, config no crítica | SPEC mini | build + test relevante |
| B · Normal | feature local, bugfix | SPEC + acceptance | tests + static + review |
| C · Crítico | pagos, facturación, seguridad | SPEC formal + ADR | unit+integration+security+rollback |
| D · Multi-sistema | API pública, eventos | SPEC + contratos + ADR | contract tests + versionado |
| E · Legacy grande | refactor, migración | SPEC incremental + baseline | characterization + no regression |
/uscha-devloop fase por fase.El uscha-devloop es un orquestador spec-driven multi-repo. Toma el ADR set + ACCEPTANCE.md como input, construye, y corre un loop de review severity-gated que converge en vez de loopear para siempre, con los tests como guardrail entre pasos. Para en el merge gate. Tres principios no-negociables lo gobiernan: convergé no persigas el cero; los tests son guardrail no finale; generar tests no es correr tests.
qa_ledger.py init --config uscha.config.json. La config lista cada repo y su tipo (maven/flutter/python/node/go/rust/dotnet/cpp/gradle/swift). En multi-repo, los otros repos se montan con --add-dir./uscha-adr-refine primero. Los acceptance criteria se vuelven los contract tests de la fase 1.snapshot --phase pre + check-coverage. Si coverage ≥ umbral, la suite existente es el guardrail. Si está por debajo (o no hay reporte): escribís characterization/contract tests en el borde (API pública, endpoints) — que el humano revise antes de confiar en ellos.qa_tools_order (default: code-review → judgment-day → improve). Un pase de todas = un ciclo. Tras cada tool: aplicá solo fixes ≥ severity gate (el resto a ISSUES-DEFERRED.md), corré los tests, logueá con log-step, ingestá el static gate con ingest-gate, y persistí cada veredicto de fact gate (golden-diff / gate-check / pit-check / simplicity) con log-gate — un fail persistido bloquea convergencia. waste-check (reuse-first, 1.26.0) corre sobre el diff para detectar clones Type-1/2 contra el repo — advisory salvo --gate. Chequeos advisory de fin de ciclo: converged y oscillation.--repo integration. Es la segunda capa de la arquitectura de QA: per-repo verde no implica que las costuras estén verdes./improve test escribe el coverage fino que diferiste. Suite completa verde + coverage ≥ umbral antes de seguir.summary + readiness; si lo pedís, /uscha-sysdoc arma el deck (reporting opcional a pedido, no fase obligatoria del pipeline). Retrospectiva sacada del ledger.La grieta que el método cierra: si el mismo agente que hace el cambio escribe el ledger, puede poner exit_code: 0 sin correr nada. La garantía solo vale si la evidencia la produce la ejecución — el qa_ledger.py parsea artefactos reales (JaCoCo XML, Surefire XML, los reportes de los linters), no transcribe lo que el agente dice.
# el ledger NO confía en el agente: parsea archivos
coverage ← target/site/jacoco/jacoco.xml (LINE counter)
tests ← target/surefire-reports/TEST-*.xml
static ← checkstyle-result.xml · pmd.xml · spotbugsXml.xml
LOC ← conteo propio (prod vs test), stdlib pura
Ausente = sin evidencia, nunca "OK". Un reporte de linter que existe pero está vacío acredita el fix; un reporte ausente no se trata como limpio (significa que el gate no corrió). Una herramienta que no corrió no se inventa en verde.
qa_ledger.py — referencia de comandos.Stdlib pura, Python 3.8+, sin dependencias. Es dueño de todo lo que debe ser exacto y reproducible. Los fact gates (golden-diff, gate-check, simplicity-check, pit-check) salen 0/1 y sus veredictos se persisten con log-gate; los juicios (¿oscila? ¿hay que escalar?) los hace el skill leyendo estos datos, con helpers advisory deterministas.
Superficie exacta del parser actual: 56 subcomandos; la lista de abajo espeja build_parser(), incluidos dashboard, waste-check y spec-change-request.
| Subcomando | Qué hace |
|---|---|
| doctor | Diagnóstico de la instalación (espíritu flutter doctor): python/git, las 9 skills junto al engine, hook INV-GOLDEN-01 registrado, config/ACCEPTANCE/ledger del proyecto y toolchains por type. Exit 1 solo con errores (1.22.0). |
| init | Crea el ledger desde uscha.config.json (repos, umbrales, comandos). |
| snapshot | Mide coverage / tests / LOC de un repo (--phase pre|post). |
| check-coverage | Exit 0 si ≥ umbral, 1 si está por debajo. Es el gate de la fase 1. |
| log-step | Registra un pase de una QA tool: reported / gated-reported / fixed / deferred / suppressed / tests-passed / files-changed / fingerprint. |
| ingest-gate | Parsea Checkstyle/PMD/SpotBugs/FindSecBugs, normaliza severidades y computa el fixed real diffeando finding-IDs contra el pase anterior. |
| converged | Exit 0 = convergió: último paso de agente de cada tool de qa_tools_order limpio + todos los linters limpios + todos los fact gates persistidos limpios. Un snapshot medido rojo veta el verde narrado (rellenar la ventana no ayuda). |
| log-gate | Persiste el veredicto de un fact gate: --kind golden-diff|gate-check|pit-check|simplicity|regression --verdict pass|fail|not-run. Un fail bloquea convergencia y capea readiness ≤65; un not-run queda registrado pero nunca cuenta como verde. |
| corpus-run | Corre un corpus de ENTRADAS REALES (JSONL: input / expected por linea, entregadas al comando por stdin) y persiste gate:corpus con el porcentaje de aciertos. Verdad de campo para greenfield, donde cada payload de test lo invento el agente que escribio el codigo. ADVISORY mientras no haya umbral declarado (--threshold, si no repos[R].corpus_threshold, si no defaults.corpus_threshold); con uno declarado, una corrida por debajo capea readiness ≤65 y bloquea convergencia. Un corpus ausente, vacio o malformado es exit 2 nombrando la linea, nunca un 0 % medido. --ac cierra un criterio MEDIDO con una corrida verde (ADR-046, 2.2.0). |
| smoke-ingest | Ingiere la corrida de smoke como el HECHO que es: un reporte que escribe la herramienta del PROYECTO ({"checks": [{"name", "ok", "status", "latency_ms", "evidence"}]}), persistido como gate:smoke. La fase 7 deja de ser prosa: "el jar sirvio /admin" creido por la palabra del agente es la falla que esto elimina (un equipo publico una lista vacia reportada como verificada). Un check fallado capea readiness ≤65 y bloquea convergencia; un reporte ausente, malformado o VACIO es exit 2 nombrando el check o el campo ofensor, nunca una corrida medida. Es un kind de HECHO: --verdict advisory se rechaza, porque ok es binario. Un check llamado AC-nn ... cierra ese criterio MEDIDO en un reporte que pasa, y uno fallado lo VETA (ADR-047, 2.2.0). |
| flag-blocker | Registra un blocker (p. ej. --kind constitution por violación de la CONSTITUTION): capea readiness ≤65 y bloquea convergencia hasta --resolve (humano). |
| resolve-escalation | Cierra una escalación registrada; hasta entonces readiness queda capeado ≤75. |
| oscillation | Advisory. Detecta que el fingerprint de findings de una tool se repite con período 2 (pase N == pase N-2). |
| escalate | Registra un evento de escalación humana con razón. |
| summary | Métricas retrospectivas (--json lo consume /uscha-sysdoc). Incluye el first-time yield (FTY, 1.27.0): % de repos que pasaron QA en el primer ciclo, sin rework ni escalación — métrica Lean informativa, jamás gatea. |
| readiness | El KPI de estado del proyecto: score 0–100 con pesos y caps duros. Reporta (siempre exit 0); no es un gate exit 0/1. Veredicto único (anti-ceremonia, 1.25.0): por default UNA pantalla — veredicto + una línea --- gates: colapsada; --verbose expande las dimensiones y el detalle por repo. |
| rebuild | El rebuild test: completitud de la SPEC. --mode baseline firma el sistema; --mode compare puntúa la regeneración (COVERS/PARTIAL/DIVERGE). |
| regression-check | Find Bugs Once (1.16.0): findings cerrados sin línea nueva de test = NARRATED — avisa; --strict gatea; se persiste con log-gate --kind regression. |
| waste-check | Reuse-first (1.26.0): detección determinística de clones Type-1/2 del diff contra el repositorio — la duplicación que simplicity-check no ve (puntúa el diff aislado). Cada flag nombra el file:line a reusar. Advisory salvo --gate o defaults.waste.gate. Raíz: muda Lean (Poppendieck) + el +81% de duplicación de GitClear. |
| phase | Estado del workflow DERIVADO del ledger (plan/build/qa/escalated/pr-ready), jamás declarado. --require pr-ready gatea el PR y veta ramas spike/* (1.18.0–1.19.0). |
| rubric-ingest | Ingesta el JSON de la r?brica. |
| production-finding | Registra feedback de producci?n. |
| spec-doubt | Registra dudas de SPEC. |
| spec-change-request | Registra pedidos de cambio de SPEC/ADR. |
| fastpath-eval | Veredicto medido de fast-path (ADR-003): ALLOW/DENY a partir del diff real contra el merge-base; --intent lo registra, sin él la llamada es dry-run. Fail-closed (1.57.0). |
| spec-drift | Deriva consultiva de spec vs código desde las fechas de commit (ADR-005). Nunca gatea; exit 0 siempre (1.58.0). |
| golden-coverage | Registra los archivos fuente MEDIDOS que ejercita el harness de un golden, corriéndolo bajo coverage.py (ADR-006). Alimenta la veda golden-touched opt-in (1.60.0). |
| cleanroom | Ejecuta un comando provisto por el invocador contra un worktree LIMPIO de un commit; gate opt-in de pr-ready (ADR-008, 1.63.0). |
| curation-check | El gate INV-CURATION-01: candidatas, veredictos, behavior ledger append-only (ADR-009/010, 1.64.0). |
| roundtrip | Consultivo: qué candidatas promovidas son trazables en el código vía ids uscha-spec (1.65.0). |
| facts | Deriva SYSTEM-FACTS.json desde los artefactos y chequea claims publicados contra él (ADR-012, 1.68.0). |
| discover | Emite discovery/CANDIDATE-DELTA.json: observaciones tipadas con ids OBS content-addressed, measured/static/narrated (ADR-013, 1.69.0). |
| curate | Registra UN veredicto humano por observacion como objeto append-only del ledger; sin camino batch (ADR-013, 1.69.0). |
| promote | Mueve las observaciones preserve al paquete canonico con lineage derived_from; rechaza sobre OBS sin curar (ADR-013, 1.69.0). |
| fidelity | El vector de fidelidad: 5 dimensiones medidas mas una cuarentena advisory que jamas bloquea (ADR-014, 1.69.0). |
| ir-extract | Extrae el paquete canonico a un grafo tipado (ir/IR.json); lo que no se puede tipar de forma determinista queda UNTYPED, contado, jamas adivinado (ADR-015, 1.72.0). |
| ir-render | Regenera la vista humana (ir/IR.md) desde el grafo; round-trip estable en contenido para las partes estructuradas (ADR-015, 1.72.0). |
| compile-validate | Valida un COMPILATION.json (el contrato de salida del compilador-LLM) contra una IR de referencia: ids de manifest desconocidos, hashes de unidad que no cuadran, un ir_hash obsoleto y un sello roto bloquean; las estadisticas de degeneracion son advisory y nunca bloquean (ADR-016, 1.73.0). |
| compile-ingest | Registra una compilacion validada en el ledger: unexplained_code por construccion mas cada unresolved_intent como objeto UINT content-addressed y append-only, espejado en ISSUES-DEFERRED.md — la salida del compilador genera el backlog de la representacion (ADR-016, 1.73.0). |
| bootstrap-oracle | Corre un oraculo OCULTO contra una implementacion compilada y sale 0 sii cada caso matchea su exit esperado — la pared maker≠checker hecha ejecutable; un hecho medido de si una compilacion independiente es el mismo sistema (ADR-017, 1.74.0). |
| bootstrap-variance | Metricas estructurales (LOC, nodos AST, funciones, imports) y divergencia por pares que prueban que compilaciones independientes del mismo paquete canonico genuinamente difieren; evidencia advisory, nunca un gate (ADR-017, 1.74.0). |
| bench | El Diamond Bench: una tabla de veredictos por arquetipo (PASS / PARTIAL / FAIL / PENDING) sobre un conjunto de sistemas acotados, agregando compile-validate + bootstrap-oracle + bootstrap-variance y escribiendo DIAMOND-BENCH.md; la discriminacion del oraculo es un gate, las identidades de modelo se anonimizan en el headline; determinista, sin LLM (ADR-018, 1.75.0). Con --fidelity, agrega el descriptor de fidelidad por compilador — el extractor estatico M1 sobre cada fuente compilada, cobertura de trazas, pass-rate del oraculo, curation UNMEASURED donde no hay veredicto humano y judged/total donde lo hay; advisory, nunca cambia un veredicto (ADR-022, 1.79.0; ADR-023, 1.80.0). Desde 1.85.0 (ADR-028) las entradas pueden ser JavaScript: el runner del oráculo se enruta por extensión (node para .js), las métricas estructurales para JS son honestamente bidimensionales (no existe un AST de JS en la stdlib), y la superficie estática es el conjunto de exports propio del módulo, tal como lo lista Node; una entrada JS sin node en el PATH se lee como PENDING/UNMEASURED, nunca como un pase falso. Desde 1.85.0 (ADR-029) las entradas pueden ser multi-unidad: el oráculo ejecuta la unidad de entrada (cli.*) con el directorio de la compilación como cwd, la superficie de fidelidad y la curación cubren cada unidad, y la primera entrada de este tipo lleva el primer IR del bench con aristas (un ADR de costura dentro del paquete canónico). |
| bench-curate | UN veredicto humano (preserve|fix|undefined) para UNA observacion de UNA compilacion del bench, agregado append-only a BENCH-CURATION.json; el set curable son las mismas observaciones del extractor estatico M1 que publica static_surface, re-extraidas al momento del veredicto (una edicion del fixture invalida el id viejo y se reporta STALE); sin batch, obs desconocida o store malformado rechazan con exit 2 (ADR-023, INV-CURATION-01, 1.80.0). --dir es el subdirectorio de compilacion (p.ej. c-opus); --compilation es un alias del mismo --dir para evitar confundirlo con el --dir de bench, que es la raiz del bench (1.82.0). |
| bench-r2 | Varianza intra-modelo (ADR-027): para cada entrada del bench con una segunda corrida ciega r2/ del mismo modelo sobre el mismo paquete canonico, la distancia estructural entre corrida 1 y corrida 2 via la MISMA _struct_distance que el bench usa entre compiladores, mas la estabilidad de comportamiento contra el oraculo; clase SIGNAL/NOISY/NOISE por entrada segun intra/inter (piso de ruido bajo cada afirmacion de varianza del programa); advisory, nunca cambia un veredicto del bench; escribe DIAMOND-BENCH-R2.md (ADR-027, 1.84.0). |
| bench-roundtrip | Recuperabilidad round-trip (ADR-030): para cada compilación del bench, cuánto de la IR fijada por humanos pueden anclar los órganos reversos mecánicos que ya existen — anclaje estático (un id referenciado literalmente en una unidad fuente o en una observación estática) y anclaje de comportamiento (un caso de oráculo retenido etiquetado con el id AC que pasa; UNMEASURED donde ningún caso lleva ese tag). El anclaje del trace_manifest del compilador se reporta APARTE como claimed y nunca cuenta como recuperado — es lo que el compilador CLAIMEÓ (y el prompt ciego le entregó los ids), así que contarlo sería tautológico: la primera corrida de este instrumento leyó 1.000 en cada entrada por esa razón exacta. No regenera ninguna IR desde el código ni infiere ninguna spec (ADR-013): es una cobertura MEDIDA sobre la IR autorada por humanos, nunca una IR′. Número honesto de hoy: recoverability media 0.815 sobre 12 entradas, con la dimensión de comportamiento MEDIDA en las 12 — leía 0.828 mientras se juzgaban tres compiladores y 0.815 cuando se sumó un cuarto, de un segundo vendor (ADR-042), porque la recuperabilidad de una entrada es la media sobre sus compilaciones. El edges_recovered_mean de ledger-lite se movió 1.00 → 0.75 por lo mismo: ese campo es una media de CANTIDAD de aristas recuperadas por compilación, no una razón — sólo un brazo ancla las tres aristas, así que el valor es 3/N y N pasó de 3 a 4. Leía 0.062 con comportamiento UNMEASURED en todas hasta que los 12 oráculos del bench fueron curados con una lista ac por caso (ADR-030 enmendado, 1.90.0) — una ausencia nombrada, no un cero, y lo que movió el número fue el tagging: payloads y expectativas quedan intactos. Advisory, nunca cambia un veredicto; escribe DIAMOND-ROUNDTRIP.md (ADR-030, 1.85.0/1.90.0). |
| lang-compare | El brazo de lenguaje controlado: compara un paquete canonico en prosa libre contra una reescritura EARS+STE, juzgados por un unico oraculo oculto compartido (comportamiento fijo), y computa un veredicto behaviour-first — REDUCED / IMPROVED / MIXED / NO EFFECT / WORSE — sobre la varianza entre compiladores, el pass-rate medio del oraculo y el unresolved_intent; varianza reducida con regresion de comportamiento lee MIXED, nunca REDUCED, y un resultado nulo es de primera clase; IMPROVED (ADR-026, 1.83.0): el comportamiento mejoro mientras la varianza no bajo -- el espejo de MIXED; dos compiladores convergiendo en el mismo error se lee como baja varianza; determinista, sin LLM (ADR-019, 1.76.0). Replicado deconfounded en 5 arquetipos: REDUCED en 1, IMPROVED en 1, NO EFFECT en 2, WORSE en 1 -- el positivo del guard no generalizo solo a los sistemas simples, el transformer leyo WORSE (un verde del oraculo perdido), y un segundo arquetipo denso en decisiones (scheduler) dio IMPROVED, fortaleciendo la division denso/simple (ADR-024/ADR-025/ADR-026, 1.81.0-1.83.0). |
| top | Proyección de solo lectura del ledger para uscha top (board, feed, verdicts, drift, rerun; ADR-031..037, 1.86.0-1.91.0). |
| check-terminado | INV-T1 (ADR-038, 1.92.0): ¿el TERMINADO está sellado al estado del código en disco? Recomputa el mismo sello que publica top --json -- árbol limpio, sin cambios relevantes de fuente desde el commit del último snapshot (una diferencia no-fuente sella con nota; ADR-039, 1.93.0), cada reporte ingerido con el sha256 registrado. Exit 0 sellado, 1 roto, 2 sin medir. Solo lee. |
| execution-policy | Imprime el ruteo de ejecuci?n. |
| dashboard | Entrega el contrato de datos de Mirador. |
| simplicity-check | Eval?a minimalidad del diff. |
| pit-check | Eval?a efectividad de tests. |
| gate-check | Detecta debilitamiento de gates o secretos. |
| spec-check | Valida SPEC/ACCEPTANCE; dimension lifecycle (ADR-040): compara el fin de soporte citado en los bloques lifecycle: de los ADR contra el go_live del SPEC. Advisory: nunca gatea. |
| golden-diff | Compara bytes received/approved. |
| operability | Mide la operabilidad del repo como HECHOS del árbol (ADR-048, 2.2.0): un workflow que corre el test command configurado, un workflow que publica un asset de release, un RUNBOOK.md con arranque/config/rollback/smoke, y un seed_command declarado cuyo script existe. Exit 0 siempre; persiste gate:operability — advisory en los perfiles A/B, BLOCKER en C/D/E vía defaults.operability.gate. |
# un pase típico del loop QL=./.claude/skills/uscha-devloop/qa_ledger.py python3 $QL init --config uscha.config.json python3 $QL snapshot --repo backend-api --phase pre python3 $QL check-coverage --repo backend-api # exit 0/1 # ...build + una tool de QA... python3 $QL log-step --repo backend-api --tool code-review --iteration 1 \ --reported 12 --gated-reported 4 --fixed 9 --deferred 2 --suppressed 1 \ --tests-passed true --files-changed 7 --fingerprint a,b,c python3 $QL ingest-gate --repo backend-api --iteration 1 python3 $QL converged --repo backend-api --tools-per-cycle 3
El static gate (tu java-qa-gate: Checkstyle/PMD/SpotBugs/FindSecBugs) no se cuenta a mano. Corrés el gate para que escriba sus XML, y ingest-gate los parsea, normaliza a una escala de severidad común, separa FindSecBugs de SpotBugs, y computa el fixed real por diff de IDs.
| Linter | Severidad nativa → escala común |
|---|---|
| Checkstyle | error → HIGH · warning → MEDIUM · info → INFO |
| PMD | priority 1 → BLOCKER · 2 → CRITICAL · 3 → HIGH · 4 → MEDIUM · 5 → LOW |
| SpotBugs | priority 1 → HIGH · 2 → MEDIUM · 3 → LOW |
| FindSecBugs | findings categoría SECURITY → piso HIGH (se separan bajo tool findsecbugs) |
Escala completa, de menor a mayor: INFO · LOW · MEDIUM · HIGH · CRITICAL · BLOCKER. El severity gate por defecto bloquea en BLOCKER / CRITICAL / HIGH; todo lo de abajo va a ISSUES-DEFERRED.md, nunca al loop. Granularidad de finding-ID configurable (file o line) para el diffing de fixed y oscilación.
El java-qa-gate local es autosuficiente (stdlib + linters, cero servidor). Si el equipo ya corre SonarQube, el plugin sonarqube@claude-plugins-official expone el mismo rol (issues, coverage, quality-gate) como tools MCP — mismo gate, ingest distinto. Normalizá su severidad a esta misma escala común.
Mide el estado del resultado, nunca el esfuerzo. Score ponderado 0–100 con caps duros que pisan el promedio. Reporta, no gatea: siempre sale exit 0 — el que corta la cadena es rebuild (exit 1 salvo COVERS). Los ciclos/regresiones son churn (salud del proceso) y se reportan aparte — nunca suben el readiness. Mostralo después de cualquier tarea, no solo de runs completos.
[x] sin test = narrated_only, no cierra)resolve-escalation)Veredicto único (anti-ceremonia, 1.25.0): por default readiness es UNA pantalla — el veredicto + una línea --- gates: colapsada; el desglose de dimensiones que ves arriba, la acceptance, el churn y el detalle por repo viven detrás de --verbose. Un meta-invariante en la CONSTITUTION gobierna que los gates futuros sigan callados-por-default y colapsen dentro del readiness. Los blockers que capean sí hablan siempre (condicionales: "hablan solo cuando importa"). Sin fórmula, no hay número: eso sería la numerología que el método evita.
El ledger prueba corrección (este build pasó). El rebuild test prueba completitud (la SPEC es suficiente para volver a generar el sistema). Dos preguntas distintas. Es el benchmark de completitud que se está estandarizando en el SOTA.
La mecánica: en un árbol limpio, una sesión fresca apunta solo al paquete (SPEC/ADR/ACCEPTANCE — no al chat) y regenera. Si reconstruye y pasa acceptance + tests → la SPEC cubre. Si diverge → la SPEC tiene huecos: casi siempre decisiones implícitas (códigos de error, estrategia de cache, elección de librería) que vivían en tu cabeza y nunca llegaron a la SPEC.
# rebuild test — subcomando real (1.4.0+) # 1) tree ORIGINAL: capturá la firma $ python3 $QL rebuild --mode baseline --config uscha.config.json → REBUILD-BASELINE.json # 2) tree LIMPIO / sesión fresca: regenerá SOLO producción desde # SPEC/ADR/ACCEPTANCE, PRESERVANDO los tests, y corré la suite # 3) puntuá el tree regenerado contra la baseline $ python3 $QL rebuild --mode compare --baseline REBUILD-BASELINE.json REBUILD: 72.2/100 — PARTIAL ! 1 test(s) fail on regenerated code — behavior the SPEC left implicit ! coverage 45.0% vs baseline 80.0% (tolerance 5)
Implementado y probado (kit 1.4.0+). Pesos: tests 60 · acceptance 20 · coverage 15 · surface 5. Veredictos: COVERS ≥90 · PARTIAL ≥70 · DIVERGE <70 (exit 0 solo si COVERS; --json lo consume /uscha-sysdoc). La señal dominante es la suite preservada: un test que pasaba y falla en el código regenerado = comportamiento que la SPEC dejó implícito. Cuándo correrlo: perfil C+ y E, o de forma periódica (lo disparás vos) — es el QA del output del Discovery.
No limpiar todo; no empeorar nada. La deuda legacy se congela, la nueva se bloquea. Y el loop tiene techo: superarlo no es seguir solo, es escalar.
max_iterations: 5 · tools_per_cycle: 3Escalar no es fallar: es que apareció una decisión humana. El proceso detectó que no debía seguir solo. Nunca auto-merge, nunca exceder el cap en silencio, nunca arreglar por debajo del gate para que el número quede mejor.
Las Laws of Simplicity de Maeda son filosofía de diseño, no un ciclo de dev. Mapear las 10 leyes a gates es tentador — y es la trampa: construir 13 checks para "hacer las cosas simples" viola la Ley 1 (Reduce). El framework se auto-refuta. De todo el combo, una sola pieza tiene dientes — y esa pieza son en realidad dos cosas con un mismo abrigo. Separalas, o reconstruís la misma mezcla, más chica:
| Qué | ¿Determinístico? | ¿Ya lo tenés? |
|---|---|---|
| Budgets numéricos: +líneas de diff (400 default), crecimiento neto, archivos tocados, profundidad de anidación, hunk más grande | SÍ — medible, se enforce | SÍ — simplicity-check |
| "¿Esta abstracción era necesaria? ¿Es especulativa?" | NO — juicio del checker | SÍ — es simplify + maker≠checker |
SIMPLICITY en la CONSTITUTION: presupuesto de complejidad con caps duros.qa_ledger.py simplicity-check que puntúa — determinístico, cero opinión — y desde 2.1.0 es advisory por defecto (exit 0): solo frena donde el proyecto declara sus propios presupuestos y defaults.simplicity.gate. Una dimensión pesada >1.5× su budget fuerza el veredicto a OVERBUILT: las dimensiones baratas no la promedian.simplify (skill oficial, post-diff) + el checker separado.# la invariante como gate objetivo, no como filosofía $ python3 .claude/skills/uscha-devloop/qa_ledger.py simplicity-check --diff HEAD # budgets: +lines (400), net growth, files, nesting, largest hunk SIMPLICITY: PASS · diff 47L / budget 400 · anidación 3 · hunk mayor 22L
La Ley 10 vale más que las otras nueve juntas. Va como norte arriba de la CONSTITUTION, no como checklist:
"Simplicity is about subtracting the obvious and adding the meaningful."
Un sensor es un check automático que le devuelve feedback accionable al agente para que se auto-corrija — antes del review humano. Es tu "evidencia capturada, no narrada" con otro nombre. Se ordenan por cuándo corren y por qué los evalúa:
| Timing | Naturaleza | Ejemplos |
|---|---|---|
| Durante la sesión (rápido) | computacional | type-check, linters, reglas de capas, tests, secret-scan |
| Pipeline (confirmación) | computacional | los mismos, en infra limpia |
| Reviews programados (drift) | inferencial (LLM) | modularidad, seguridad, freshness de dependencias |
Tres hallazgos que le agregan dientes a tu kit:
100% de coverage con 13 mutantes vivos: código que corre pero que ningún test aserta. Coverage mide ejecución, no verificación. Tapa un hueco de tu rebuild test: si los tests preservados no asertan, el rebuild pasa al vacío. Tool Java: PIT. Caro → tier scheduled, no inner loop.
El agente refactorizó complejidad solo cuando el mensaje le dijo cómo y cuándo. Un pass/fail pelado no cambia conducta. Sumá el escape hatch documentado: suprimir con justificación, no muro binario. Tus flags de simplicity-check ya van por acá.
Las métricas determinísticas ganan a nivel archivo (largo, complejidad, args); para lo cross-cutting (acoplamiento) las métricas crudas dan ruido — el review LLM que lee el código les gana. Regla de diseño: los caps de archivo son gate; el juicio de "¿esto está sobre-arquitecturado?" va al checker inferencial, no a un regex. (Es exactamente por qué new_abstractions da falsos positivos con records/DTOs en Java.)
Un montón de sensores puede crear ilusión de calidad tapando lo semántico que el análisis estático no ve. Más sensores ≠ más calidad. Y correr un review inferencial dos veces da issues distintos: una sola pasada no alcanza.
Tu kit mide todo — pero nada chequeaba que el cambio no DEBILITÓ la medición. Un maker corriendo como optimizador toma el camino más barato a "verde", y editar el gate suele ser lo más barato. Tres guardas cierran el exploit más directo de un gate automático: modificar el gate.
Un diff que borra tests (incluso archivos enteros), los deshabilita, o baja/borra thresholds (matcheado cross-hunk) es BLOCKER — qa_ledger.py gate-check, determinístico, exit 1. Supresiones de lint, asserts reescritos y una caída medida del conteo de tests ejecutados (--repo) son REVIEW soft: se gatean con --strict. El aparato que mide la correctitud no lo modifica el cambio que mide.
Afila maker ≠ checker: para cambios de alto blast-radius conviene que el checker tenga puntos ciegos distintos — otra familia de agente o, al menos, otro perfil precisión-recall. Es disciplina de proceso, no lo enforcea el código. Dato: 93,4% de los defectos los cazó exactamente 1 de 4 tools. El loop que produjo el cambio no es su único aprobador.
Heurística barata ANTES del pit-check: priorizar los hunks de archivos de test y flaggear la reescritura masiva de asserts existentes — la red de seguridad editada para aceptar lo roto. El mutation testing sigue siendo la autoridad sobre si un test detectaría el defecto.
Osmani (Agentic Code Review) enumera estos red-flags para revisores humanos; el detector automático y el principio "el aparato es inmutable por el cambio que mide" son síntesis del kit, no recomendación suya. El dato 93,4% (1 de 4 tools) sí es del artículo.
En el Uscha casi todo lo authorea el agente (discovery, ADRs, spec, código, tests). El golden suite es la ÚNICA pieza que el agente no puede authorear — y esa es su razón de existir. Si el agente escribe el test que lo juzga, codifica el mismo blind spot que ya perdió lógica una vez.
El golden se captura ejecutando el código ORIGINAL con inputs reales, mecánicamente, por un script. Los .approved son verdad de campo: los aprueba un HUMANO, nunca el agente. qa_ledger.py golden-diff byte-compara .received vs .approved — un hecho, no un juicio. Cualquier no-match = DIVERGE, corta la cadena.
Ningún módulo entra en migración/modernización sin golden capturado y commiteado ANTES de tocarlo. La spec de la migración se escribe contra ese golden.
Congelá reloj, seeds, orden de maps, GUIDs, y sobre todo el locale objetivo (separador decimal, formato de fecha — alto riesgo entre máquinas con locale distinto). La foto sale byte a byte idéntica cuando el comportamiento es idéntico.
Directamente aplicable a cualquier migración de un sistema legacy con caminos críticos (byte-equivalence), donde la pérdida silenciosa de lógica es el riesgo real. Ya en el kit: front brownfield uscha-reverse-discovery (mapa + hechos), skill uscha-characterize (captura), gate golden-diff, hook block-approved-writes.py (bloquea que el agente toque un .approved) y .gitattributes.
Un kit lleno de gates puede volverse un diamante: hermoso e impráctico. El principio que lo evita — tu regla Böckeler (computacional vs inferencial) aplicada a la propia metodología:
Los gates que leen HECHOS bloquean; los que ADIVINAN sobre prosa, avisan.
| Gate | Lee | |
|---|---|---|
| golden-diff | byte-comparison vs .approved — exit 0 CLEAN · 1 DIVERGE · 2 NOT-RUN (cero fixtures = NOT-RUN, nunca CLEAN); volátiles declarados en golden.scrub.json enmascaran con masking VISIBLE, aprobado por el humano (1.15.0) | BLOQUEA |
| pit-check | XML de PIT (mutation testing) — tier scheduled/incremental, no inner-loop; si el reporte existe y falla, se persiste con log-gate | BLOQUEA |
| gate-check | estructura del diff: tests borrados/deshabilitados, thresholds bajados o borrados, secretos agregados (PEM/AKIA/tokens/contenedores de claves — 1.12.0) | BLOQUEA |
| rebuild | baseline vs regenerado; COVERS/PARTIAL/DIVERGE — exit 1 salvo COVERS | BLOQUEA |
| simplicity · budgets | líneas/archivos/anidación/hunk mayor — los tests quedan FUERA del presupuesto (1.11.0): escribir tests nunca penaliza | BLOQUEA |
| spec-check · estructural | hechos: out-of-scope faltante, acceptance ausente/vacío, cero criterios AC-n trazables o IDs duplicados — exit 1 | BLOQUEA |
| phase --require pr-ready | estado del workflow DERIVADO del ledger (convergencia + tests verdes + 0 BLOCKER/CRITICAL), jamás declarado; rama spike/* nunca pasa (1.18.0–1.19.0) | BLOQUEA |
| readiness | KPI 0–100 con caps duros; siempre exit 0 | REPORTA |
| regression-check | cierre de findings sin línea nueva de test = NARRATED (Find Bugs Once, 1.16.0) — --strict lo gatea | AVISA |
| waste-check | clones Type-1/2 del diff vs el repositorio (reuse-first, 1.26.0) — la duplicación que simplicity-check no ve; flags con file:line a reusar — gatea SOLO con --gate o defaults.waste.gate | AVISA |
| rubric-ingest | grade de la rúbrica: criterio cualitativo versionado con evidencia obligatoria (1.23.0) — gatea SOLO si el humano lo declara (defaults.rubric.gate) | AVISA |
| spec-check · prosa / abstracciones | heurística sobre prosa (términos vagos, EARS) / "tipos nuevos" — --strict las gatea | AVISA |
Un heurístico sobre lenguaje natural que bloquea tira falsos positivos → el dev lo apaga → gate muerto (la erosión que gate-check combate). Por eso los heurísticos de prosa de spec-check son advisory (sus dos checks estructurales — out-of-scope faltante, acceptance ausente/vacío — son hechos y bloquean) y las abstracciones salieron del score. Los gates de hechos los corre el agente inline en /uscha-devloop y los persiste con log-gate — no hay workflow de CI en el kit.
El cruce con Hunt & Thomas (20th Anniversary, 497 pp) dio un conjunto de validaciones, tensiones y diez mejoras accionables, todas shippeadas (kits 1.10.0 → 1.19.0, cada una con review fresco y smoke verde). El registro por ítem vive en los changelogs de esos releases (uscha-kit/CHANGELOG-1.10.0.md … 1.19.0.md); el documento de análisis original se retiró en la limpieza de docs.
| Tip / Topic | Qué quedó en el engine | Kit |
|---|---|---|
| Tip 87 · anécdota Sudoku | Acceptance trazable: AC-n cierra por testcase verde MEDIDO, no por checkbox — la dimensión dominante del readiness | 1.10.0 |
| Topic 51 | Tests fuera del presupuesto de simplicity: escribir tests nunca penaliza el gate | 1.11.0 |
| Topic 43 | Secret-scan en gate-check: PEM/AKIA/tokens/contenedores agregados bloquean como hecho | 1.12.0 |
| Topic 34 | Ledger atómico: checksum sha256 + carga blindada — mutación externa o JSON corrupto bloquea con mensaje de recuperación | 1.13.0 |
| Topics 37 · 5 | Plateau/stop-signal (advisory): findings sin bajar 3 ciclos → "volvé a ADR"; todo convergido → "candidato a PR" | 1.14.0 |
| Topic 41 | Golden scrub: volátiles declarados (timestamps/ids) enmascaran con masking visible, humano-aprobado | 1.15.0 |
| Tips 94 · 31 | Regression-capture: cierre de findings sin test nuevo = NARRATED; resolver un blocker exige escape-analysis | 1.16.0 |
| Tip 8 | Procedencia de umbrales: cada cap dice si es requerimiento (config) u opinión default del kit | 1.17.0 |
| Topic 29 | FSM derivada: phase computa el estado del ledger — el PR se gatea con --require pr-ready | 1.18.0 |
| Tip 21 | Spikes formales: rama spike/* jamás pasa el gate de PR; el output legítimo es un ADR con lecciones | 1.19.0 |
En las dos tensiones grandes el kit se apartó de la letra del libro para defender su propio principio: los caps siguen mordiendo (que existan es definición, el número es opinión — y ahora lo dice), y la FSM se deriva en vez de declararse — una FSM declarada sería estado narrado. Measured beats narrated, aplicado hasta al libro.
"Loop engineering" explotó en una semana de junio 2026 (Steinberger, Cherny, Osmani): "no promptees agentes; diseñá loops que los prompteen". Cuando el frente lo bajó a spec-driven, convergió en estos mismos principios. Uscha no quedó atrás: llegó a la misma forma por separado.
| Principio del loop autónomo trending | Uscha | Dónde vive en el kit |
|---|---|---|
| Done-criteria que rechaza loops abiertos | ✓ | ACCEPTANCE.md (acceptance que puede fallar) |
| Iteration budget / techo duro | ✓ | max_iterations: 5 · change budget |
| Maker ≠ checker: el que escribe no se autocalifica | ✓ | evidencia capturada (ledger parsea artefactos) |
| Estado externalizado a disco | ✓ | QA-LEDGER.json + paquete en el repo |
| Done solo con checker-pass + human sign-off | ✓ | fase 6: para en el merge (human gate) |
| Detección de oscilación / stuck | ✓ | oscillation (fingerprint período-2) |
| Outer loop auto-agendado (timer / routine) | ✗ | deliberadamente no — lo disparás vos |
No solo lo alcanzamos: llegamos a la misma forma por separado, y en integridad de verificación (maker ≠ checker) lo articulamos tan filoso como cualquiera. La única diferencia es que el loop lo dispara una persona, no un cron — y eso es la decisión, no la carencia.
El playbook de loop engineering (Osmani / AlphaSignal, jun 2026) pone un test de 4 condiciones antes de automatizar nada: si falla una, el loop cuesta más que prompterar a mano. Uscha ya define "cuándo NO usarlo"; esto es el filtro de entrada para el outer loop.
Buenos: triage de CI, dep bumps, lint-and-fix, repro de flaky tests, issue→PR sobre código bien testeado.
Malos (humano en la silla): arquitectura, auth, pagos, deploys a producción, producto difuso.
Los dominios de alto blast-radius —regulados, financieros, retail crítico, safety-critical— caen del lado de los "malos para autonomizar". El propio playbook dice: no pongas el loop a decidir en auth/payments/arquitectura. Eso es exactamente el gated-by-design: el artículo lo confirma desde el otro lado.
Uscha es hoy human-triggered: lo disparás vos. El playbook aporta el heartbeat — lo que convierte "una corrida" en un loop. La síntesis: Uscha es el payload ideal de ese latido, porque ya trae el gate, el estado, los skills y el maker ≠ checker. Solo falta el disparador.
# outer loop = schedule + /goal, envolviendo /uscha-devloop > /loop 0 3 * * * # cadencia: cada noche /goal readiness ≥ 80 AND tests verdes en módulos tocados AND 0 findings CRITICAL # lo chequea un modelo aparte /guard 4-condition-test # tarea repetitiva + verificación automática > ejecutá /uscha-devloop sobre <scope machine-checkable> gate = qa_ledger.py ingest-gate + fact gates (log-gate) # YA existe · readiness es KPI, no gate state = QA-LEDGER.json + paquete en repo # YA existe stop = /goal cumplido (checker fresco) o change budget agotado on-done = abrir PR draft · escalar lo que toca la CONSTITUTION human = merge / deploy SIEMPRE con aprobación # gate humano
/loop — re-corre en cadencia, mire o no el estado./goal — sigue hasta que una condición sea verdadera, verificada por un modelo aparte (maker ≠ checker en la condición de parada).Tu seguridad hoy es build-time (static gate + CONSTITUTION con CWE). Cuando el loop corre solo aparecen riesgos operacionales que el playbook documenta. Entran como una capa operacional de la CONSTITUTION: invariantes que el outer loop no puede violar.
| Riesgo del loop desatendido | Invariante operacional (CONSTITUTION) |
|---|---|
| Código generado mergeando sin revisar | El gate incluye SAST + secret-scan + dependency-audit; sin eso, no hay PR. |
| Skills como vector de inyección — un skill de origen dudoso puede filtrar credenciales | Auditar el origen de cada skill antes de instalar; allow-list de fuentes. |
| Secretos en logs (verbose en corridas largas) | Logging no-verbose en loops de producción; sanitizar lo que se loguea. |
| Permission scope creep (un "solo un permiso de escritura" que nadie re-auditó) | Re-auditar permisos cada 30 días; el loop arranca read-only por defecto. |
| Sesión que se infla en loops largos y se cuelga — evidencia perdida, loop muerto sin aviso | Rotar de sesión antes del umbral de tamaño; hook que avisa + rescate del transcript (tooling del autor — no viene en el kit). La higiene de sesión es parte del security tax. |
Falla silenciosa: el agente emite el token de "listo" antes de tiempo y el loop sale con el trabajo a medias. El antídoto es el que ya tenés: un gate objetivo (test/build/linter que devuelve pass o fail), no un verificador "con opinión". El loop no se cierra solo: lo cierran los gates más el humano en el merge gate; /goal chequeado por un modelo fresco ayuda a detectarlo.
El playbook y Uscha no compiten: son capas distintas. El artículo es el outer loop (el latido que dispara mantenimiento machine-checkable). Uscha es el inner loop riguroso + Discovery para sistemas críticos. La convergencia en principios (maker ≠ checker = el evaluator-optimizer de Anthropic, dic 2024) confirma que llegaste a lo durable, no al hype.
Cost per accepted change — no tokens ni tareas intentadas. Si la tasa de cambios aceptados cae por debajo del 50%, el loop te está devolviendo el trabajo de revisión que vino a sacarte: ahí pierde. Uscha es el payload ideal del heartbeat justamente porque su gate sube esa tasa.
Doce arquetipos acotados, un oráculo escondido (M4), cuatro compiladores ciegos de dos proveedores — Haiku, Sonnet, Opus y OpenAI Codex gpt-5.5 (medido en septiembre de 2026). La pregunta: ¿cuánto del sistema sobrevive el viaje de ida y vuelta?
221 veredictos: 213 preserve, 8 fix, ninguno sin juzgar. El brazo cross-vendor es un test de falsación que la afirmación de reemplazabilidad sobrevivió, no una encuesta: cuatro compiladores ciegos, dos proveedores, gpt-5.5 vía Codex incluido.
Más: uscha.dev/diamond · el paper completo en docs/paper/uscha-paper.html.
La release ya no es una checklist a mano: tools/release.py corre ocho invariantes y se niega nombrando cuál falló. La frescura del dogfooding se decide por ANCESTRÍA de git, nunca por reloj.
Todo claim publicado (versión, subcomandos, skills, arquetipos del diamante) es un HECHO derivado, comparado contra el motor. Nació porque el homepage dijo "9/12" durante nueve releases después de que el número se movió — y ningún gate lo vio.
Los presets de riesgo nunca habían tenido efecto: init copiaba la config de referencia del kit, y esa copia superaba al perfil por la regla "lo explícito gana". Ahora init genera una config mínima.
Un gate necesita un presupuesto adoptado. Sin uno declarado, el score informa mas no bloquea.
origin: agent (ADR-044, quién decidió cada ítem), corpus-run (ADR-046, verdad de campo con inputs reales), smoke-ingest (ADR-047, la propia suite como evidencia medida), operability (ADR-048, dimensión medida) y el marcador de versión en cada skill instalada (ADR-045, doctor dice si está desactualizada).
Código descartable para aprender. No SPEC; sí una nota de qué se aprendió. Lo que sobreviva, se especifica después.
Demo que no va a producción. Marcado como tal, aislado, con fecha de muerte.
Incidente en producción: arreglás primero. Pero la evidencia mínima (qué se cambió, cómo se revierte) y la SPEC/ADR retroactivos son obligatorios dentro de 24h — un compromiso que el equipo asume, no una feature que el kit enforcea. Sin válvula de escape, la gente le hace cargo-cult a los descartables o abandona el método bajo presión.
La capa genérica que hace correr la metodología: Claude Code + Python + git/gh + los skills. Lo específico del stack (JDK/Maven, la base de datos, los linters) es el adapter del proyecto y vive en el CLAUDE.md de cada repo — no en el workbench.
| Componente | Para qué | Mínimo |
|---|---|---|
| Claude Code | el agente / orquestador | cuenta Pro / Max / Team / Enterprise / Console |
| Python 3.8+ | corre qa_ledger.py (stdlib pura) | python3 en PATH |
| git | versionado | 2.x con user.name/email |
| gh | crear repo / abrir PR | opcional, recomendado |
| skills del kit | uscha-discovery, uscha-adr-refine, uscha-devloop, uscha-sysdoc, uscha-reverse-discovery, uscha-characterize, uscha-rubric, uscha-mirador, uscha-status | en ~/.claude/skills/ |
| skills de QA | code-review, judgment-day, improve | tus skills globales (el uscha-devloop los orquesta, no los trae) |
| engram (plugin) | memoria persistente entre sesiones | marketplace Gentleman-Programming/engram |
| sonarqube (plugin) | static gate hosteado (alternativa al java-qa-gate) | marketplace claude-plugins-official |
| gentle-ai (CLI) | hook skill-registry (refresca el índice de skills) — opcional, toolchain del autor; el kit no lo requiere | scoop, bucket gentleman |
# Claude Code (native installer — no requiere Node, se auto-actualiza) curl -fsSL https://claude.ai/install.sh | bash # macOS/Linux/WSL irm https://claude.ai/install.ps1 | iex # Windows PowerShell claude # login OAuth # Skills del kit (global) cp -r uscha-kit/.claude/skills/* ~/.claude/skills/ # opcional — gentle-ai (toolchain del autor, el kit no lo requiere) scoop bucket add gentleman https://github.com/Gentleman-Programming/scoop-bucket scoop install gentle-ai claude --version && claude doctor # verificar
En Windows, WSL2 es el camino recomendado (instalás y corrés claude dentro de WSL). Para headless/servidor: export ANTHROPIC_API_KEY=...
El kit instala en siete agentes. Lo que cada uno aporta no es parejo — y la honestidad es decir dónde no lo es.
| Agente | Qué aporta |
|---|---|
| Claude Code | skills, sub-agentes, hooks — incluido el hook de escritura de INV-GOLDEN-01 (bloquea que el agente authoree un .approved), --add-dir multi-repo. |
| Codex | .codex-plugin + skills + AGENTS.md como archivo de contexto. Sin hooks nativos: el golden guard (INV-GOLDEN-01) no se aplica ahí — es una honestidad, no un detalle menor. |
| pi · cursor · copilot · gemini · cline | los cinco targets de skill-root: solo skills (Agent Skills estándar), sin plugin ni hooks. |
Protocolo estable del repo: comandos, no-go zones, DoD, cómo registrar evidencia. Lo permanente vive acá; lo puntual en SPEC/ADR.
uscha-discovery, uscha-adr-refine, uscha-devloop, uscha-sysdoc, uscha-reverse-discovery, uscha-characterize, uscha-rubric, uscha-mirador, uscha-status en .claude/skills/. Invocables, versionables, compartibles con el equipo.
Paralelizan las tools de QA (review, security, mejoras) sin ensuciar el contexto principal.
Corren gates solos: tests post-edit, lint pre-commit. La evidencia se captura por ejecución, no se narra.
Montás varios repos en una sesión para QA de integración/contratos (multi-repo).
Seguís y manejás la sesión desde el celular mientras corre el loop. Sumá plan mode, settings.local.json para permisos, y ccusage para monitorear uso.
La verdad sobrevive al cierre de la sesión: engram guarda hechos y decisiones LLM-oriented por proyecto; MEMORY.md es la auto-memoria en archivos. Es la regla "la verdad vive en archivos" extendida entre sesiones, no solo dentro del repo.
engram es OPCIONAL: uscha no lo requiere. El kit no declara ninguna dependencia (solo Node ≥18 para el router y Python 3.8+ stdlib para el motor) y no nombra a engram en una sola línea de lo que publica. Resuelven cosas distintas: la verdad del proyecto vive en el QA-LEDGER.json, versionado junto al repo y compartido por el equipo; engram guarda la memoria del agente, que es personal de cada máquina. Sin engram el método funciona igual; lo que perdés es contexto del agente entre sesiones, no evidencia.
Los /uscha-devloop largos inflan el transcript hasta colgar la app — visto en vivo: 77 MB = sesión perdida. Un hook que avisa por tamaño, un mapa con semáforo y un rescate del .jsonl a Markdown legible (tooling del autor — no viene en el kit). Regla: rotá de sesión antes del umbral.
Verificado en Codex: la instalación, AGENTS.md, los prompts portables (templates/rubric-grader-prompt.md, templates/esceptico-prompt.md), el brazo de compilación cross-vendor (ADR-042: gpt-5.5 vía codex-cli, --write-mode return, 0 comandos de shell) y — 2026-09-20, app de escritorio de Codex, GPT-5.6 (ADR-049) — un /uscha-devloop completo, de punta a punta, sobre un repo piloto greenfield: CONVERGIÓ, el criterio cerró MEDIDO, se detuvo limpio en el gate de merge. Quedan dos límites: sin hooks nativos en Codex, el guardián de escritura del golden (INV-GOLDEN-01) no se aplica ahí; y sin statusline en vivo, mitigado (kit 2.4.0) imprimiendo el resumen de estado compacto en la respuesta visible en superficies que no tienen una.
Reglas permanentes que Claude Code lee en cada sesión. Lo puntual de cada cambio vive en SPEC/ADR/ACCEPTANCE, no acá. (Si usás otros agentes, copiá este archivo como AGENTS.md.)
/uscha-discovery o /uscha-adr-refine).// ADR: <slug>.Antes de modificar cualquier .md trackeado (CLAUDE.md, docs de plan/delta, docs/adr), pedí la versión actual del repo primero. Esos archivos llevan progreso real (checkboxes, notas); nunca los regeneres desde cero.
Un servicio backend (Java, Spring Boot, React, SQL), de la idea al PR, todo en Claude Code:
claude code · sesión única $ claude > /uscha-discovery grilla 1×1, propone entidades/endpoints, vos decidís 3 cosas ✓ CONTEXT.md · DOMAIN-MODEL.md · SPEC-001 · ADR-001/2/3 ✓ ACCEPTANCE.md · RISKS.md · HANDOFF.md > /uscha-devloop plan → coverage gate → build → QA loop (sub-agentes) → integration CONVERGED · PR abierto. Paro en el merge. > !python3 .claude/skills/uscha-devloop/qa_ledger.py readiness --acceptance ACCEPTANCE.md READINESS: 88/100 — RELEASE CANDIDATE > /uscha-sysdoc # deck de dos vistas desde el ledger # (vos) revisás el PR y mergeás — el human gate no se automatiza
La herramienta ejecuta. La metodología gobierna. La evidencia decide. El humano aprueba.
Un feature mínimo — applyDiscount(monto, porcentaje) — recorrido entero. 6 de los 10 pasos son falla o refinamiento (marcados ⚠): la metodología no es que todo salga bien de una — es que cada desvío tiene un gate que lo caza.
/uscha-discovery "applyDiscount(monto, porcentaje)" → grilla 1×1 (¿redondeo? ¿límites del %?). Sale SPEC-001 + ACCEPTANCE + ADR-001.spec-check --spec SPEC-001.md → criterio vago; lo reescribís testable: "monto=100, %=20 → 80.00 (2 decimales)"./uscha-adr-refine "redondeo: ¿HALF_UP o HALF_EVEN?" → ADR-002, con la alternativa registrada./uscha-devloop → plan → coverage → build → QA loop. Compila, happy path verde.pit-check (corrida scheduled/incremental, no inner-loop; el fail se persiste con log-gate) → mutante vivo en el redondeo: el test corre pero no aserta el centavo. Sumás assertEquals("80.00", …). Coverage mentía.gate-check --from-git → el agente bajó el threshold de cobertura en pom.xml para pasar. Se revierte, se arregla el test de verdad.rebuild --mode compare → el caso %=120 quedó implícito. Enmendás la SPEC (">100 o <0 → error"), regenerás, volvés a Ready. No parcheás el código.simplicity-check --from-git → OVERBUILT: la DiscountStrategyFactory para 3 líneas infló el diff muy por encima del budget (una dimensión >1.5× lo fuerza); el conteo de abstracciones lo señala como advisory. Se recorta.readiness --acceptance ACCEPTANCE.md → 88/100 RELEASE CANDIDATE. Vos leés el diff, aprobás el merge (nunca auto). /uscha-sysdoc arma el deck. Ship.Si en vez de crear applyDiscount la portás del legacy, el paso 1 es otro: /uscha-reverse-discovery mapea el sistema viejo (hechos) y /uscha-characterize captura el golden del código VIEJO con inputs reales. Después, golden-diff byte-compara el nuevo vs .approved — cualquier no-match = DIVERGE. El agente nunca authorea los .approved.
Los skills son "compartibles con el equipo" — pero el comportamiento NO vive en un archivo. Esta taxonomía de 7 capas + una CLI externa + un gotcha de paths describe el entorno completo del autor en Claude Code Desktop; el kit en sí solo requiere Python 3.8+, git (gh opcional), los skills instalados por agente y la config/permisos por repo. Copiar solo settings.json no alcanza. Instalar el kit en otro agente no necesita nada de esto: npx --yes @andresmassello/uscha@latest install --target codex.
| Capa | Qué es | Cómo se reproduce |
|---|---|---|
| Config | settings.json · CLAUDE.md · output-style · theme | copiar (+ arreglar paths) |
| Plugins | engram · sonarqube | instalar desde marketplace |
| Skills | ~/.claude/skills/ (uscha-discovery, uscha-devloop, QA...) | copiar la carpeta entera |
| Agents + Commands | los sdd-* | copiar verbatim |
| Hooks | gates + higiene de sesión (.ps1) | copiar (usan $env:USERPROFILE, portables) |
| CLI externa | gentle-ai (hook skill-registry) | scoop, bucket de Gentleman |
| MCP servers | computer-use, chrome, preview... | gratis con Claude Code Desktop |
settings.json lleva el username literal (C:\Users\...). En otra cuenta se rompe todo. El bootstrap (script del autor, no del kit) tokeniza el path al exportar y lo reemplaza por el usuario local al importar.
El kit no lo requiere. Si lo querés: no está en el bucket main de scoop — primero el bucket propio de Gentleman, si no install falla con "couldn't find manifest".
# bootstrap: un comando por lado # en TU maquina — arma el bundle (sin secretos, sin memoria): bootstrap-claude-setup.ps1 -Mode export -Bundle D:\claude-bundle # en la maquina NUEVA (bundle ya copiado ahi): bootstrap-claude-setup.ps1 -Mode import -Bundle D:\claude-bundle # manual: gentle-ai (bucket propio) + los 2 plugins via /plugin scoop bucket add gentleman https://github.com/Gentleman-Programming/scoop-bucket scoop install gentle-ai
La memoria NO se comparte. engram + MEMORY.md son personales de cada máquina y se construyen solos al trabajar. El comportamiento se replica; la memoria se gana.
Para consultar al vuelo cuando un término aparece en las slides. No es lectura lineal: saltá acá cuando lo necesites (End te trae al final).
| SPEC | Qué debe pasar. Requisitos verificables de un cambio. |
| ADR | Architecture Decision Record. Por qué esta forma y no otra; una decisión con sus alternativas. |
| CONSTITUTION | Invariantes que ningún ADR ni SPEC puede violar. Qué nunca es aceptable. |
| ACCEPTANCE | Criterios de "done" verificables del cambio. |
| CONTEXT · DOMAIN-MODEL · RISKS · HANDOFF | Paquete documental que produce /uscha-discovery: contexto, modelo de dominio, riesgos, traspaso. |
| ISSUES-DEFERRED.md | Findings por debajo del severity gate; no bloquean, se difieren. |
| LEARNINGS.md | Correcciones compiladas a principios (learnings loop). |
| /uscha-discovery | Modo idea→forma (greenfield): grilla 1×1, propone entidades/endpoints. |
| /uscha-reverse-discovery | Front brownfield: extrae HECHOS del sistema viejo (mapa + golden), no propone forma. El humano infiere la SPEC. |
| /uscha-characterize | Captura el golden del código ORIGINAL con inputs reales; para en la aprobación humana. El agente no authorea el .approved. |
| /uscha-adr-refine | Modo para pulir decisiones arquitectónicas. |
| /uscha-devloop | El orquestador: plan → coverage gate → build → QA loop → integración. |
| /uscha-sysdoc | Genera el deck de dos vistas desde el ledger. Reporting opcional, a pedido — no es fase obligatoria del pipeline. |
| Human gate | El humano aprueba el merge/release; nunca automático. |
| qa_ledger.py | Motor de medición (stdlib): readiness, rebuild, simplicity-check, waste-check, pit-check, gate-check, spec-check, golden-diff, log-gate, flag-blocker, resolve-escalation. |
| golden-diff / .approved | Gate de golden testing: byte-compara .received vs .approved (verdad de campo, aprobada por humano). El agente no lo authorea. |
| hechos vs prosa | Principio rector: los gates que leen hechos bloquean; los que adivinan sobre prosa (spec-check, abstracciones) avisan. |
| static gate (java-qa-gate) | Checkstyle / PMD / SpotBugs / FindSecBugs sobre el código. |
| ingest-gate | Parsea los XML del static gate y los normaliza a una escala de severidad común. |
| severity gate | Umbral que bloquea convergencia: BLOCKER / CRITICAL / HIGH. |
| Readiness (KPI) | Score derivado del estado (tests / acceptance / coverage), con caps duros. Reporta (siempre exit 0); no es un gate. |
| rebuild test | ¿La SPEC alcanza para regenerar el sistema? Bandas COVERS / PARTIAL / DIVERGE (exit 1 salvo COVERS). |
| change budget | Máx iteraciones / archivos / deps. Superarlo → escalación. |
| SIMPLICITY (Reduce) | Invariante: presupuesto de complejidad del diff. Bandas SIMPLE / ACCEPTABLE / OVERBUILT. |
| maker ≠ checker | El que hace no se gradúa; un checker en contexto fresco valida (evaluator-optimizer). |
| evidencia capturada | La produce la ejecución (reports), no la narración. Ausente = sin evidencia. |
| inner loop | El ciclo riguroso por cambio (Uscha). |
| outer loop | El latido que dispara mantenimiento machine-checkable. |
| heartbeat | El disparador del outer loop (Automations · /loop · /goal). |
| Ralph Wiggum loop | Falla silenciosa: el agente emite el token de "listo" antes de tiempo. Lo cierran los gates objetivos + el humano, no el motor solo. |
| engram | Memoria persistente LLM-oriented, por proyecto, entre sesiones. |
| MEMORY.md | Auto-memoria en archivos; índice destilado de hechos. |
| higiene de sesión | Rotar de sesión antes de que el transcript la infle y la cuelgue. |
| CWE | Common Weakness Enumeration: catálogo estándar de debilidades de seguridad. |
| YAGNI | "You Aren't Gonna Need It": no construir lo que no se pidió. |
| progressive disclosure | Mostrar lo mínimo primero; diferir el detalle hasta que se necesite. |
Por una cuestión de principios: las ideas de este documento no salieron de la nada. Al concepto/idea que desarrollé, luego lo enriquecí con los siguientes aportes documentales:
| Persona / fuente | Aporte a esta metodología |
|---|---|
| Birgitta Böckeler · martinfowler.com | "Maintainability sensors for coding agents" — el marco de sensores (timing × naturaleza), coverage vs mutation testing, computacional vs inferencial. |
| Addy Osmani | "The New SDLC", "Agentic Code Review", "Loop Engineering" — anti gate-gaming (red-flags), checker no-correlacionado (93,4% = 1 de 4 tools), leer diffs de test más estricto, aislamiento por git worktree, fan-out ≤ review bandwidth. |
| Martin Fowler | Technical Debt Quadrant (deuda inadvertida vs deliberada); su sitio es el hogar de buena parte de estas ideas. |
| Vlad Khononov | Learning Domain-Driven Design — el marco de modularidad que fundamenta el review de modularidad. |
| Dave Thomas & Andy Hunt | The Pragmatic Programmer (20th Anniversary Ed.) — convergencia verificada contra el libro completo: "Don’t Assume It — Prove It" (measured beats narrated), Design by Contract (CONSTITUTION), blackboards (el ledger), Crash Early (diseño fail-closed); "Find Bugs Once" y tracer bullets alimentan el backlog 2.0 (uscha-kit/CHANGELOG-1.10.0.md … 1.19.0.md). |
| Andrej Karpathy | "Simplicity First" — KISS operacionalizado (mínimo código, cambios quirúrgicos, goal-driven). |
| John Maeda | The Laws of Simplicity — la Ley 1 (Reduce), norte de la invariante SIMPLICITY. |
| Geoffrey Huntley | El "Ralph Wiggum loop": la falla silenciosa del token de "listo" prematuro. |
| Anthropic | Evaluator-optimizer (maker ≠ checker, dic 2024); Claude Code; el skill simplify. |
| Gentleman Programming | Tooling del entorno: engram (memoria persistente) y gentle-ai. |
Crédito honesto: la técnica de mutation testing se populariza con Stryker (JS) / PIT (Java); "Simplicity First" se operacionalizó públicamente en el repo de Forrest Chang (ene 2026), no lo endosó Karpathy. Atribuir de menos empobrece; atribuir de más miente.