@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,46 @@
1
+ # dev-loop-kit 1.12.0 — secret-scan en gate-check (2026-07-03)
2
+
3
+ Tercera mejora del backlog PragProg (M8 de `docs/analisis-pragmatic-programmer.md`;
4
+ Topic 43 *"Stay Safe Out There — nunca commitear secretos, API keys ni
5
+ credenciales"*). Un secreto agregado al diff bloquea como HECHO, exactamente
6
+ igual que hoy bloquea borrar tests o bajar thresholds. Python stdlib puro.
7
+ Smoke suite: 79/79.
8
+
9
+ ## La idea (facts block, guesses advise — aplicado a secretos)
10
+
11
+ - **Alta precisión = BLOCKER**: clave privada PEM, AWS access key (`AKIA…`),
12
+ GitHub token (`ghp_`/`github_pat_`), Slack (`xox?-`), Google API key
13
+ (`AIza…`), y archivos contenedores de claves (`.p12/.pfx/.jks/.keystore/.key`)
14
+ agregados o modificados — también en modo binario (línea `Binary files`,
15
+ que no trae `+++`).
16
+ - **Genérico = advisory**: literales tipo `password = "…"` y JWTs — en
17
+ fixtures de test abundan placeholders y bloquearlos castigaría escribir
18
+ tests. `--strict` los gatea.
19
+ - Solo se escanean líneas **agregadas**: sacar un secreto del código es bueno
20
+ y el diff que lo saca no debe frenarse. Borrar un `.p12` tampoco bloquea
21
+ (el lado `b/` del diff es `/dev/null` y no matchea).
22
+
23
+ ## Engine (qa_ledger.py)
24
+
25
+ - `_GC_SECRETS_HARD` (lista etiquetada de patrones) + `_GC_SECRET_SOFT` +
26
+ `_GC_KEYFILE`, cableados al loop de `cmd_gate_check`: `secrets_added` suma
27
+ al veredicto hard, `secret_literals` al soft. JSON expone ambos.
28
+ - Los patrones no se auto-matchean como source (verificado): después de
29
+ `-----BEGIN ` en el código viene `(?:RSA`, no `PRIVATE KEY`.
30
+ - Límite conocido (corner self-hosting): los fixtures del smoke contienen la
31
+ AKIA de ejemplo canónica de AWS — un gate-check del diff del PROPIO kit
32
+ la flaggea. Correcto: el gate no exime archivos de test a propósito
33
+ (un secreto en un test sigue siendo un secreto).
34
+
35
+ ## Smoke
36
+
37
+ - **T34**: AKIA agregada → BLOCKER · PEM privado → BLOCKER · `.p12` binario
38
+ agregado → BLOCKER · borrado de `.p12` → CLEAN · literal password →
39
+ REVIEW exit 0 / `--strict` exit 1.
40
+
41
+ ## Diferido consciente
42
+
43
+ - Entropía/base64 genérico (detectores adivinos) NO entra: violaría
44
+ "facts block, guesses advise" — solo patrones con lectura inequívoca
45
+ bloquean. El resto del backlog PragProg sigue en
46
+ `docs/analisis-pragmatic-programmer.md`.
@@ -0,0 +1,33 @@
1
+ # dev-loop-kit 1.13.0 — ledger atómico: checksum de integridad (2026-07-03)
2
+
3
+ Cuarta mejora del backlog PragProg (M3 de `docs/analisis-pragmatic-programmer.md`;
4
+ Topic 34 *"los recursos compartidos mutables incluyen ARCHIVOS"*). Todo el
5
+ edificio "measured beats narrated" se apoya en `QA-LEDGER.json` — y hasta hoy
6
+ ese JSON podía corromperse o mutarse en silencio. Smoke suite: 85/85.
7
+
8
+ ## La idea
9
+
10
+ - La **escritura ya era atómica** (write-temp + `os.replace`, desde antes) —
11
+ lo que faltaba era el otro lado: detectar al CARGAR que el archivo quedó
12
+ inconsistente.
13
+ - `_save` ahora escribe un campo `integrity` con **sha256 canónico** del
14
+ contenido (claves ordenadas — el hash no depende del orden del dict).
15
+ - `_load` **verifica** el checksum cuando el campo existe: una mutación externa
16
+ (edición a mano, merge accidental) o una escritura parcial **bloquea** con
17
+ mensaje de recuperación (`git checkout -- QA-LEDGER.json`). Aceptar una
18
+ edición externa deliberada = borrar el campo `integrity` (acto humano
19
+ explícito, mismo espíritu que INV-GOLDEN-01).
20
+ - JSON corrupto/truncado = hecho bloqueante con mensaje claro, **no un
21
+ traceback crudo**.
22
+ - **Legacy**: ledgers pre-1.13.0 sin `integrity` cargan sin verificar
23
+ (adopción incremental, no rotura retroactiva). `init` re-inicializa sobre
24
+ un ledger corrupto sin leerlo.
25
+
26
+ ## Diferido consciente
27
+
28
+ - File-lock inter-proceso (la parte "opcional" de M3): omitido — stdlib
29
+ portable Windows/Linux no lo da barato (`fcntl` no existe en Windows) y el
30
+ loop es single-writer por diseño. Si aparece un caso real de escritura
31
+ concurrente, se re-evalúa.
32
+ - El baseline de `rebuild` (artefacto aparte) no lleva checksum — mismo
33
+ candidato si el rebuild gana peso.
@@ -0,0 +1,42 @@
1
+ # dev-loop-kit 1.14.0 — plateau y stop-signal en readiness (2026-07-03)
2
+
3
+ Quinta mejora del backlog PragProg (M6 de `docs/analisis-pragmatic-programmer.md`;
4
+ Topic 37 *"Listen to Your Lizard Brain"* + Topic 5 *"Know When to Stop"*).
5
+ La convergencia per-tool existía, pero el engine nunca decía las dos cosas que
6
+ un senior sí dice: "esto no está convergiendo — el problema es de diseño" y
7
+ "esto ya está — cortá". **ADVISORY puro: recomienda, jamás gatea.**
8
+ Smoke suite: 89/89.
9
+
10
+ ## La idea
11
+
12
+ - **stall-check**: findings gateados por CICLO de agente (suma sobre las tools
13
+ del ciclo). Si los últimos 3 ciclos muestran la serie plana o SUBIENDO — y
14
+ todavía hay findings — iterar más no está acercando la solución: el engine
15
+ deja de sugerir implícitamente "seguí iterando" y recomienda **volver a
16
+ ADR / re-planear con el humano**. Una serie bajando es progreso y no dispara.
17
+ Con `qa_tools_order` configurado solo cuentan ciclos **COMPLETOS** (todas las
18
+ tools logueadas) — un ciclo a medio correr suma parcial y podría enmascarar
19
+ o inventar el stall (hallazgo del review fresco, aplicado).
20
+ - **stop-signal**: todos los repos convergieron, cero caps activos y cero
21
+ findings gateados abiertos — no queda ningún fact bloqueante. Lo que falte
22
+ es deuda medible (coverage/acceptance), no findings: **candidato a cortar e
23
+ ir a PR**, decisión del humano.
24
+
25
+ ## Engine (qa_ledger.py)
26
+
27
+ - `_stall_series()` / `_is_stalled()` (ventana `STALL_WINDOW = 3`, constante:
28
+ es un advisory, no un gate parametrizable — cf. tensión ETC/M5, deliberado).
29
+ - `readiness`: JSON expone `advice: {stalled_repos, stop_signal}`; el texto
30
+ imprime ambos avisos marcados "(advisory)".
31
+
32
+ ## Smoke
33
+
34
+ - **T36**: serie 4→5→6 dispara stall · un ciclo 4 PARCIAL no contamina la
35
+ serie · serie 5→3→1 (progreso) NO dispara · repo único convergido con cero
36
+ facts bloqueantes emite `stop_signal: true`.
37
+
38
+ ## Diferido consciente
39
+
40
+ - El stall mide findings gateados por ciclo, no el score de readiness
41
+ persistido por iteración (el ledger no guarda score histórico — si algún
42
+ día lo guarda, el detector puede leer el KPI directo).
@@ -0,0 +1,58 @@
1
+ # dev-loop-kit 1.15.0 — scrub del golden master (2026-07-03)
2
+
3
+ Sexta mejora del backlog PragProg (M7 de `docs/analisis-pragmatic-programmer.md`;
4
+ Topic 41 *"no apoyar tests en cosas no confiables"* — timestamps exactos,
5
+ ids, posiciones absolutas). Sin esto, cualquier salida del código original con
6
+ volátiles vuelve el golden master perma-rojo — y un golden perma-rojo mata la
7
+ credibilidad de todo el gate. Smoke suite: 97/97.
8
+
9
+ ## La idea
10
+
11
+ - `golden.scrub.json` en el root de fixtures declara los volátiles:
12
+ `{"rules": [{"pattern": "<regex>", "replace": "<placeholder>"}]}`.
13
+ - `golden-diff` enmascara AMBOS lados (received y approved) antes de comparar —
14
+ solo texto; el binario sigue byte a byte. El match vía scrub se reporta
15
+ **APARTE** (`N via scrub` + conteo de sustituciones por regla): el masking
16
+ jamás es invisible.
17
+ - **Cadena de custodia**: las reglas se declaran en characterize (paso nuevo,
18
+ ANTES de la aprobación) y el humano las aprueba junto con los `.approved` —
19
+ son contrato. gate-check flaggea cualquier edición posterior a
20
+ `golden.scrub.json` como señal blanda SIEMPRE visible (`--strict` la gatea):
21
+ una regla ensanchada puede enmascarar divergencia real.
22
+ - Archivo de scrub inválido (JSON roto, regex rota) = **exit 2 explícito** —
23
+ el scrub nunca se saltea en silencio.
24
+ - Preferencia documentada: arreglar el determinismo EN LA FUENTE (checklist de
25
+ Phase 2 de characterize); el scrub es para lo que genuinamente no se controla.
26
+
27
+ ## Engine (qa_ledger.py)
28
+
29
+ - `_load_scrub_rules()` / `_scrub()` + integración en `cmd_golden_diff`
30
+ (`matched_scrubbed`, `scrub_rules`, `scrub_substitutions` en el JSON —
31
+ el conteo es del lado RECEIVED; sumar ambos lados duplicaría cada volátil).
32
+ - gate-check: lista `scrub_rules_touched` (soft) en veredicto y JSON — cubre
33
+ agregar, modificar y BORRAR el archivo (borrar reglas también es editarlas).
34
+ - Shape estricta del archivo: top-level lista, key `rules` ausente o typo =
35
+ exit 2 — un error de forma no puede degradar a "cero reglas" en silencio.
36
+
37
+ ## Hardening (review fresco pre-commit, 4 hallazgos aplicados)
38
+
39
+ - AttributeError cruda con lista top-level en el scrub file → exit 2 limpio.
40
+ - `{}` sin key `rules` cargaba cero reglas en silencio → exit 2.
41
+ - Doble conteo de sustituciones (received + approved al mismo dict) → solo
42
+ received.
43
+ - Borrado de `golden.scrub.json` no se flaggeaba (el check vivía solo en
44
+ líneas `+`) → también en `-`.
45
+
46
+ ## Smoke
47
+
48
+ - **T37** — respetando INV-GOLDEN-01: crear un `.approved` es un acto HUMANO
49
+ incluso en tests, así que el path CLEAN-vía-scrub NO se auto-testea (misma
50
+ disciplina que el path CLEAN byte-a-byte desde 1.3.0). Se prueba la mecánica
51
+ a nivel función (enmascara, cuenta, binario intacto, divergencia real no se
52
+ tapa) + los paths CLI sin `.approved`: scrub no fabrica aprobación (DIVERGE
53
+ se mantiene), scrub inválido exit 2, gate-check flaggea ediciones.
54
+
55
+ ## Diferido consciente
56
+
57
+ - Reglas por-fixture (hoy son por root de fixtures) — si un corpus real lo
58
+ pide, se agrega `"glob"` opcional por regla.
@@ -0,0 +1,55 @@
1
+ # dev-loop-kit 1.16.0 — regression-capture: Find Bugs Once (2026-07-03)
2
+
3
+ Séptima mejora del backlog PragProg (M1 de `docs/analisis-pragmatic-programmer.md`;
4
+ Topic 51 / Tip 94 *"Find Bugs Once"* + Tip 31 *"Failing Test Before Fixing
5
+ Code"*). Ataca el cierre narrado de findings: el agente reporta `fixed` y nadie
6
+ pregunta **qué test reproduce el bug que dice haber arreglado**.
7
+ Smoke suite: 106/106.
8
+
9
+ ## La idea (measured beats narrated, ahora sobre el CIERRE)
10
+
11
+ - `regression-check --repo X [--fixed N] --diff/--from-git`: cruza los findings
12
+ cerrados con el diff que los arregla. Verdicts:
13
+ - **N/A** — nada cerrado, nada que exigir.
14
+ - **MEASURED** — el diff agrega/modifica líneas en el árbol de test
15
+ (clasificador compartido de los 9 stacks).
16
+ - **NARRATED** — desaparecieron findings sin tocar UN solo test. Aconseja
17
+ por default (exit 0 con warning); `--strict` gatea; `log-gate --kind
18
+ regression --verdict fail` lo persiste si el equipo decide bloquear.
19
+ - Sin `--fixed` explícito lee la suma de `fixed` de la iteración más reciente
20
+ del repo en el ledger — el dato ya estaba, ahora se interroga.
21
+ - **escape_analysis obligatoria**: resolver un `flag-blocker` ahora EXIGE
22
+ `--escape-analysis "<qué gate/test debió atraparlo y qué se hizo>"` —
23
+ la reflexión es parte del cierre, no opcional (cerrar un BLOCKER sin ella
24
+ es garantía de encontrarlo dos veces). Se persiste en el registro del gate.
25
+
26
+ ## Decisiones de diseño (documentadas, no implícitas)
27
+
28
+ - La evidencia es a nivel **DIFF** y es mecánica: ¿el diff que arregla agrega
29
+ líneas NO VACÍAS en el árbol de test? — un hecho. El mapeo finding→test
30
+ específico sería adivinanza sin una convención de naming
31
+ (¿`test_regr_<fingerprint>`?): **diferido consciente**, el gate mide lo que
32
+ se puede medir sin inventar.
33
+ - **Límite disclosed (hallazgo del review fresco, aplicado)**: es un tripwire,
34
+ no un juez de calidad — una línea de comentario en un test file cuenta como
35
+ evidencia (juzgar contenido entre 9 lenguajes sería adivinanza). Mitigación
36
+ honesta: las líneas en blanco NO cuentan, y las señales
37
+ `has_test_definition`/`has_assertion` se exponen como hechos en el JSON —
38
+ un MEASURED sin ninguna de las dos imprime "evidencia DÉBIL" para el ojo
39
+ humano. La calidad de los tests la juzga pit-check (mutation testing), no
40
+ este gate.
41
+ - Advisory-first: NARRATED no bloquea por default — mismo patrón de adopción
42
+ incremental que acceptance trazable (1.10.0) y los advisories de 1.14.0.
43
+
44
+ ## Skills
45
+
46
+ - `dev-loop`: tras loguear un pass con `--fixed > 0`, correr `regression-check`;
47
+ cierre NARRATED = escribir el test de regresión (el test que falla va ANTES
48
+ del fix).
49
+
50
+ ## Smoke
51
+
52
+ - **T38**: fix sin tests → NARRATED (exit 0 advisory / `--strict` exit 1) ·
53
+ fix con test nuevo → MEASURED (pasa aun con `--strict`) · `--fixed 0` → N/A ·
54
+ lookup del `fixed` de la última iteración del ledger sin `--fixed`.
55
+ - **T4 endurecido**: resolver un blocker sin `--escape-analysis` → rechazado.
@@ -0,0 +1,44 @@
1
+ # dev-loop-kit 1.17.0 — procedencia visible de umbrales (2026-07-03)
2
+
3
+ Octava mejora del backlog PragProg (M5 de `docs/analisis-pragmatic-programmer.md`;
4
+ Tip 8 *"Make Quality a Requirements Issue"* + Topic 8 *"ETC es un valor, no una
5
+ regla"*). Resolución elegida por el humano entre tres opciones: **procedencia
6
+ visible** — ni fiel-al-libro (advisory por default hubiera desarmado el
7
+ anti-Goodhart) ni rechazo. Smoke suite: 111/111.
8
+
9
+ ## La idea (el conteo es hecho · el umbral es opinión · salvo que lo declares)
10
+
11
+ - **Que el cap EXISTA es principio, no opinión**: "tests rojos ⇒ no estás
12
+ ready" es una definición. Los caps siguen mordiendo por default — nada se
13
+ afloja, ningún usuario existente ve su score subir en silencio.
14
+ - **El NÚMERO es opinión del kit SALVO que el humano lo declare.** El acto de
15
+ declaración ya existía: el `dev-loop.config.json` commiteado es humano-owned.
16
+ Valor explícito en `config.defaults` = requerimiento; fallback a la
17
+ constante del engine = default del kit. Cero schema nuevo.
18
+ - Lo que faltaba era la **etiqueta**: ahora cada umbral que muerde dice de
19
+ dónde vino.
20
+
21
+ ## Engine (qa_ledger.py)
22
+
23
+ - `readiness`: el cap que muerde se etiqueta — `capped at 35: tests red —
24
+ umbral default del kit` vs `— umbral requerimiento (config)`. El threshold
25
+ de coverage se etiqueta en la línea resumen (`thr 60, declarado` /
26
+ `default del kit`). JSON expone `cap_source` y `thresholds_declared`.
27
+ - `simplicity-check`: los presupuestos declarados (config o CLI) llevan `*`
28
+ en la tabla; sin ninguno declarado imprime el aviso "todos los presupuestos
29
+ son defaults del kit — opinión, no requerimiento". JSON expone
30
+ `budgets_declared`.
31
+
32
+ ## Skills
33
+
34
+ - `discovery`: paso 9 nuevo en la agenda — **quality bar**: "¿qué nivel de
35
+ calidad BASTA acá y qué dimensiones son negociables?" con propuesta acorde
36
+ al perfil de riesgo (un core de pagos no es un dashboard interno). Lo que el
37
+ humano declara va al config — declarar ES commitear el config.
38
+
39
+ ## Smoke
40
+
41
+ - **T39**: cap declarado (`readiness_caps.tests_red` en config) → etiqueta
42
+ `requerimiento (config)` en JSON y texto · sandbox sin caps declarados →
43
+ lista vacía · simplicity sin declarar → aviso de opinión · presupuesto por
44
+ CLI → listado en `budgets_declared`.
@@ -0,0 +1,42 @@
1
+ # dev-loop-kit 1.18.0 — phase: la FSM derivada del workflow (2026-07-03)
2
+
3
+ Novena mejora del backlog PragProg (M4 de `docs/analisis-pragmatic-programmer.md`;
4
+ Topic 29 *"Juggling the Real World"* — FSM como tabla de datos). Resuelta con
5
+ decisión humana explícita entre tres opciones: **FSM derivada** (ni la FSM
6
+ declarada del análisis original, ni solo-pr-gate, ni diferir).
7
+ Smoke suite: 117/117.
8
+
9
+ ## La idea (measured beats narrated, aplicado a la FSM misma)
10
+
11
+ - El reframe clave: una FSM donde el agente DECLARA "entro a build/qa" es
12
+ **estado narrado** — exactamente lo que el kit combate. Acá el estado se
13
+ **COMPUTA** de los hechos del ledger; no existen transiciones ilegales que
14
+ validar porque no hay nada que declarar.
15
+ - Reglas de derivación (precedencia): **escalated** (escalación abierta — el
16
+ acto humano pendiente pisa todo) → **pr-ready** (convergencia + tests verdes
17
+ medidos + 0 BLOCKER/CRITICAL) → **qa** (pasos registrados sin converger) →
18
+ **build** (snapshots medidos, sin QA) → **plan** (ledger virgen).
19
+ - La "tabla de estados × eventos" del análisis original se convierte en estas
20
+ reglas de derivación — documentadas en el código como datos, no prosa.
21
+
22
+ ## Engine (qa_ledger.py)
23
+
24
+ - `phase --repo X` (subcomando 21): imprime el estado derivado CON su
25
+ evidencia. `--require pr-ready` sale 1 si los hechos no alcanzan, listando
26
+ exactamente qué falta ("el estado no se negocia, se construye"). JSON:
27
+ `{phase, evidence, required, satisfied}`.
28
+ - `dev-loop` Phase 6: el PR se gatea con `phase --require pr-ready` ANTES de
29
+ abrirse — la transición ilegal "PR sin convergencia" del análisis original
30
+ ahora es un hecho bloqueante mecánico, que era todo el punto de M4.
31
+
32
+ ## Smoke
33
+
34
+ - **T40**: ciclo de vida completo derivado — virgen→plan · snapshot→build ·
35
+ QA sin converger→qa · pr-ready con findings abiertos→exit 1 · escalación
36
+ abierta→escalated (pisa todo) · convergido+limpio→pr-ready.
37
+
38
+ ## Diferido consciente
39
+
40
+ - El estado es per-repo; un estado agregado feature-level (todos los repos
41
+ pr-ready + integración verde) se deriva componiendo — si el dogfooding lo
42
+ pide como comando propio, se agrega.
@@ -0,0 +1,41 @@
1
+ # dev-loop-kit 1.19.0 — spikes formales: descartable por contrato (2026-07-03)
2
+
3
+ Décima y ÚLTIMA mejora del backlog PragProg (M10 de
4
+ `docs/analisis-pragmatic-programmer.md`; Tip 21 *"Prototype to Learn"* +
5
+ Topic 37 *"It's Playtime!"*). Con esta release, **las 10 mejoras del cruce con
6
+ The Pragmatic Programmer están resueltas** — 8 implementadas directas y 2
7
+ resueltas con decisión humana explícita entre opciones (M5 procedencia, M4 FSM
8
+ derivada). Convención elegida por el humano: prefijo **`spike/*`**.
9
+ Smoke suite: 121/121.
10
+
11
+ ## La idea (la versión ejecutable de "make it clear this code is disposable")
12
+
13
+ - Un riesgo de incertidumbre ALTA en discovery dispara la pregunta: "¿amerita
14
+ un spike time-boxed antes de congelar la SPEC?" (paso 10 de la agenda).
15
+ - El spike corre en una rama `spike/*` y su ÚNICO output legítimo es un
16
+ **ADR con lecciones** — hechos que alimentan la SPEC, jamás código mergeable.
17
+ - El contrato es ejecutable, estilo INV-GOLDEN-01: `phase --require pr-ready`
18
+ **rechaza cualquier rama `spike/*`** aunque los hechos del ledger den
19
+ pr-ready — "escribí el ADR y arrancá limpio en una rama normal". Sin
20
+ `--require` solo informa (consultar no gatea). Resuelve la tensión con
21
+ Stone Soup sin romper spec-first.
22
+
23
+ ## Engine (qa_ledger.py)
24
+
25
+ - `_spike_branch()`: `git symbolic-ref --short -q HEAD` (funciona antes del
26
+ primer commit; detached HEAD o directorio sin git = sin veto, disclosed).
27
+ - `phase --require pr-ready`: veto de spike ANTES del veredicto; JSON expone
28
+ `spike_branch`.
29
+
30
+ ## Smoke
31
+
32
+ - **T41**: repo pr-ready por hechos PERO en `spike/*` → exit 1 con el mensaje
33
+ del contrato · consulta sin `--require` → informa sin gatear · rama normal
34
+ → pr-ready vuelve a pasar.
35
+
36
+ ## El backlog PragProg, cerrado
37
+
38
+ M2 (1.10.0) · M9 (1.11.0) · M8 (1.12.0) · M3 (1.13.0) · M6 (1.14.0) ·
39
+ M7 (1.15.0) · M1 (1.16.0) · M5 (1.17.0) · M4 (1.18.0) · M10 (1.19.0).
40
+ Cada release: smoke verde → review fresco → hallazgos aplicados → triple
41
+ sync → commit único. Lo diferido consciente de cada una vive en su CHANGELOG.
@@ -0,0 +1,16 @@
1
+ # dev-loop-kit 1.2.2
2
+
3
+ ## Fixes (simplicity-check)
4
+ - **[BLOCKER] `--from-git` crasheaba en Windows** — `subprocess.run` sin `encoding`
5
+ decodificaba el diff con cp1252 y reventaba (UnicodeDecodeError → None.splitlines).
6
+ Fix: `encoding="utf-8", errors="replace"`.
7
+ - **Salida en mojibake en consola Windows** — `main()` fuerza `stdout/stderr` a UTF-8.
8
+ - **No filtraba por tipo de archivo** — medía docs/config/resources como código
9
+ (inflaba lines/nesting; falsos OVERBUILT). Ahora solo cuenta código
10
+ (`_SIMPLICITY_CODE_EXT` + `SKIP_DIRS`), y reporta cuántos archivos salteó.
11
+
12
+ ## Nuevo: `pit-check` (mutation testing)
13
+ - Gate de EFECTIVIDAD de tests (coverage miente): ingesta el `mutations.xml` de PIT,
14
+ reporta mutation score + **test-strength** (matados/cubiertos) + hotspots.
15
+ - Invariante **Tests efectivos** agregada a `templates/CONSTITUTION.md`.
16
+ - Tier scheduled/incremental (PIT es caro), no inner loop.
@@ -0,0 +1,20 @@
1
+ # dev-loop-kit 1.2.3
2
+
3
+ ## Fixes (qa_ledger.py) — verificados por doble review adversarial (judgment-day, 2 rondas, 0 critical)
4
+ - pit-check: NON_VIABLE + RUN_ERROR excluidos del denominador (antes inflaban el score y
5
+ podían tirar un BELOW-GATE falso). Nuevo campo `excluded`.
6
+ - simplicity-check: parser robusto con máquina de estados `in_hunk` — una línea agregada
7
+ `++ x` (que en el diff aparece como `+++ x`) ya no se confunde con un header y deja de
8
+ perder el conteo del resto del hunk.
9
+ - simplicity-check: skip-set más angosto (`_SIMPLICITY_SKIP`) — código en paquetes
10
+ `generated`/`out`/`bin`/`dist` ya no se descarta del budget.
11
+
12
+ ## Nuevo (CONSTITUTION.md)
13
+ - Invariante "Integridad del gate — no gamear al que mide": un diff que debilita el aparato
14
+ de medición (tests/lint/thresholds) o reescribe una masa de asserts = BLOCKER; checker
15
+ no-correlacionado para alto blast-radius. (Osmani, "Agentic Code Review".)
16
+
17
+ ## Limitación conocida (documentada)
18
+ - simplicity-check espera diffs formato git (`diff --git` por archivo, como `git diff` y
19
+ `--from-git`). Un `diff -u` plano de POSIX sin esos headers miscuenta archivos después
20
+ del primero. Usar `--from-git` o `git diff > file`.
@@ -0,0 +1,10 @@
1
+ # dev-loop-kit 1.2.4
2
+
3
+ ## Nuevo: gate-check (integridad del gate — "no gamear al que mide")
4
+ - Comando `qa_ledger.py gate-check`: detecta si un diff DEBILITA el aparato de medición.
5
+ - **BLOCKER (exit 1):** tests borrados o deshabilitados, thresholds de coverage/mutation bajados.
6
+ - **Revisar (soft):** supresiones de lint agregadas, asserts removidos en tests. `--strict` los gatea.
7
+ - Lee diffs formato git (`--from-git` / `--diff` / stdin). stdlib pura.
8
+ - Implementa en CÓDIGO la invariante "Integridad del gate" de la CONSTITUTION (antes solo texto).
9
+ - (Osmani, "Agentic Code Review": los red-flags para revisores humanos; el detector automático
10
+ y el principio "el aparato no lo modifica el cambio que mide" son síntesis del kit.)
@@ -0,0 +1,23 @@
1
+ # dev-loop-kit 1.2.5 — destilación ("gates de hechos bloquean, adivinadores de prosa avisan")
2
+
3
+ ## Nuevo: golden-diff + INV-GOLDEN-01 (golden/approval testing)
4
+ - `qa_ledger.py golden-diff`: byte-compara `*.received.*` vs `*.approved.*`. Cualquier no-match
5
+ o `.received` sin `.approved` = DIVERGE (exit 1). Es un HECHO, no un juicio. Nunca toca `.approved`.
6
+ - Invariante `INV-GOLDEN-01` (CWE-440) en CONSTITUTION: en migraciones, golden capturado ANTES de
7
+ tocar el módulo; el golden lo captura un script sobre el código ORIGINAL, el agente no lo authorea.
8
+ - (Del HANDOFF de golden testing — pendiente a mano: skill `characterize`, hook PreToolUse que bloquee
9
+ escrituras a `.approved`, `.gitattributes *.approved.* binary`, y decisión de stack del harness.)
10
+
11
+ ## Destilación (distinción hechos vs adivinanza)
12
+ - `spec-check` → **ADVISORY** por defecto (heurística sobre prosa): reporta, NO bloquea. `--strict` gatea.
13
+ - `simplicity-check` → el regex de "abstracciones" (falsos positivos con records/DTOs) sale del score y
14
+ del hard-cap; queda como métrica/flag informativa. Los caps numéricos (líneas/archivos/anidación) siguen gateando.
15
+
16
+ ## Fixes de spec-check (judgment-day Ronda 1, 2 jueces, 2 critical + 4 warnings, todos resueltos)
17
+ - Code-fence tracking (un `# Exclusions` dentro de ```` ``` ```` ya no cuenta), setext headings,
18
+ checkbox vacío no cuenta como criterio, `_SC_ACCEPT` anclado (no "Success/Exit criteria"),
19
+ `\d` suelto ya no exime vago, stack ambiguo podado + scopeado a criterios, vagos en español sumados.
20
+
21
+ ## Gates: hechos (bloquean) vs prosa (avisan)
22
+ - BLOQUEAN (leen hechos): golden-diff, pit-check, gate-check, readiness/rebuild, caps de simplicity.
23
+ - AVISAN (heurística): spec-check, abstracciones de simplicity.
@@ -0,0 +1,11 @@
1
+ # dev-loop-kit 1.2.6 — fix golden-diff (judgment-day, 2 jueces, coincidencia)
2
+
3
+ ## Fix (cmd_golden_diff) — verificado contra los casos reproducidos por los jueces
4
+ - **[CRITICAL] `str.replace` global** reemplazaba TODAS las ocurrencias de `.received.`:
5
+ con el marcador en una carpeta padre (snapshots por fecha) o dos veces en el nombre,
6
+ derivaba el `.approved` mal → DIVERGE falso, y en el peor caso un **FALSE CLEAN** (dejaba
7
+ pasar una divergencia, exit 0). Fix: `_golden_approved_path()` deriva solo del BASENAME,
8
+ última ocurrencia (rpartition), nunca del path del directorio.
9
+ - **[real] directorios `*.received.*`** se escaneaban como fixtures → filtro `os.path.isfile`.
10
+ - **[gap] `Foo.received` sin extensión** se salteaba (false CLEAN) → segundo glob `*.received`.
11
+ - Read-only confirmado por ambos jueces: cero write/rename; el agente no puede authorear el golden.
@@ -0,0 +1,15 @@
1
+ # dev-loop-kit 1.2.7 — skill reverse-discovery (front brownfield)
2
+
3
+ ## Nuevo skill: reverse-discovery
4
+ - Hermano brownfield de `discovery`, para migrar/modernizar un sistema EXISTENTE.
5
+ - El inverso de discovery: el sistema ya existe y su comportamiento ES la verdad → se
6
+ EXTRAEN hechos, no se propone forma.
7
+ - **Facts-only por diseño:** produce SOLO (1) un mapa del sistema (endpoints, contratos,
8
+ grafo de dependencias, candidatos de módulo vía análisis estático) y (2) un golden suite
9
+ capturado mecánicamente en los bordes. NO authorea SPEC/ADR inferidos — eso lo escribe el
10
+ humano leyendo los hechos; las decisiones forward de módulos van a `/adr-refine`.
11
+ - Hereda INV-GOLDEN-01: el agente nunca toca los `.approved`.
12
+ - Cae limpio del lado "hechos" de la línea hechos-vs-prosa: análisis estático + byte-captura,
13
+ cero heurística sobre prosa → nada frágil que se rompa.
14
+ - Flujo migración: reverse-discovery → humano escribe SPEC + /adr-refine → /dev-loop
15
+ (golden-diff + ApplicationModules.verify() verdes) → readiness + human gate.
@@ -0,0 +1,24 @@
1
+ # dev-loop-kit 1.2.8 — golden testing completo (characterize + hook + gitattributes)
2
+
3
+ ## Nuevo skill: characterize (golden-capture)
4
+ - Captura el golden del código ORIGINAL con inputs reales, por un harness determinístico.
5
+ - 4 fases: harness → checklist de no-determinismo (locale es-AR obligatorio) → captura
6
+ (.received) → STOP para aprobación humana. NUNCA crea/edita un .approved.
7
+ - Lo orquesta reverse-discovery en su fase 2.
8
+
9
+ ## Nuevo hook: hooks/block-approved-writes.ps1 (PreToolUse, INV-GOLDEN-01)
10
+ - Bloquea mecánicamente que el agente escriba/renombre cualquier *.approved.* (Write/Edit/
11
+ Bash-write → exit 2). Deja pasar lecturas. Probado.
12
+ - WIRING (pegar en settings.json — no lo puede hacer el agente por el guardrail de self-mod):
13
+ "hooks": { "PreToolUse": [ { "matcher": "*", "hooks": [
14
+ { "type": "command",
15
+ "command": "powershell -NoProfile -ExecutionPolicy Bypass -File \"<KIT>/hooks/block-approved-writes.ps1\"" }
16
+ ] } ] }
17
+
18
+ ## Nuevo: templates/.gitattributes
19
+ - `*.approved.* binary` + `*.received.* binary` — line endings no generan diffs falsos
20
+ (crítico Windows/SQL Server). Copiar a la raíz del repo de migración.
21
+
22
+ ## Estado del golden testing (del HANDOFF) — AHORA completo en el kit
23
+ - golden-diff (gate, byte-compare) ✓ · characterize (captura) ✓ · reverse-discovery (front
24
+ brownfield) ✓ · INV-GOLDEN-01 (CONSTITUTION) ✓ · hook PreToolUse ✓ · .gitattributes ✓
@@ -0,0 +1,4 @@
1
+ # dev-loop-kit 1.2.9 — documentación generalizada
2
+ - Removidas todas las referencias a proyecto/dominio específico del CONSTITUTION template
3
+ y de los skills characterize/reverse-discovery (ejemplos fiscales → genéricos; locale es-AR
4
+ → "target locale"; money-path → "camino crítico"). El kit queda dominio-agnóstico.
@@ -0,0 +1,29 @@
1
+ # dev-loop-kit 1.20.0 — instalación global (2026-07-03)
2
+
3
+ Release chica de instalabilidad: el kit ahora puede instalarse UNA vez en
4
+ `~/.claude/` y quedar disponible para todos los proyectos, existentes y nuevos.
5
+ Sin cambios de engine. Smoke suite: 121/121.
6
+
7
+ ## Qué cambia
8
+
9
+ - **Resolución portable del engine**: `dev-loop` y `sys-doc` resuelven
10
+ `qa_ledger.py` primero en el proyecto (`./.claude/skills/dev-loop/`) y caen a
11
+ la instalación global (`~/.claude/skills/dev-loop/`) si no hay local. La
12
+ instalación por proyecto sigue teniendo precedencia (un proyecto puede pinnear
13
+ su versión del kit copiándolo local).
14
+ - **Instalación global documentada completa** (README § Instalación, Opción B):
15
+ las SEIS skills (antes el snippet copiaba solo dev-loop y sys-doc — quedó de
16
+ cuando eran las únicas dos) + el hook `block-approved-writes.ps1` a
17
+ `~/.claude/hooks/` con registro en `~/.claude/settings.json`, para que
18
+ INV-GOLDEN-01 rija en todos los proyectos.
19
+ - **Qué sigue siendo por-proyecto** (estado, no instalable — ahora explícito):
20
+ `dev-loop.config.json` (los `path` son relativos a la raíz de la run, y la
21
+ quality bar declarada del humano vive ahí — 1.17.0), `QA-LEDGER.json`,
22
+ `ACCEPTANCE.md`, y el `.gitattributes` de templates para migración.
23
+ - Nota para la máquina donde se DESARROLLA el kit: junction/symlink por skill al
24
+ repo canónico en vez de copia — global siempre al día con main.
25
+
26
+ ## Diferido consciente
27
+
28
+ - Un comando `install.sh`/`install.ps1` empaquetado: hoy son dos líneas de shell
29
+ documentadas; si el on-ramp real (dogfooding) muestra fricción, se mecaniza.
@@ -0,0 +1,33 @@
1
+ # dev-loop-kit 1.21.0 — namespace specloop-* (2026-07-03)
2
+
3
+ Release de naming, decidida por el humano entre tres opciones (prefijo largo /
4
+ prefijo corto / dejar como está): las seis skills pasan de nombres genéricos a
5
+ **namespace `specloop-*`**. Nombres como `discovery` o `characterize` eran
6
+ squatting del namespace global — cualquier otro kit podía pisarlos (evidencia:
7
+ el harness ya desambiguaba proyecto-vs-global con los genéricos). Se hace AHORA
8
+ porque es el momento más barato: antes de publicar, renombrar no rompe a nadie.
9
+ Sin cambios de engine. Smoke suite: 121/121.
10
+
11
+ ## El mapa de renombres
12
+
13
+ | Antes | Ahora |
14
+ |---|---|
15
+ | `/discovery` | `/specloop-discovery` |
16
+ | `/adr-refine` | `/specloop-adr-refine` |
17
+ | `/reverse-discovery` | `/specloop-reverse-discovery` |
18
+ | `/characterize` | `/specloop-characterize` |
19
+ | `/dev-loop` | `/specloop-devloop` |
20
+ | `/sys-doc` | `/specloop-sysdoc` |
21
+
22
+ - **El path del engine cambia** (breaking para scripts que lo hardcodeaban):
23
+ `.claude/skills/dev-loop/qa_ledger.py` → `.claude/skills/specloop-devloop/qa_ledger.py`.
24
+ El fallback global (1.20.0) apunta ahora a `~/.claude/skills/specloop-devloop/`.
25
+ - **NO cambian**: `dev-loop-kit` (el nombre del kit), `dev-loop.config.json`
26
+ (el archivo de config), los subcomandos del engine, ni el schema del ledger.
27
+ - Instalación global: re-crear los junctions/copias con los nombres nuevos
28
+ (los viejos quedan rotos tras el rename de directorios).
29
+ - Sweep completo con truth-pass: SKILL.md (frontmatter + referencias cruzadas),
30
+ READMEs, templates/CLAUDE.md, decks ES/EN, playbooks ES/EN, onepagers,
31
+ pitches, skills-referencia, smoke. Los artefactos HISTÓRICOS (audits/*.json,
32
+ changelogs viejos, HANDOFFs de releases pasadas) NO se retro-editan —
33
+ documentan el estado de su época.
@@ -0,0 +1,60 @@
1
+ # dev-loop-kit 1.22.0 — doctor: diagnóstico de la instalación (2026-07-03)
2
+
3
+ `qa_ledger.py doctor` (subcomando 22) — espíritu flutter doctor: verifica que
4
+ spec-loop esté instalado y sano en la máquina de trabajo, Windows y Linux.
5
+ Smoke suite: 124/124.
6
+
7
+ ## La idea
8
+
9
+ - **El engine verifica su PROPIA instalación**: las skills viven al lado de
10
+ `qa_ledger.py` (sea `~/.claude/skills/` global o `.claude/skills/` del
11
+ proyecto), así que doctor inspecciona a sus hermanos y reporta qué modo de
12
+ instalación detectó.
13
+ - **Output ASCII puro** (`[OK]` / `[ !]` / `[ X]`): es la herramienta de primer
14
+ contacto en máquinas vírgenes — no puede depender del encoding de la consola.
15
+ - **Exit 1 SOLO con errores**; los avisos no fallan (un toolchain ausente en la
16
+ máquina puede vivir en CI). `--json` para consumo programático.
17
+
18
+ ## Qué chequea
19
+
20
+ | Check | Nivel si falla |
21
+ |---|---|
22
+ | Python ≥ 3.8 · git en PATH | error |
23
+ | Las 6 skills junto al engine, con frontmatter `name:` verificado | error |
24
+ | Hook INV-GOLDEN-01: archivo presente · registrado en `settings.json` (PreToolUse) · intérprete powershell/pwsh disponible | aviso (cada capa faltante, con el remedio) |
25
+ | `dev-loop.config.json` del cwd: parseable, versión, repos | error si inválido · aviso si ausente |
26
+ | ACCEPTANCE del config: criterios + AC-IDs trazables | aviso |
27
+ | Skills de QA de `qa_tools_order` (default code-review/judgment-day/improve) presentes en `.claude/skills` del proyecto o del usuario — el loop las orquesta sin traerlas | aviso (las built-in del harness no son detectables por filesystem, y lo dice) |
28
+ | Ledger: carga + checksum de integridad (1.13.0) | **error** si corrupto/mutado |
29
+ | Toolchain primario por repo type (`mvn`/`pytest`/`npm`/`go`/`cargo`/`dotnet`/`ctest`/`gradlew` del repo/`swift`/`flutter`) | aviso |
30
+
31
+ ## El doctor no solo diagnostica: cura
32
+
33
+ Cada aviso/error lleva su **remedio accionable** — el comando o el link de
34
+ instalación (python.org, git-scm, maven/flutter/nodejs/go/rustup/dotnet/cmake/
35
+ gradle/swift, pwsh para el hook en Linux, y los pasos del propio kit para
36
+ skills/hook/config faltantes). Espíritu flutter doctor completo: corrés
37
+ `doctor`, instalás lo listado, re-corrés hasta verde.
38
+
39
+ ## Hardening (review fresco pre-commit, aplicado)
40
+
41
+ - `startswith` sin separador final clasificaba `~/.claude/skills-evil/` como
42
+ instalación global → separador + `normcase` (case de drive en Windows).
43
+ - El header humano usaba em-dash y el JSON `ensure_ascii=False` — violaba la
44
+ promesa ASCII → header ASCII y `ensure_ascii=True` (el doctor promete bytes
45
+ ASCII SIEMPRE, es la herramienta de primer contacto).
46
+ - Detección de registro del hook por substring en la rama PreToolUse: límite
47
+ disclosed en el docstring (falso positivo rebuscado, dirección benigna).
48
+
49
+ ## Notas de plataforma
50
+
51
+ - Linux: el hook es PowerShell — doctor avisa si no hay `pwsh` (instalarlo o
52
+ portar el twin bash, diferido consciente desde el header del .ps1).
53
+ - Windows: junctions de instalación global se atraviesan con normalidad
54
+ (doctor clasifica global vs por-proyecto por el path real del engine).
55
+
56
+ ## Smoke
57
+
58
+ - **T42**: doctor en sandbox → exit 0 con config y ACCEPTANCE leídos, 6/6
59
+ skills, install clasificada por-proyecto · ledger corrupto → exit 1 (la
60
+ integridad del ledger es error, no aviso).