@andresmassello/uscha 1.40.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +104 -0
  3. package/bin/README.md +6 -0
  4. package/bin/uscha.js +28 -0
  5. package/package.json +38 -0
  6. package/uscha-kit/.claude/skills/uscha-adr-refine/SKILL.md +161 -0
  7. package/uscha-kit/.claude/skills/uscha-characterize/SKILL.md +124 -0
  8. package/uscha-kit/.claude/skills/uscha-devloop/SKILL.md +490 -0
  9. package/uscha-kit/.claude/skills/uscha-devloop/qa_ledger.py +5602 -0
  10. package/uscha-kit/.claude/skills/uscha-discovery/SKILL.md +161 -0
  11. package/uscha-kit/.claude/skills/uscha-mirador/SKILL.md +145 -0
  12. package/uscha-kit/.claude/skills/uscha-mirador/mirador-render.py +112 -0
  13. package/uscha-kit/.claude/skills/uscha-mirador/mirador-watch.ps1 +22 -0
  14. package/uscha-kit/.claude/skills/uscha-mirador/mirador-watch.sh +26 -0
  15. package/uscha-kit/.claude/skills/uscha-mirador/mirador.template.html +586 -0
  16. package/uscha-kit/.claude/skills/uscha-mirador/telemetry-extract.py +130 -0
  17. package/uscha-kit/.claude/skills/uscha-reverse-discovery/SKILL.md +116 -0
  18. package/uscha-kit/.claude/skills/uscha-rubric/SKILL.md +79 -0
  19. package/uscha-kit/.claude/skills/uscha-sysdoc/SKILL.md +88 -0
  20. package/uscha-kit/.claude-plugin/plugin.json +24 -0
  21. package/uscha-kit/.codex-plugin/plugin.json +37 -0
  22. package/uscha-kit/CHANGELOG-1.10.0.md +84 -0
  23. package/uscha-kit/CHANGELOG-1.11.0.md +67 -0
  24. package/uscha-kit/CHANGELOG-1.12.0.md +46 -0
  25. package/uscha-kit/CHANGELOG-1.13.0.md +33 -0
  26. package/uscha-kit/CHANGELOG-1.14.0.md +42 -0
  27. package/uscha-kit/CHANGELOG-1.15.0.md +58 -0
  28. package/uscha-kit/CHANGELOG-1.16.0.md +55 -0
  29. package/uscha-kit/CHANGELOG-1.17.0.md +44 -0
  30. package/uscha-kit/CHANGELOG-1.18.0.md +42 -0
  31. package/uscha-kit/CHANGELOG-1.19.0.md +41 -0
  32. package/uscha-kit/CHANGELOG-1.2.2.md +16 -0
  33. package/uscha-kit/CHANGELOG-1.2.3.md +20 -0
  34. package/uscha-kit/CHANGELOG-1.2.4.md +10 -0
  35. package/uscha-kit/CHANGELOG-1.2.5.md +23 -0
  36. package/uscha-kit/CHANGELOG-1.2.6.md +11 -0
  37. package/uscha-kit/CHANGELOG-1.2.7.md +15 -0
  38. package/uscha-kit/CHANGELOG-1.2.8.md +24 -0
  39. package/uscha-kit/CHANGELOG-1.2.9.md +4 -0
  40. package/uscha-kit/CHANGELOG-1.20.0.md +29 -0
  41. package/uscha-kit/CHANGELOG-1.21.0.md +33 -0
  42. package/uscha-kit/CHANGELOG-1.22.0.md +60 -0
  43. package/uscha-kit/CHANGELOG-1.23.0.md +75 -0
  44. package/uscha-kit/CHANGELOG-1.24.0.md +50 -0
  45. package/uscha-kit/CHANGELOG-1.25.0.md +55 -0
  46. package/uscha-kit/CHANGELOG-1.26.0.md +70 -0
  47. package/uscha-kit/CHANGELOG-1.27.0.md +45 -0
  48. package/uscha-kit/CHANGELOG-1.28.0.md +35 -0
  49. package/uscha-kit/CHANGELOG-1.29.0.md +20 -0
  50. package/uscha-kit/CHANGELOG-1.3.0.md +74 -0
  51. package/uscha-kit/CHANGELOG-1.30.0.md +46 -0
  52. package/uscha-kit/CHANGELOG-1.31.0.md +59 -0
  53. package/uscha-kit/CHANGELOG-1.32.0.md +50 -0
  54. package/uscha-kit/CHANGELOG-1.33.0.md +46 -0
  55. package/uscha-kit/CHANGELOG-1.34.0.md +55 -0
  56. package/uscha-kit/CHANGELOG-1.35.0.md +30 -0
  57. package/uscha-kit/CHANGELOG-1.36.0.md +33 -0
  58. package/uscha-kit/CHANGELOG-1.37.0.md +41 -0
  59. package/uscha-kit/CHANGELOG-1.38.0.md +11 -0
  60. package/uscha-kit/CHANGELOG-1.39.0.md +14 -0
  61. package/uscha-kit/CHANGELOG-1.4.0.md +68 -0
  62. package/uscha-kit/CHANGELOG-1.40.0.md +16 -0
  63. package/uscha-kit/CHANGELOG-1.40.1.md +11 -0
  64. package/uscha-kit/CHANGELOG-1.5.0.md +64 -0
  65. package/uscha-kit/CHANGELOG-1.6.0.md +57 -0
  66. package/uscha-kit/CHANGELOG-1.7.0.md +74 -0
  67. package/uscha-kit/CHANGELOG-1.8.0.md +46 -0
  68. package/uscha-kit/CHANGELOG-1.9.0.md +112 -0
  69. package/uscha-kit/LICENSE +21 -0
  70. package/uscha-kit/README.md +497 -0
  71. package/uscha-kit/VERSION +1 -0
  72. package/uscha-kit/WORKBENCH.md +178 -0
  73. package/uscha-kit/hooks/block-approved-writes.ps1 +46 -0
  74. package/uscha-kit/hooks/hooks.json +15 -0
  75. package/uscha-kit/install-uscha.py +344 -0
  76. package/uscha-kit/skills/uscha-adr-refine/SKILL.md +161 -0
  77. package/uscha-kit/skills/uscha-characterize/SKILL.md +124 -0
  78. package/uscha-kit/skills/uscha-devloop/SKILL.md +490 -0
  79. package/uscha-kit/skills/uscha-devloop/qa_ledger.py +5602 -0
  80. package/uscha-kit/skills/uscha-discovery/SKILL.md +161 -0
  81. package/uscha-kit/skills/uscha-mirador/SKILL.md +145 -0
  82. package/uscha-kit/skills/uscha-mirador/mirador-render.py +112 -0
  83. package/uscha-kit/skills/uscha-mirador/mirador-watch.ps1 +22 -0
  84. package/uscha-kit/skills/uscha-mirador/mirador-watch.sh +26 -0
  85. package/uscha-kit/skills/uscha-mirador/mirador.template.html +586 -0
  86. package/uscha-kit/skills/uscha-mirador/telemetry-extract.py +130 -0
  87. package/uscha-kit/skills/uscha-reverse-discovery/SKILL.md +116 -0
  88. package/uscha-kit/skills/uscha-rubric/SKILL.md +79 -0
  89. package/uscha-kit/skills/uscha-sysdoc/SKILL.md +88 -0
  90. package/uscha-kit/templates/.gitattributes +6 -0
  91. package/uscha-kit/templates/CLAUDE.md +56 -0
  92. package/uscha-kit/templates/CONSTITUTION.md +149 -0
  93. package/uscha-kit/templates/RUBRIC.md +38 -0
  94. package/uscha-kit/templates/docs/adr/README.md +19 -0
  95. package/uscha-kit/templates/rubric-grader-prompt.md +63 -0
  96. package/uscha-kit/tests/smoke-engine.sh +1739 -0
  97. package/uscha-kit/uscha.config.json +181 -0
  98. package/uscha-kit/workbench-doctor.sh +45 -0
@@ -0,0 +1,75 @@
1
+ # dev-loop-kit 1.23.0 — rubric layer: el ACCEPTANCE de lo no-testeable (2026-07-03)
2
+
3
+ Origen: evaluación del concepto "rubrics" de Claude Code (Outcomes/graders) contra
4
+ el método. Veredicto: la ARQUITECTURA no aporta nada nuevo (maker≠checker con
5
+ contexto aislado ya existe); el edge real es **el artefacto** — criterio cualitativo
6
+ versionado. Entra con el acople corregido respecto de la propuesta original:
7
+ **advisory-first**, jamás dimensión ponderada del readiness (un grade LLM es guess
8
+ estructurado, no hecho medido — doctrina 1.10.0). Smoke suite: 139/139.
9
+
10
+ ## Restricción de diseño: cero dependencia de un agente específico
11
+
12
+ Tres capas de acople decreciente — pedido explícito del humano:
13
+
14
+ 1. **Núcleo (engine stdlib, cero LLM)**: `RUBRIC.md` + `spec-check --rubric` +
15
+ `rubric-ingest` + advisory en readiness + doctor. Un humano puede llenar el JSON
16
+ del grader a mano y todo funciona — **el smoke T43 lo prueba ejecutablemente**
17
+ (el grader.json del test se llena a mano, sin ningún LLM).
18
+ 2. **Interfaz**: el contrato JSON
19
+ `{"criteria":[{"id","verdict":"pass|fail","evidence","note"}]}` — la ÚNICA
20
+ superficie entre el método y cualquier grader. IDs normalizados por número
21
+ (RB-01 == RB_1 == rb1, mismo criterio que AC-n).
22
+ 3. **Graders pluggables**: `templates/rubric-grader-prompt.md` (markdown plano,
23
+ corre pegado en Codex, Gemini CLI, Cursor, un curl, o un humano) + la skill
24
+ `specloop-rubric` como adapter FINO de Claude Code (documentado como adapter).
25
+
26
+ ## Qué hace
27
+
28
+ - **`RUBRIC.md`** (template en `templates/`): criterios `- [ ] RB-01 (peso 3) — …`
29
+ con anchor-pass/anchor-fail (calibran al grader, el engine no los parsea),
30
+ criterios negativos `RB-NEG-nn` (si aparecen, restan su peso) y `threshold: 0.NN`.
31
+ - **`spec-check --rubric`**: estructura = HECHO (archivo ausente, cero criterios
32
+ positivos, IDs duplicados normalizados, threshold ausente o fuera de (0,1] →
33
+ exit 1).
34
+ - **`rubric-ingest`** (subcomando 23): valida el contrato (IDs desconocidos =
35
+ error — el grader no inventa criterios), aplica **evidence-or-nothing** (un
36
+ veredicto que afecta el score sin cita `file:line` NO puntúa y se lista como no
37
+ sustentado), computa el score ponderado vs threshold y persiste un registro
38
+ `rubric:grade` en el ledger. **Advisory por default**: BELOW no gatea. Con
39
+ `--gate` o `defaults.rubric.gate: true` (procedencia: declaración humana), un
40
+ BELOW escribe registro gateado → bloquea convergencia y capea readiness ≤65 por
41
+ la maquinaria existente; un grade limpio posterior lo levanta (latest-wins).
42
+ - **readiness**: muestra el último grade por repo como línea advisory — NO es
43
+ dimensión con peso (measured beats narrated).
44
+ - **doctor**: si `defaults.rubric.file` está declarado, chequea existencia y
45
+ estructura (aviso con remedio).
46
+ - **`specloop-rubric`** (7ma skill): el adapter de Claude Code — contexto aislado
47
+ (solo diff + rúbrica, jamás el razonamiento del maker), sesgo a `fail` ante la
48
+ duda, y prohibido declararse el gate a sí misma.
49
+
50
+ ## Hardening (review fresco pre-commit, 4 hallazgos aplicados)
51
+
52
+ - `threshold: 0.8.0` (malformado) crasheaba con ValueError cruda → se trata como
53
+ ausente y bloquea con mensaje.
54
+ - Entradas no-dict en `criteria` crasheaban → contrato roto, error explícito.
55
+ - **IDs duplicados en el reporte** (RB-01 y RB-1 del mismo criterio): el último
56
+ pisaba al primero en silencio — vector de gaming del grader → contrato roto,
57
+ un veredicto por criterio.
58
+ - El check de smoke "convergencia bloqueada por el gate" era VACUO (converged ya
59
+ salía 1 en ledger virgen por 'no agent steps') → el fixture ahora converge
60
+ ANTES, y se verifica además que un grade limpio LIBERA la convergencia
61
+ (latest-wins).
62
+
63
+ ## La ventaja (por qué entra al método)
64
+
65
+ El criterio cualitativo (convenciones, ergonomía de API, sanidad del error
66
+ handling, calidad de docs) hoy vivía en la cabeza de cada pase de QA. Ahora es
67
+ **versionado, diffeable, auditable** (cada veredicto con evidencia al ledger) y
68
+ **portable** (viaja en el repo; cualquier humano/agente hereda el estándar).
69
+ Anchors + negativos bajan la varianza del grader.
70
+
71
+ ## Límite honesto
72
+
73
+ Nicho más chico que en la era en que se evaluó la idea: el acceptance trazable
74
+ (1.10.0) ya cierra lo testeable — esta capa cubre SOLO lo genuinamente cualitativo,
75
+ y siempre como guess estructurado: nunca reemplaza un gate duro.
@@ -0,0 +1,50 @@
1
+ # dev-loop-kit 1.24.0 — plugin de Claude Code (2026-07-03)
2
+
3
+ El kit ahora se distribuye como **plugin instalable** para Claude Code — el repo es
4
+ su propio marketplace. Los schemas se verificaron contra los docs oficiales
5
+ (code.claude.com/docs: plugins, plugins-reference, plugin-marketplaces,
6
+ discover-plugins) ANTES de escribir una línea — formato de tool real jamás se
7
+ inventa. Smoke suite: 140/140.
8
+
9
+ ## Instalación (Opción C, la recomendada para Claude Code)
10
+
11
+ ```
12
+ /plugin marketplace add andresmassello/SPEC-LOOP
13
+ /plugin install specloop@specloop
14
+ ```
15
+
16
+ - Las 7 skills quedan como `specloop:specloop-*` (el namespace del plugin se suma
17
+ al prefijo propio — mismo patrón que sonarqube:sonar-*).
18
+ - **El hook INV-GOLDEN-01 se auto-registra** (`hooks/hooks.json` con
19
+ `${CLAUDE_PLUGIN_ROOT}`): desaparece la edición manual de settings.json que la
20
+ instalación global requería. Linux: el hook sigue siendo PowerShell — pwsh +
21
+ ajuste del comando (el doctor lo señala con remedio).
22
+ - Updates versionados: el plugin declara `version`, así que `/plugin update`
23
+ solo actualiza con bump de release.
24
+ - **Cero reestructuración**: `plugin.json` soporta path custom de skills
25
+ (`"skills": "./.claude/skills/"`) — el layout canónico del kit no cambió, y
26
+ las Opciones A (por proyecto) y B (global copy/junctions) siguen intactas
27
+ para Codex/Gemini/Cursor. El plugin es EMPAQUETADO, no dependencia: el
28
+ agnosticismo no se negocia.
29
+
30
+ ## Qué se agregó
31
+
32
+ - `dev-loop-kit/.claude-plugin/plugin.json` — manifest (name `specloop`,
33
+ skills path custom, hooks auto-registrados, MIT).
34
+ - `dev-loop-kit/hooks/hooks.json` — registro PreToolUse del hook del golden
35
+ vía `${CLAUDE_PLUGIN_ROOT}`.
36
+ - `.claude-plugin/marketplace.json` (root del repo) — el marketplace `specloop`
37
+ con source relativo `./dev-loop-kit`.
38
+ - **doctor**: tercer modo de instalación detectado (`plugin`, path bajo
39
+ `~/.claude/plugins/`); en modo plugin el hook cuenta como registrado si
40
+ `hooks/hooks.json` existe (auto-registro — settings.json ya no participa).
41
+ JSON expone `plugin_install`.
42
+ - **Smoke T44 — el sync de versión ahora es un HECHO**: VERSION =
43
+ config.version = plugin.json.version = marketplace.version, o la suite falla.
44
+ La regla 6 del repo pasa de triple a **quíntuple** (con CHANGELOG).
45
+
46
+ ## Nota para la máquina del autor
47
+
48
+ Los junctions de la Opción B y el plugin no deben convivir (skills duplicadas):
49
+ la máquina donde se desarrolla el kit se queda con junctions (siempre al día con
50
+ main); el plugin es el canal para las demás máquinas y terceros.
@@ -0,0 +1,55 @@
1
+ # dev-loop-kit 1.25.0 — anti-ceremonia: Lean sobre el propio método (2026-07-04)
2
+
3
+ Origen: el kit llegó a 23 subcomandos, 7 skills, hooks y una CONSTITUTION de 7
4
+ invariantes. El riesgo #1 dejó de ser un gate malo — es la **suma** de gates buenos
5
+ volviendo `/dev-loop` una auditoría. Eso es *over-processing*, muda de ceremonia
6
+ (Poppendieck cap. 4). Este release aplica Lean a la herramienta misma: una
7
+ meta-invariante que gobierna qué gate futuro entra, y el cambio de UX que la hace
8
+ real — **un veredicto único**. Smoke suite: 148/148.
9
+
10
+ ## Qué hace
11
+
12
+ - **Meta-invariante "Anti-ceremonia"** (`templates/CONSTITUTION.md`): la prueba ácida
13
+ que TODO gate futuro debe pasar antes de entrar — (1) corre sin que el humano tipee
14
+ nada, (2) habla solo cuando importa, (3) colapsa en `readiness`, (4) un cambio
15
+ trivial lo saltea. Es principio + criterio de review, no un check del engine.
16
+ - **Veredicto único en `readiness`** (la regla 3, mecanizada): por default `readiness`
17
+ es UNA pantalla — la línea de veredicto, los warnings condicionales que sí dispararon
18
+ (habla solo cuando importa) y una línea `--- gates:` que **colapsa** cada fact gate
19
+ persistido (`gate:*`, `rubric:grade`, `blocker:*`) en `N ok · M bloqueando
20
+ (repo/gate…)`. `--verbose` abre la tabla de dimensiones, el resumen
21
+ acceptance/coverage/churn y el desglose por repo.
22
+ - **`readiness --json`** gana una sección `"gates"` aditiva (la consume sys-doc/CI).
23
+
24
+ ## Decisión de acople: presentación, no re-peso
25
+
26
+ El rollup **lee hechos ya persistidos en el ledger — jamás recomputa el score**. Un
27
+ gate es "bloqueando" sii su último registro gateó ≥1 finding (`gated_reported > 0`),
28
+ que es exactamente la señal que ya alimentaba el cap ≤65. Consecuencia dura: el número
29
+ de readiness es **idéntico** al de 1.24.0 — el smoke lo prueba (las ~30 aserciones de
30
+ readiness previas siguen verdes sin tocarse). No entra como dimensión ponderada: plegar
31
+ sub-scores al peso rompería el anti-Goodhart (la dimensión dominante es measured-
32
+ acceptance a propósito).
33
+
34
+ ## Qué NO entra (y por qué)
35
+
36
+ - **Perfiles de riesgo A–E mecanizados**: la regla ácida 4 ("un cambio trivial lo
37
+ saltea") es principio hasta que exista el clasificador. Los perfiles hoy son doctrina
38
+ (CONSTITUTION/SKILL), no máquina — el engine no tiene `--profile`. Mecanizarlos es su
39
+ propio ADR (¿por tamaño de diff? ¿por paths? ¿por declaración?), release aparte.
40
+ - **`rebuild` en el rollup**: es eje de completitud (COVERS/PARTIAL/DIVERGE), no un
41
+ gate pass/fail; no tiene `--kind` en `log-gate` y queda fuera a propósito.
42
+
43
+ ## Smoke (T45, 8 checks nuevos)
44
+
45
+ Ledger fresco aislado: default emite el veredicto y **colapsa** dimensiones y by-repo;
46
+ `--verbose` los expande; un `log-gate --kind simplicity --verdict fail` aparece nombrado
47
+ en la línea de gates; un grade limpio posterior (latest-wins) la libera; `--json`
48
+ expone `gates[]` con `blocking` correcto.
49
+
50
+ ## Límite honesto
51
+
52
+ El rollup mejora la **legibilidad**, no cambia qué se mide: los mismos hechos, una sola
53
+ pantalla. La meta-invariante es disciplina — su valor depende de que se aplique al
54
+ admitir cada gate futuro (empezando por `waste-check`, que por eso entrará
55
+ advisory-first y default-quiet, no como BLOCKER).
@@ -0,0 +1,70 @@
1
+ # dev-loop-kit 1.26.0 — REUSE-FIRST: la muda que el gate no veía (2026-07-04)
2
+
3
+ Origen: el handoff anti-ceremony+reuse+compliance, ítem 2. La muda dominante del
4
+ código IA —**duplicación / reinvención en vez de reuso**— caía en el punto ciego del
5
+ kit: `simplicity-check` puntúa el diff en AISLAMIENTO, nunca contra lo que ya existe.
6
+ Evidencia: GitClear *Maintainability Gap* (2026): +81% de duplicación desde 2023,
7
+ código refactorizado/movido en 3,8%. Entra con el acople corregido respecto del
8
+ handoff original: **advisory-first**, no BLOCKER-por-default. Smoke suite: 157/157.
9
+
10
+ ## Qué hace
11
+
12
+ - **`qa_ledger.py waste-check`** (subcomando 24): detección determinística de clones
13
+ Type-1/Type-2 del diff **vs el repo** (stdlib, sin LLM, sin red).
14
+ - Input igual que `simplicity-check` (`--diff` · `--from-git [--base]` · stdin) +
15
+ `--repo-root` para escanear lo existente.
16
+ - Ventana de `W=5` líneas normalizadas (strip + colapso de whitespace, se descartan
17
+ blancas y comment-only; una línea < `min_line_len` u sólo-puntuación no es
18
+ significativa). Métricas: `dup_windows_vs_repo` (la señal dominante),
19
+ `dup_windows_internal`, `dup_ratio`, `call_density` (informativo).
20
+ - Bandas `LEAN ≥85 · ACCEPTABLE ≥65 · WASTEFUL <65`. Flags accionables que nombran
21
+ el `archivo:linea` a reusar. `--json` con el contrato completo.
22
+ - Excluye tests (como simplicity) y **los archivos que el diff toca** (no auto-match):
23
+ captura el caso dominante — clon en archivo nuevo / desde archivos no tocados.
24
+ - **`log-gate --kind waste`**: persiste el veredicto → entra al veredicto único de
25
+ `readiness` (1.25.0) como línea `gate:waste` y, si gateado, capea ≤65 / bloquea
26
+ convergencia por la maquinaria existente.
27
+ - **`defaults.waste`** en config: `window_size`, `max_dup_windows_vs_repo`,
28
+ `max_dup_ratio`, `min_line_len`, `allow_paths` (boilerplate legítimo), `gate`.
29
+ - **CONSTITUTION**: invariante **REUSE-FIRST** framed honesta (ver abajo).
30
+ - **SKILL** Fase 2c: corre `waste-check` junto al simplicity gate; en perfil A (trivial)
31
+ no corre.
32
+
33
+ ## Decisión de acople: advisory-first (corrige el handoff)
34
+
35
+ El handoff proponía `WASTEFUL → exit 1` + hard cap POR DEFAULT. Eso viola la
36
+ meta-invariante anti-ceremonia (1.25.0) regla 2: un detector Type-1/2 *habla siempre*
37
+ (boilerplate, DTOs, SQL/JSON embebido) → si bloquea por falsos positivos, se desactiva
38
+ y muere. Split honesto: el **HECHO** (un bloque de 5+ líneas ya existe en `X:linea`) es
39
+ medido; el **VEREDICTO** "wasteful" es heurística. Por eso: **avisa por default** (exit
40
+ 0 con flags), gatea SOLO con `defaults.waste.gate: true` o `--gate` (procedencia 1.17.0:
41
+ el config commiteado ES la declaración). Un WASTEFUL declarado-gate persiste registro
42
+ bloqueante; sin declarar, sólo aconseja.
43
+
44
+ Nota: esto ELIMINA la dependencia de los perfiles A–E (que no están mecanizados): un
45
+ advisory default-quiet no necesita el perfil-skip para no ser ceremonia.
46
+
47
+ ## Desviaciones honestas del handoff
48
+
49
+ - **`connectivity` NO puntúa** — se reporta informativa (como `new_functions` en
50
+ simplicity). "Densidad de llamadas a símbolos existentes" es un proxy que
51
+ false-positivea entre lenguajes; scorearlo sería ruido. Peso movido a
52
+ `repo_reuse 55 · internal_dup 45`.
53
+ - **`max_dup_windows_vs_repo: 0` por default** es seguro PRECISAMENTE porque es
54
+ advisory: cualquier clon vs repo baja el score y avisa, sin bloquear. Quien gatea
55
+ sube el budget o usa `allow_paths`.
56
+
57
+ ## Smoke (T46, 9 checks)
58
+
59
+ Repo sintético `wrepo/` + 4 fixtures: LEAN (código único → 0 clones), clon-vs-repo
60
+ (→ WASTEFUL, advisory exit 0, flag nombra el original; con `--gate` exit 1), clon
61
+ interno (`dup_windows_internal ≥ 1`), clon en archivo de TEST (excluido). Más:
62
+ determinismo (misma entrada → mismo score) y `log-gate --kind waste` visible en el
63
+ veredicto único de readiness.
64
+
65
+ ## Límite honesto
66
+
67
+ Proxy Type-1/Type-2 sobre líneas normalizadas, NO detección semántica (Type-3/4:
68
+ renombres/reordenamientos) ni CC por AST. Es la definición operativa de GitClear —
69
+ suficiente para AVISAR, no para probar equivalencia semántica. El escaneo del repo es
70
+ O(archivos·líneas); en un monorepo enorme es un one-shot, no corre en cada pase.
@@ -0,0 +1,45 @@
1
+ # dev-loop-kit 1.27.0 — First-Time Yield: el KPI Lean que ya estaba en los datos (2026-07-04)
2
+
3
+ Origen: el handoff anti-ceremony+reuse+compliance, ítem 3 (parte pasiva). FTY
4
+ (First-Time Yield) es la métrica Lean clásica de calidad de proceso: qué fracción
5
+ del trabajo salió bien **al primer intento**, sin reprocesos. El ledger ya tenía los
6
+ datos (ciclos, regresiones, escalaciones) — sólo faltaba leerlos. Smoke suite: 161/161.
7
+
8
+ ## Qué hace
9
+
10
+ - **`summary` reporta `first_time_yield`**: % de repos que pasaron QA **en el primer
11
+ ciclo** — sin segundo pase, sin regresiones nuevas, sin escalación (resuelta o no: una
12
+ intervención humana significa que NO salió a la primera). Derivado 100% de hechos ya
13
+ en el ledger (`node["iterations"]` con su número de ciclo, `new_regressions`,
14
+ `ledger["escalations"]`).
15
+ - Aparece en `summary` (texto + `--json` con `by_repo` detallado). Va en la
16
+ retrospectiva.
17
+
18
+ ## Por qué pasivo e informativo (NO gatea)
19
+
20
+ Cumple la meta-invariante anti-ceremonia (1.25.0): **cero paso humano nuevo** (sale de
21
+ datos ya registrados) y **no es otra pantalla** (una línea en el summary que ya existía).
22
+ Y es explícitamente **informativo, jamás gate ni dimensión del readiness**: FTY mide el
23
+ PROCESO (¿cuánto reproceso hubo?), no el ESTADO del resultado. Meterlo al readiness
24
+ confundiría las dos cosas — readiness reporta si el resultado está listo, FTY reporta
25
+ qué tan limpio fue el camino. Son ejes distintos y se reportan aparte (como churn).
26
+
27
+ ## Definición (de hechos del ledger)
28
+
29
+ Un repo cuenta como *first-time yield* sii: entró a QA (tiene iteraciones) **y** su ciclo
30
+ máximo es 1 **y** cero `new_regressions` **y** no tuvo escalación. `pct = first_time /
31
+ repos_que_entraron_a_QA`. Repos que nunca entraron a QA no cuentan en la base.
32
+
33
+ ## Smoke (T47, 3 checks)
34
+
35
+ Dos repos: uno limpio al ciclo 1 (first-time), otro que necesitó 2 ciclos → FTY 50%,
36
+ visible en texto y `--json`. Y una escalación sobre el repo limpio lo saca del yield
37
+ (→ 0%), probando que la intervención humana cuenta como reproceso.
38
+
39
+ ## Nota: compliance-map DIFERIDO
40
+
41
+ El ítem 3 del handoff traía también `compliance-map` (tabla requisito↔gate para dominios
42
+ auditados). Se DIFIERE: es tooling de auditoría sin consumidor auditado real todavía —
43
+ construirlo ahora sería especulativo (choca con el YAGNI del propio kit). Entra cuando
44
+ haya un consumidor que lo pida; el diseño (proyección del ledger, genérico, "trazabilidad"
45
+ no "compliance") ya está pensado en el handoff.
@@ -0,0 +1,35 @@
1
+ # dev-loop-kit 1.28.0 — acceptance medido: el "% terminado" que el kit puede firmar (2026-07-05)
2
+
3
+ Origen: la pregunta del humano — "¿se puede computar un % de proyecto terminado?".
4
+ Análisis: un % ponderado por etapa es progreso NARRADO y gameable (choca measured-beats-
5
+ narrated + anti-Goodhart). El único "% terminado" honesto es el **acceptance medido**:
6
+ criterios cerrados por un test verde name-tagged / total. El ledger ya lo tenía; faltaba
7
+ superficiarlo. Smoke suite: 177/177.
8
+
9
+ ## Qué hace
10
+
11
+ - **`readiness` reporta `acceptance.measured_pct`**: el % de criterios AC cerrados MEDIDO
12
+ (`measured_closed / total`), NO el ratio de checkboxes. En `--json` (campo aditivo) y en
13
+ la **vista default** como una línea: `acceptance medido: 33.3% (1/3 criterios cerrados
14
+ por test verde — medido, no gatea)`.
15
+ - **Honesto por construcción**: la línea aparece SOLO con trazabilidad AC-n
16
+ (`acc_traceable and total`). Sin AC-IDs no hay % honesto → no se muestra nada (un %
17
+ sobre checkboxes sin test sería narrado, justo lo que el kit rechaza). `measured_pct`
18
+ es `null` en ese caso.
19
+ - **Informativo, jamás gatea**: es una lectura del hecho ya medido, no un gate ni una
20
+ dimensión nueva. Cumple la meta-invariante anti-ceremonia (1.25.0): un número medido
21
+ que "habla cuando importa" (hay criterios trazables), colapsado en readiness.
22
+
23
+ ## Por qué NO un % ponderado por etapa
24
+
25
+ Se descartó explícitamente: asignar pesos a discovery/build/QA/… y mostrar una barra es
26
+ un burndown — progreso narrado ("en fase 5 de 8" no dice si lo hecho está correcto) y
27
+ gameable (tildar etapas mueve el número = el "done" prematuro que el método combate). El
28
+ "dónde estamos" ya lo da `phase` (FSM derivada); el "cuánto del spec está probado" lo da
29
+ este acceptance-%. Dos ejes medidos, ninguno narrado.
30
+
31
+ ## Smoke (T29 extendido, 3 checks)
32
+
33
+ Sobre el fixture AC-trazable existente (AC-01 verde, AC-02 rojo, AC-03 sin marcar):
34
+ `measured_pct == 33.3` en `--json`; la línea `acceptance medido: 33.3%` visible en la
35
+ vista default; y —clave— con un ACCEPTANCE sin AC-IDs la línea NO aparece (honesto).
@@ -0,0 +1,20 @@
1
+ # uscha-kit 1.29.0 — rebrand: spec-loop -> Uscha (2026-07-05)
2
+
3
+ La metodologia se renombra a **Uscha**. Rebrand completo: la marca, las 7 skills
4
+ (specloop-* -> uscha-*, comandos /uscha-discovery etc.), el plugin (specloop -> uscha),
5
+ el kit (dev-loop-kit -> uscha-kit), el config (dev-loop.config.json -> uscha.config.json)
6
+ y el paper (docs/paper/uscha-paper.*). Cambio BREAKING: los slash-commands cambian de
7
+ prefijo.
8
+
9
+ ## Que NO cambio
10
+ - Los subcomandos de qa_ledger.py (readiness, waste-check, gate-check, ...) y el propio
11
+ qa_ledger.py: nombres neutros, sin marca.
12
+ - El hook block-approved-writes.ps1: nombre neutro.
13
+ - Los CHANGELOGs historicos y audits/: registro, se dejan verbatim.
14
+ - El remoto GitHub (SPEC-LOOP) y la carpeta local del checkout: fuera de alcance (rename
15
+ de repo/filesystem es otra operacion).
16
+
17
+ ## Verificacion
18
+ - Smoke 177/177 (la suite y sus paths se actualizaron al nuevo kit).
19
+ - Sync quintuple 1.29.0 (T44).
20
+ - rg --hidden 'spec.?loop' sin colgados fuera de los historicos protegidos.
@@ -0,0 +1,74 @@
1
+ # dev-loop-kit 1.3.0 — "facts block, wired" (2026-07-01)
2
+
3
+ Release temático: el kit ahora **cumple su propio principio de cabecera**. Dos auditorías
4
+ adversariales (7 lentes de metodología + fidelidad doc↔código↔idea, 231 agentes) encontraron
5
+ que los fact-gates existían pero no alimentaban ni convergencia ni readiness, mientras que
6
+ los números auto-reportados del agente sí. 1.3.0 invierte eso de vuelta: *under-claim,
7
+ then wire, then re-claim*.
8
+
9
+ ## Engine (qa_ledger.py)
10
+
11
+ ### Nuevo
12
+ - **`log-gate`** — persiste el veredicto de un fact gate (golden-diff / gate-check /
13
+ pit-check / simplicity) como record static-gate-shaped: `fail` → BLOCKER que capea
14
+ readiness ≤65 y bloquea convergencia; `pass` → limpia el gate (latest-per-tool);
15
+ `not-run` → evento registrado que NUNCA lee como verde (la ausencia no es evidencia).
16
+ - **`flag-blocker [--resolve]`** — violación de CONSTITUTION/invariante como BLOCKER de
17
+ primera clase por la MISMA plomería que un BLOCKER de linter. El enforcement "capea ≤65 y
18
+ bloquea convergencia" ahora es real una vez registrada la violación.
19
+ - **`resolve-escalation`** — cerrar el gate humano es un evento registrado (`resolved_at`),
20
+ no una implicación.
21
+
22
+ ### Endurecido
23
+ - **readiness**: un repo linteable cuyo static gate nunca corrió puntúa UNMEASURED (0.0),
24
+ no 1.0 — *el silencio no es éxito*. La dimensión agregada es el promedio per-repo (que ya
25
+ codifica UNMEASURED). El cap de escalación es independiente de converged y se sostiene
26
+ hasta `resolve-escalation`.
27
+ - **converged**: con `qa_tools_order` exige el último step POR HERRAMIENTA (rellenar la
28
+ ventana con steps limpios ya no esconde findings) y un snapshot rojo MEDIDO veta el verde
29
+ narrado del agente (`--tests-passed`).
30
+ - **gate-check**: borrado de archivo de test entero (`+++ /dev/null`) ya no es invisible;
31
+ thresholds bajados O borrados se detectan cross-hunk por keyword; con `--repo` compara los
32
+ totales de tests ejecutados entre snapshots (caída medida → REVIEW).
33
+ - **golden-diff**: cero fixtures = **NOT-RUN (exit 2)**, nunca CLEAN — también en `--json`
34
+ (antes reportaba CLEAN con 0 comparaciones).
35
+ - **simplicity-check**: min-floor en dims pesadas — un diff >1.5× del presupuesto de
36
+ tamaño/crecimiento no se promedia a ACCEPTABLE con dims baratas verdes.
37
+ - **spec-check**: veredictos partidos por la propia regla del kit — los 2 chequeos
38
+ ESTRUCTURALES (falta out-of-scope / sin criterios de aceptación) son HECHOS y bloquean
39
+ (exit 1); la prosa (vagos/EARS/stack) sigue advisory (`--strict` gatea).
40
+ - **oscillation**: detecta por Jaccard ≥0.8 sobre finding_ids (períodos 2-3) — un finding-ID
41
+ corrido ya no esconde el loop; fallback al fingerprint exacto.
42
+
43
+ ## Hook (block-approved-writes.ps1)
44
+ - Bloquea CUALQUIER comando Bash que referencie un path `.approved` (la co-ocurrencia de
45
+ keywords era bypasseable vía `python -c "open(p,'w')"` / paths por variable / dd).
46
+
47
+ ## Skill dev-loop (SKILL.md)
48
+ - Línea de audiencia arriba de todo: *un operador, un cambio con riesgo, ledger, human gate*
49
+ — y disclaimer NOT-for-trivial.
50
+ - Contrato de dos tiers explícito: medido (puede bloquear) vs auto-reportado (narración).
51
+ - **El golden ahora es alcanzable desde el camino principal**: captura en Phase 1 (perfil E,
52
+ vía `characterize`/`reverse-discovery`), `golden-diff` + `gate-check` persistidos con
53
+ `log-gate` en cada pass de Phase 3 (3b), hook + `.gitattributes` en Setup.
54
+ - Convergencia documentada como es: per-tool + fact gates + snapshot veta narración.
55
+ - CONSTITUTION: se registra con `flag-blocker` (obligación del agente), enforcement del
56
+ engine después del registro — se eliminó el "by construction" sin mecanismo.
57
+ - readiness: sin `--section` en el llamado documentado (un heading mismatch cereaba la
58
+ dimensión más pesada en silencio); UNMEASURED documentado.
59
+ - sys-doc degradado a reporting opcional (era la instancia más clara de scope creep).
60
+ - `improve-deep` → `improve` (la skill real); protocolo tracked-md generalizado.
61
+
62
+ ## Kit
63
+ - `dev-loop.config.json`: version 1.3.0, `qa_tools_order` con `improve`, repos de ejemplo
64
+ genéricos (`backend-api`/`mobile-app` — antes traía repos del proyecto del autor).
65
+ - `templates/CONSTITUTION.md`: enforcement honesto (flag-blocker), CWE-1124 para anidación
66
+ (era 1121), tag CWE-1074 removido de YAGNI (no mapea).
67
+ - `README.md`: las SEIS skills (faltaban `characterize` y `reverse-discovery` — justo donde
68
+ vive el flujo golden), on-ramp de migración, subcomandos nuevos, audiencia, generalización.
69
+ - `workbench-doctor.sh`: sonda characterize/reverse-discovery/improve (sin improve-deep).
70
+ - `VERSION`: 1.3.0.
71
+
72
+ ## Deferido (consciente, no olvidado)
73
+ - rebuild: densidad de asserts por test-file en la firma (anti tests-preservados-destripados).
74
+ - Perfiles A-E mecanizados en config (`--profile`). Hoy: heurística de proceso, marcada como tal.
@@ -0,0 +1,46 @@
1
+ # uscha-kit 1.30.0 — gate de dependencias: supply-chain hecho visible (2026-07-05)
2
+
3
+ Origen: intake de research (SLR de agentic-AI arXiv:2605.15245 + survey de seguridad de
4
+ agentes). De los 4 papers, la única mejora genuina y doctrina-pura: el modelo de amenaza
5
+ "el agente mete una dependencia sin vetear (o envenenada)" — que Uscha no cubría. La
6
+ CONSTITUTION ya dice "0 dependencias nuevas sin aprobación", pero como **prosa**. Esto la
7
+ hace visible. Smoke suite: 181/181.
8
+
9
+ ## Qué hace (mínimo, sin overengineering)
10
+
11
+ - **`gate-check` flaguea dependencias nuevas** como señal **BLANDA** (avisa; gatea con
12
+ `--strict`), NO como subcomando nuevo: una dep nueva es exactamente la categoría que
13
+ `gate-check` ya cubre ("el cambio hace algo que necesita sign-off humano" — tests
14
+ borrados, thresholds bajados, secretos). Reusa toda la maquinaria: cero config nuevo,
15
+ cero subcomando.
16
+ - Detecta líneas AGREGADAS con forma de dependencia en los manifests de los 9 stacks
17
+ (`package.json`, `requirements*.txt`, `pyproject.toml`, `Cargo.toml`, `go.mod`,
18
+ `pom.xml`, `build.gradle(.kts)`, `Gemfile`, `Podfile`, `*.csproj`). En `--json`:
19
+ campo `new_dependencies`.
20
+ - **Advisory por default**: verdict `REVIEW`, exit 0. Con `--strict`, la señal blanda
21
+ (deps + supresiones + asserts removidos) gatea exit 1.
22
+
23
+ ## Por qué así (decisiones de la doctrina)
24
+
25
+ - **NO subcomando nuevo** — hubiera sido fragmentación/ceremonia. Extender `gate-check`
26
+ es el tamaño correcto: mismo concepto (integridad del cambio), misma maquinaria.
27
+ - **Blando, no BLOCKER** — agregar una dependencia suele ser legítimo; solo necesita que
28
+ un humano la vea. Bloquear por default mataría el gate (se desactiva). El humano
29
+ declara `--strict` si su dominio lo exige (procedencia, misma doctrina que 1.17.0).
30
+ - **Heurística honesta** — matcheo de líneas por manifest, no un resolver semántico de
31
+ dependencias. Un falso positivo (bump de versión, key no-dep) es benigno porque es
32
+ advisory. Documentado en el código.
33
+
34
+ ## Lo que NO entró del intake (anti-overengineering)
35
+
36
+ - **Ruteo de modelo por perfil de riesgo** (de ALMAS, arXiv:2510.03463): diferido —
37
+ depende de mecanizar los perfiles A–E (que no existen) y vive en la capa de
38
+ orquestación, no en el engine model-agnostic.
39
+ - **Meta-RAG / índice de repo**: diferido — no hay problema de perf medido en el walk del
40
+ waste-check; índice especulativo = YAGNI.
41
+
42
+ ## Smoke (T7b, 4 checks)
43
+
44
+ Dep nueva en `package.json` → `REVIEW` + listada en `new_dependencies`; sin `--strict`
45
+ exit 0 (advisory); con `--strict` exit 1; y un diff de código normal NO flaguea dep
46
+ (sin falso positivo).
@@ -0,0 +1,59 @@
1
+ # uscha-kit 1.31.0 — freshness de evidencia + gate de doc-version (2026-07-05)
2
+
3
+ Origen: review externo (Codex) que encontró al kit fallando su **propia** regla de
4
+ truth-pass. Dos findings reales, un aprendizaje meta: *lo que no mecanizás, deriva*.
5
+ Smoke suite: 185/185.
6
+
7
+ ## #3 — Freshness de evidencia (el que ataca el core)
8
+
9
+ `gate-check`/`readiness` cerraban o vetaban un AC por el testcase JUnit sin chequear si
10
+ el reporte estaba **vigente**. Un `TEST-*.xml` viejo (surefire/gradle no lo limpian sin
11
+ `clean`) podía mantener un AC en falso-verde o vetarlo en falso-rojo. Evidencia stale
12
+ no es evidencia — atacaba directo "la evidencia decide".
13
+
14
+ - **`_ac_tags` descarta reportes STALE**: un reporte más viejo que el código fuente del
15
+ repo (correlación `mtime(fuente) > mtime(reporte)`) se DESCARTA. Un AC respaldado solo
16
+ por reportes stale queda **UNMEASURED** — el mismo patrón honesto que el engine ya usa
17
+ para coverage/static que no corrió, nunca falso-verde ni falso-rojo.
18
+ - **Por qué correlación con la fuente y no advisory** (a diferencia de waste/deps): acá
19
+ honrar evidencia stale rompería el núcleo del método. La correlación no tiene falso
20
+ positivo en el flujo real (editar → testear deja el reporte como lo más nuevo) y sí
21
+ detecta el caso peligroso (tests no re-corridos). Sin fuente que correlacionar (repo
22
+ sin código) → nunca marca stale (evita falso positivo).
23
+ - Campo `stale_reports` en `readiness --json` + advisory en la vista default.
24
+ - **Scope honesto**: `junit_test_count` (el conteo *aproximado*) mantiene el límite —
25
+ su blast radius es menor (no cierra ACs). Diferido, documentado en el código.
26
+
27
+ ## #1/#2 — Doc drift + gate de doc-version (el meta-fix)
28
+
29
+ El README raíz declaraba v1.24.0 / 23 subcomandos / 139 smoke mientras el engine estaba
30
+ en 1.30.0 / 24 / 181; el README del kit copiaba un `uscha-devloop.config.json` inexistente
31
+ y decía "6 skills" (son 7). El sync era QUÍNTUPLE pero solo cubría 5 archivos-máquina;
32
+ los README se truth-passeaban por **disciplina, no por máquina** — y la disciplina derivó.
33
+
34
+ - **READMEs corregidos** (raíz + kit): versión, conteo de subcomandos, cadena de
35
+ changelog, filename del config, conteo de skills, y el título `# dev-loop kit` →
36
+ `# uscha-kit` (miss del rebrand).
37
+ - **`gate de doc-version` mecanizado** (smoke T52): un marcador invisible
38
+ `<!-- uscha:version -->` en la línea canónica de versión de cada README; el smoke
39
+ verifica que coincida con `VERSION`. Robusto sin falso positivo: la cadena de changelog
40
+ lista todas las versiones históricas legítimamente, así que solo se gatea la línea
41
+ marcada. Esto extiende el T44 (sync quíntuple) a los docs.
42
+
43
+ ## Lo que NO entró del review (con criterio)
44
+
45
+ - **#4 CONSTITUTION detección-por-agente**: by-design y documentado; muchos invariantes ya
46
+ se mecanizan (simplicity/waste/gate/golden/pit). Un evento "constitution reviewed" es
47
+ candidato futuro, no urgente.
48
+ - **#5 contrato de QA vendor-neutral** + **#8 acoplar cierre de AC ↔ pit-check**: válidos,
49
+ doctrina-consistentes; van al backlog (el mecanismo #8 ya existe — falta el acople).
50
+ - **#6 separar spec canónica del adapter Claude**: higiene de docs, no defecto.
51
+ - **#7 partir `qa_ledger.py`**: NO. Partir un archivo estable con 185 smoke checks es el
52
+ "refactor de lo que no está roto" que el propio CLAUDE.md prohíbe; YAGNI hasta que la
53
+ concentración cause un bug real.
54
+
55
+ ## Smoke (T51 + T52, 4 checks)
56
+
57
+ T51: reporte fresco → AC-1 cierra medido, `stale_reports` vacío (sin falso positivo);
58
+ código más nuevo → reporte STALE descartado, AC-1 UNMEASURED; advisory visible.
59
+ T52: los dos READMEs declaran la versión actual (drift de docs = exit 1).
@@ -0,0 +1,50 @@
1
+ # uscha-kit 1.32.0 — /mirador: vista bird's-eye + `dashboard --json` (2026-07-05)
2
+
3
+ La 8va skill (`uscha-mirador`) y un subcomando nuevo (`dashboard`, el 25º) que pinta el
4
+ estado del proyecto de un vistazo. **Read-only, determinista, cero narración del LLM.**
5
+ Smoke suite: 189/189.
6
+
7
+ ## Pieza 1 — `qa_ledger.py dashboard [--json]`
8
+
9
+ Agrega SOLO estado que el ledger YA tiene al contrato `DATA` del template mirador
10
+ (readiness, subscores, phases, specs, adrs, inv, capas, loops, snapshots, evidence).
11
+ `--json` imprime el objeto; sin flags, un veredicto de una línea.
12
+
13
+ **Truth-pass estricto — under-claim, then wire.** Un campo sin fuente en el ledger sale
14
+ `null`/`[]` (el template degrada), **nunca se inventa**. La honestidad primero:
15
+
16
+ - **Real hoy**: `readiness` (score/band, reusando `readiness --json` VERBATIM — mismo KPI,
17
+ sin drift), `subscores.coverage`, `subscores.{simplicity,waste,rebuild,golden}` desde
18
+ los gates persistidos (`_gate_rollup`; `val` = null porque el ledger guarda pass/fail,
19
+ no un score 0-100), `loops` (iters + estado por repo), `inv` (7 nombres fijos; status
20
+ del gate donde mapea, si no null), `project`, `phases` (proyección determinista del
21
+ readiness sobre los 8 nodos).
22
+ - **`null`/`[]` por diseño** (el engine no lo trackea): `specs` (trackea `AC-nn`, no
23
+ `SPEC-nnn`), `capas` (no puntúa las 6 capas de verdad), `evidence` (steps[] es log
24
+ plano). Cada uno destraba un panel el día que exista la fuente — quedan en el backlog.
25
+
26
+ ## Add-ons aprobados (baratos y honestos)
27
+
28
+ - **ADRs** (`adrs`): glob read-only de `docs/adr/*.md` (`--adr-dir` configurable) — id de
29
+ un token `ADR-<n>`, título del primer heading, status de una línea `Status:`. `[]` si no
30
+ hay dir.
31
+ - **Time-lapse prospectivo** (`snapshots`): `readiness --record` (opt-in) persiste el
32
+ readiness del momento en `ledger.readiness_history`. `readiness` sigue **read-only por
33
+ default**; el historial se llena **solo hacia adelante** (no se backfillea). El dashboard
34
+ lo consume, nunca escribe. `reached` = proyección determinista del score (como la band).
35
+
36
+ ## Pieza 2 — skill `uscha-mirador`
37
+
38
+ `SKILL.md` + `mirador.template.html`. Flujo: corre `dashboard --json` → inyecta ese JSON
39
+ en el template reemplazando la región entre `/*MIRADOR_DATA_START*/` y
40
+ `/*MIRADOR_DATA_END*/` → escribe `mirador.html` en la raíz del proyecto → lo abre
41
+ best-effort (`start`/`open`/`xdg-open`), sin fallar en headless/CI. El demo del template
42
+ queda como **fallback offline**. El skill cablea, no calcula: los números vienen del ledger.
43
+
44
+ ## Smoke (T53, 4 checks)
45
+
46
+ Contrato completo con las 12 claves; `specs`/`capas` `[]` (truth-pass); `adrs` del glob
47
+ (accepted→done, proposed→prog); `readiness` del dashboard == `readiness --json` (sin
48
+ drift); `snapshots` vacío hasta `readiness --record`, luego poblado (add-on prospectivo);
49
+ `inv` mapea el gate persistido por su kind REAL (`pit-check`→Tests efectivos, no un typo
50
+ mudo — hallazgo del review fresco).
@@ -0,0 +1,46 @@
1
+ # uscha-kit 1.33.0 — mirador session telemetry (tokens / time / model, per-model) (2026-07-05)
2
+
3
+ The mirador gains a **session-telemetry** strip: how many tokens, how much wall time, which
4
+ model(s), at what effort — with a **per-model breakdown** (so you can see if the expensive
5
+ model is being burned on cheap work). Smoke suite: 190/190.
6
+
7
+ **The engine is UNTOUCHED.** `qa_ledger.py` stays model-agnostic and never sees a token —
8
+ this is vendor telemetry (Claude Code), and it enters ONLY through the mirador skill (the
9
+ vendor adapter). No new subcommand; 25 stays 25.
10
+
11
+ ## What ships (all in the `uscha-mirador` skill)
12
+
13
+ - **Segregated strip in `mirador.template.html`**: labeled *"Claude Code · session telemetry —
14
+ vendor-reported, narrated not measured"*, dashed border, at the foot — **visually apart** from
15
+ the measured panels. Shows totals (tokens in/out, wall time, effort, sessions) and, when more
16
+ than one model was used, a **per-model row** (`opus-4-8 240k → 41k · 1.3h`).
17
+ - **`telemetry-extract.py`** (shipped in the skill folder, stdlib, standalone): parses a Claude
18
+ Code session transcript (`*.jsonl`) — each assistant turn's `usage` (`input_tokens` +
19
+ `cache_*` + `output_tokens`) and `model` — sums per model, computes wall time from the
20
+ timestamps, and appends ONE line to the sidecar. Best-effort: bad/old schemas degrade, never
21
+ crash; no usage → nothing appended.
22
+ - **Sidecar contract** `.uscha/telemetry.jsonl` (append-only, one JSON object per session, with a
23
+ `by_model` breakdown). Portable, greppable, git-ignorable. Chosen over `localStorage` because a
24
+ real inspectable artifact is coherent with the doctrine — you can audit where the number came
25
+ from. The skill reads it, aggregates across lines (merging `by_model` per model), and merges a
26
+ `telemetry` object into `DATA` before injection.
27
+
28
+ ## The doctrinal boundary (non-negotiable)
29
+
30
+ Telemetry is **narrated by the vendor, not measured by the engine**. It is neither a fact-gate
31
+ nor a guess-advisor — so it is **shown, never gated, never fed into readiness**, and rendered in
32
+ a strip kept apart from the measured panels. Telemetry answers *"what did it cost"*; the measured
33
+ panels answer *"is it correct"*. Mixing them would dilute *measured beats narrated* — so they stay
34
+ apart. The mirador template also degrades: no sidecar → no strip (verified running the page's JS
35
+ under Node, with and without telemetry).
36
+
37
+ ## Also
38
+
39
+ - The `mirador.template.html` UI is now in **English** (it was the last Spanish-only product HTML;
40
+ the ES/EN decks stay bilingual by design).
41
+
42
+ ## Smoke (T54)
43
+
44
+ A synthetic 2-model transcript → `telemetry-extract.py` → the sidecar line sums tokens
45
+ (input + cache) and output correctly, computes wall time from the timestamps, and carries a
46
+ 2-entry `by_model` breakdown; a corrupt line is skipped, not fatal.