@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,55 @@
1
+ # uscha-kit 1.34.0 — mirador: project name + live second-screen view (2026-07-05)
2
+
3
+ Two operator asks: (1) the project name, clear at the top; (2) a mirador you can **watch on a
4
+ second screen** while you keep coding in the terminal with the agent. Smoke suite: 195/195.
5
+
6
+ ## Project name (from config, shown at the top)
7
+
8
+ - `dashboard` emits `project` from `uscha.config.json`'s `"project"` (or `"name"`); if unset, it
9
+ falls back to the joined repo names (truth-pass — no invented name). This is the ONE engine
10
+ touch this release: a project name is human-set config metadata, not vendor/LLM data, so the
11
+ engine stays model-agnostic.
12
+ - `uscha-discovery` asks the project name **first** and writes it to the config.
13
+ - The mirador template shows it as a **prominent title** at the top.
14
+
15
+ ## Live second-screen view
16
+
17
+ - **`mirador-render.py`** (standalone, in the skill folder): runs `dashboard --json`, merges the
18
+ sidecar telemetry (in the adapter, NEVER the engine), injects `const DATA`, writes
19
+ `mirador.html`. `--refresh N` injects a `<meta http-equiv="refresh">`.
20
+ - **`mirador-watch.sh` / `.ps1`**: re-render every N seconds (default 30) from the current
21
+ ledger. Open `mirador.html` on a second monitor; it reloads on its own. **No server.**
22
+ - Honest limit: as live as the **ledger** — the picture changes at the dev-loop's measured
23
+ checkpoints (snapshots, gates, `readiness --record`), not per keystroke.
24
+
25
+ ## Watch-safe telemetry (gotcha fix)
26
+
27
+ `telemetry-extract.py` now **upserts by session** (default key = transcript basename): a refresh
28
+ loop re-runs the extractor without duplicating the session's line, so the aggregated totals
29
+ never inflate. (Without this, watch mode would over-count tokens every tick.)
30
+
31
+ ## Boundary intact
32
+
33
+ Telemetry is still vendor-narrated, merged ONLY by the adapter (`mirador-render.py`), never by
34
+ `qa_ledger.py`; shown, never gated, never fed into readiness. The template still degrades (no
35
+ sidecar → no strip).
36
+
37
+ ## Truth-pass fix: skill-count drift (doctor + plugin)
38
+
39
+ When `uscha-mirador` shipped (1.32.0), two registries were not updated (caught here):
40
+ - `doctor`'s `USCHA_SKILLS` list was missing `uscha-mirador` → it reported `7/7` for 8 skills.
41
+ - `plugin.json` / `marketplace.json` **descriptions** said "7 skills / 24 subcommands" (the
42
+ plugin *install* was fine — it points at the skills directory — but the prose was stale).
43
+ Both fixed (8 skills / 25 subcommands), and **mechanized** so it can't drift again: smoke
44
+ **T57** asserts `USCHA_SKILLS` equals the `uscha-*` skill directories on disk. (The doc-version
45
+ gate T52 covers versions; T57 now covers the skill roster.)
46
+
47
+ ## Smoke (T54 upsert + T55 project + T56 render + T57 skill-roster)
48
+
49
+ Extractor re-run → still ONE line (upsert); config `project` → dashboard emits it, absent →
50
+ repo-name fallback; `mirador-render.py` → `mirador.html` carrying the project name, merged
51
+ telemetry, and the meta-refresh; the doctor's skill list matches the skills on disk.
52
+
53
+ The fresh review also caught two robustness gaps in `mirador-render.py` (unguarded `json.loads`
54
+ and template read) — now they return a clean error instead of a traceback (it runs in a watch
55
+ loop, so a clean non-zero is what the loop expects).
@@ -0,0 +1,30 @@
1
+ # CHANGELOG 1.35.0 — execution policy routing
2
+
3
+ ## La idea
4
+
5
+ Uscha ya medía el estado, y Mirador ya mostraba telemetría observada de modelo/tiempo/tokens. Faltaba una capa distinta: **la política de ejecución** que le dice al operador qué está haciendo el método en cada fase y qué tier/modelo/effort conviene usar.
6
+
7
+ ## Nuevo
8
+
9
+ - `defaults.execution_policy` en `uscha.config.json`:
10
+ - `default`: tier/model/effort base.
11
+ - `phases.<idea|disc|spec|adr|build|qa|verify|prod>`: `method`, `tier`, `model`, `effort`, `uncorrelated`.
12
+ - Nuevo subcomando read-only:
13
+ - `qa_ledger.py execution-policy --phase qa`
14
+ - `qa_ledger.py execution-policy --json`
15
+ - `dashboard --json` agrega:
16
+ - `execution_policy` top-level.
17
+ - `phase.execution` en cada nodo del trail.
18
+ - Mirador renderiza un panel **Execution policy** con metodología/tier/model/effort por fase.
19
+ - `uscha-devloop` ahora exige imprimir una línea antes de arrancar cada fase para que el humano vea la intención del método y la selección de modelo/effort.
20
+
21
+ ## Doctrina
22
+
23
+ Esto **no** es un gate y **no** entra al score de readiness. Es metadata de orquestación/costo/riesgo. La regla sigue intacta: facts block, guesses advise; la política guía la ejecución, la evidencia decide.
24
+
25
+ ## Smoke
26
+
27
+ - `bash uscha-kit/tests/smoke-engine.sh`: 199 ok / 0 fail.
28
+ - Nuevos checks:
29
+ - T58: `execution-policy` devuelve metodología/model/effort y `dashboard` lo expone sin contaminar readiness.
30
+ - T59: `mirador-render` preserva el panel bird's-eye de execution_policy.
@@ -0,0 +1,33 @@
1
+ # CHANGELOG 1.36.0 — discovery intake: production findings + spec-doubt
2
+
3
+ ## La idea
4
+
5
+ Uscha no puede terminar en el merge si la realidad vuelve con evidencia. Dos señales antes quedaban fuera del ledger: un bug encontrado en producción y una SPEC que el builder descubre dudosa o equivocada. Este release les da una entrada formal al siguiente ciclo de discovery.
6
+
7
+ ## Nuevo
8
+
9
+ - `qa_ledger.py production-finding`:
10
+ - crea `PF-001`, `PF-002`, ... con `repo`, `severity`, `source`, `title`, `evidence`.
11
+ - `--resolve --id PF-001 --note "..."` registra que el hallazgo entró al próximo ciclo.
12
+ - `qa_ledger.py spec-doubt`:
13
+ - crea `SD-001`, `SD-002`, ... con `kind` (`spec-doubt`, `spec-wrong`, `ambiguous`, `missing-acceptance`), `repo`, `severity`, `note`, `evidence`.
14
+ - `--resolve --id SD-001 --decision "SPEC amended"` registra la revisión humana.
15
+ - `readiness --json` agrega `discovery_intake` y facts:
16
+ - `production_findings_open`
17
+ - `spec_doubts_open`
18
+ - La vista default de `readiness` habla solo cuando importa: imprime una línea si hay production findings o spec-doubts abiertos.
19
+ - `phase` deriva `escalated` si hay `spec-doubt` abierto o production finding gateado abierto; no hay `pr-ready` mientras la SPEC esté dudosa o la producción haya devuelto una señal severa.
20
+ - `dashboard --json` transporta `discovery_intake` para Mirador/adapters.
21
+
22
+ ## Doctrina
23
+
24
+ - Production finding = hecho de campo. Entra al ledger, no a una nota perdida.
25
+ - SPEC-WRONG/spec-doubt = escape hatch legítimo del builder. El agente NO decide silenciosamente entre desviarse de la SPEC o transcribir una mentira.
26
+ - Resolver exige un acto registrado: feedback incorporado, SPEC amendada, o revisión humana explícita.
27
+
28
+ ## Smoke
29
+
30
+ - `bash uscha-kit/tests/smoke-engine.sh`: 207 ok / 0 fail.
31
+ - Nuevos checks:
32
+ - T60: production finding crea PF-001, aparece en `discovery_intake`, avisa en readiness default, y se limpia al resolver.
33
+ - T61: spec-doubt crea SD-001, aparece en `discovery_intake`, `phase` deriva `escalated`, y se limpia al resolver.
@@ -0,0 +1,41 @@
1
+ # CHANGELOG 1.37.0 ? ADR experiments: visible hypotheses
2
+
3
+ Decision #10 from the deferred analysis is implemented narrowly: an ADR can now say
4
+ `Status: Experiment` when the decision is a bounded hypothesis that needs real feedback.
5
+
6
+ ## What changed
7
+
8
+ - `dashboard --json` now preserves both ADR layers:
9
+ - legacy `status` remains the coarse Mirador bucket (`done` / `prog` / `todo`);
10
+ - `adr_status` preserves the authored ADR status (`accepted`, `proposed`, `experiment`, etc.).
11
+ - Experimental ADRs carry advisory metadata:
12
+ - `experiment_valid`
13
+ - `experiment_missing`
14
+ - `review_by`
15
+ - `review_trigger`
16
+ - `feedback_signal`
17
+ - `expired`
18
+ - Top-level `adr_experiments` summarizes `open`, `malformed`, `expired`, and `ids`.
19
+ - Mirador renders experiment ADRs as `experiment`, `experiment invalid`, or `experiment overdue`.
20
+ - `Status: Experiment` is intentionally **not** readiness scoring and **not** a hard PR gate.
21
+
22
+ ## Required sections for `Status: Experiment`
23
+
24
+ An experiment ADR must include:
25
+
26
+ - `Hypothesis`
27
+ - `Feedback Signal`
28
+ - `Review By` or `Review Trigger`
29
+ - `Promote Criteria`
30
+ - `Rollback / Supersede Criteria`
31
+
32
+ Missing/expired metadata is visible/advisory so the operator does not confuse a hypothesis with
33
+ an accepted decision.
34
+
35
+ ## Smoke
36
+
37
+ - Full smoke: `209 ok ? 0 fail`.
38
+ - New T62 covers:
39
+ - valid experiment ADR exposed in `dashboard --json`;
40
+ - malformed/expired experiment counted as advisory;
41
+ - Mirador renders `experiment` without changing readiness.
@@ -0,0 +1,11 @@
1
+ # uscha-kit 1.38.0 ? contract closure loop
2
+
3
+ ## Added
4
+
5
+ - `spec-change-request`: records a human contract-change bridge from evidence/doubt to SPEC/ADR amendment, surfaces it in `readiness --json`, and derives `phase=escalated` while open.
6
+ - `golden-diff --labels golden-labels.json --json`: reports approved fixture intent as `intended`, `observed-accidental`, or `unknown` without weakening byte comparison.
7
+ - `summary --json` now includes `post_merge_calibration` counts for production findings, SPEC doubts, and SPEC change requests so retros can calibrate the method from real reopen signals.
8
+
9
+ ## Verification
10
+
11
+ - Smoke target: 216/216 green.
@@ -0,0 +1,14 @@
1
+ # uscha-kit 1.39.0 ? universal machine installer
2
+
3
+ ## Added
4
+
5
+ - `install-uscha.py`: one stdlib machine installer for Codex and Claude.
6
+ - Codex target creates a personal local plugin at `~/plugins/uscha` and updates `~/.agents/plugins/marketplace.json`.
7
+ - Claude target installs global skills/hooks under `~/.claude`.
8
+ - Supports `install`, `doctor`, `version`, `init`, `--dry-run`, testable `--home`, and `--mode copy|link`.
9
+ - Codex plugin manifest at `.codex-plugin/plugin.json` for plugin-first adoption.
10
+ - Skill fallback examples now include Codex plugin/raw-skill install paths, not only `~/.claude`.
11
+
12
+ ## Verification
13
+
14
+ - Smoke target: 222/222 green.
@@ -0,0 +1,68 @@
1
+ # dev-loop-kit 1.4.0 — adapter `type: python` (2026-07-02)
2
+
3
+ El desbloqueante del dogfooding: el engine ahora mide repos Python (pytest + ruff +
4
+ mypy + coverage.py). Diseño: `docs/HANDOFF_python-adapter_dev-loop-kit.md` +
5
+ `docs/HANDOFF_python-adapter_ADDENDUM.md` (los 2 bugs A1/A2 de la revisión están
6
+ incorporados). Smoke suite: 24/24 (17 existentes maven/flutter intactos + 7 python).
7
+
8
+ ## Engine (qa_ledger.py)
9
+
10
+ - **`python_coverage()`** — Cobertura `coverage.xml` (`pytest --cov --cov-report=xml`),
11
+ atributos `lines-covered`/`lines-valid`; primer match gana (`coverage.xml`,
12
+ `reports/coverage.xml`). Reporte ausente o ilegible ⇒ `report_found: False`
13
+ (UNMEASURED — jamás un número inventado).
14
+ - **`python_test_count()`** — JUnit XML de `pytest --junitxml`. **Maneja el root
15
+ ENVUELTO** (`<testsuites><testsuite …>`, familia xunit2 del pytest moderno) además
16
+ del root plano — leer atributos del wrapper contaría 0 en silencio (ADDENDUM A2).
17
+ - **`parse_ruff()`** — `ruff check --output-format=json`; severidades: `S<dígito>`
18
+ (bandit/security, anclado para no pisar SIM/SLF/SLOT) y `E9*`/`F82*` (errores
19
+ reales) → HIGH, `B<dígito>` (bugbear) → MEDIUM, resto → LOW. **`code: null`**
20
+ (syntax error del ruff moderno ≥0.5) → HIGH siempre — sin esto, la rotura real
21
+ pasaba el gate como LOW en silencio (hallazgo del review de contexto fresco).
22
+ - **`parse_mypy()`** — salida de texto (`file:line: error: msg [code]`);
23
+ error → HIGH, warning → MEDIUM, note → INFO.
24
+ - **`ingest-gate`** — branch por `type` del repo: python busca `reports/ruff.json` /
25
+ `reports/mypy.txt` (o `--ruff`/`--mypy` explícitos); combinado se loguea como
26
+ `python-qa-gate`. Mismo contrato de ausencia que Java.
27
+ - **Guard UNMEASURED ampliado** (ADDENDUM A1): `static_unmeasured` ahora aplica a
28
+ `maven` Y `python` — un repo python cuyos linters nunca corrieron puntúa 0.0 en la
29
+ dimensión static, no 1.0 (habría reintroducido "el silencio es éxito" justo en el
30
+ repo del dogfooding).
31
+ - **LOC python**: test = `tests/`/`test/` en el path o `test_*.py`/`*_test.py`;
32
+ prod = el resto de los `.py` (src layout o paquete raíz). `SKIP_DIRS` suma
33
+ `.venv`, `venv`, `.tox`, `__pycache__`, `.mypy_cache`, `.ruff_cache`,
34
+ `.pytest_cache` (sin esto, el LOC contaría el virtualenv entero).
35
+ - `_rel_src()` reconoce paths repo-relativos `src/...` (como los emiten ruff/mypy)
36
+ para IDs de finding estables. **Nota de migración**: si un linter Java emitía paths
37
+ relativos `src/...` (poco común — checkstyle/PMD suelen emitir absolutos), sus
38
+ finding-IDs cambian en 1.4.0; un ledger no debería cruzar el upgrade — re-baselinear
39
+ con un ingest fresco después de actualizar.
40
+ - `SOURCE_EXT`: `"python": {".py"}`; `.py` también en `generic`.
41
+
42
+ ## Ya cubierto sin cambios (ADDENDUM A3)
43
+
44
+ - simplicity-check: `.py` ya estaba en `_SIMPLICITY_CODE_EXT`.
45
+ - gate-check: ya reconocía `test_*.py`, `def test_`, `# noqa`, `# type: ignore`.
46
+
47
+ ## Config / docs
48
+
49
+ - `dev-loop.config.json`: `test_command_python` + repo de ejemplo `data-lib`; version 1.4.0.
50
+ - Kit README: tabla de reportes Python; SKILL.md y docs actualizan
51
+ `maven|flutter` → `maven|flutter|python`; refs de versión vigente → 1.4.0 (ES+EN).
52
+
53
+ ## Smoke (tests/smoke-engine.sh)
54
+
55
+ - `repo-c` (`type: python`) con fixtures sintéticos: Cobertura 8/10 → 80.0%,
56
+ junit ENVUELTO → 5 tests, LOC prod=3/test=2, ruff 2 findings (1 gateado),
57
+ mypy 1 error (gateado), UNMEASURED pre-ingest → medido post-ingest.
58
+
59
+ ## Criterio pendiente (fase piloto)
60
+
61
+ Criterio 2 del HANDOFF — dry-run de solo lectura contra el repo piloto real
62
+ (coverage ±0.1 vs pytest-cov, test count exacto, ruff/mypy en 0 sin UNMEASURED).
63
+ Se corre desde la sesión del proyecto piloto consumiendo este kit.
64
+
65
+ ## Diferido consciente (sin cambios desde 1.3.0)
66
+
67
+ - rebuild: densidad de asserts por test-file en la firma.
68
+ - Perfiles A-E mecanizados (`--profile`).
@@ -0,0 +1,16 @@
1
+ # uscha-kit 1.40.0
2
+
3
+ ## Added
4
+
5
+ - Added npm/npx package router (`@andresmassello/uscha`) with `uscha` and `uscha-kit` CLI bins.
6
+ - Added smoke coverage for the Node router delegating to the canonical Python installer.
7
+
8
+ ## Changed
9
+
10
+ - Documented npm-first install commands for public adoption while keeping `install-uscha.py` as the source of truth.
11
+
12
+ ## Verification
13
+
14
+ - `node bin/uscha.js version --json`
15
+ - `npm pack --dry-run --json`
16
+ - `bash uscha-kit/tests/smoke-engine.sh`
@@ -0,0 +1,11 @@
1
+ # uscha-kit 1.40.1
2
+
3
+ ## Fixed
4
+
5
+ - Exclude Python bytecode artifacts (`__pycache__`, `*.pyc`, `*.pyo`) from the npm package tarball.
6
+ - Strengthen the npm smoke check so package dry-run fails if Python bytecode artifacts would be published.
7
+
8
+ ## Verification
9
+
10
+ - `npm publish --dry-run --access public`
11
+ - `bash uscha-kit/tests/smoke-engine.sh`
@@ -0,0 +1,64 @@
1
+ # dev-loop-kit 1.5.0 — adapter `type: node` (TypeScript/JS) (2026-07-02)
2
+
3
+ El adapter más barato del roadmap: reusa el parser lcov (Flutter) para coverage y el
4
+ parser JUnit-envuelto (Python) para tests; lo único nuevo son los parsers de ESLint y
5
+ tsc. Smoke suite: 31/31 (24 previos intactos + 7 node).
6
+
7
+ ## Engine (qa_ledger.py)
8
+
9
+ - **`type: node`** — TypeScript/JS (jest o vitest sobre Node).
10
+ - Coverage: `coverage/lcov.info` — **mismo parser que Flutter** (`flutter_coverage`,
11
+ docstring generalizado).
12
+ - Test count: **`junit_test_count()`** (renombrado desde `python_test_count` — es de
13
+ familia JUnit, no de lenguaje): jest-junit / `vitest --reporter=junit`, root envuelto
14
+ `<testsuites>` soportado, first-location-wins.
15
+ - **`parse_eslint()`** — `eslint --format json`: severity 2 (error) → HIGH,
16
+ 1 (warn) → MEDIUM; reglas `security/*` con floor HIGH (como findsecbugs);
17
+ **`fatal: true`** (error de parseo) → HIGH siempre — la lección del `code: null`
18
+ de ruff (1.4.0). `ruleId: null` SIN fatal (directivas eslint-disable sin usar,
19
+ default de ESLint 9) sigue la severidad del mensaje — nunca un falso blocker.
20
+ - **`parse_tsc()`** — `tsc --noEmit` texto (`file(line,col): error TSxxxx`):
21
+ error → HIGH (un type error es rotura real); paths Windows con drive soportados.
22
+ **Errores GLOBALES sin archivo** (TS18003/TS5023 — tsconfig roto) → HIGH con
23
+ ubicación `?`: un tsc que no chequeó NADA jamás lee como gate limpio.
24
+ - IDs de finding node preservan el path repo-relativo (`_mk_id_rel`): los layouts
25
+ sin `src/` (app/pages con decenas de `page.tsx`/`index.ts`) no colisionan por
26
+ basename; los paths absolutos de eslint se relativizan a la base del repo.
27
+ - `ingest-gate`: branch node (`reports/eslint.json` / `reports/tsc.txt`, o
28
+ `--eslint/--tsc`); combinado = `node-qa-gate`. Mismo contrato de ausencia.
29
+ - Guard UNMEASURED ampliado: `maven`/`python`/`node`.
30
+ - LOC node: test = `__tests__`/`tests`/`test` en el path o `*.test.*`/`*.spec.*`;
31
+ prod = el resto. `SOURCE_EXT` node: `.ts .tsx .js .jsx .mjs .cjs`.
32
+ - `SKIP_DIRS` += `coverage` (el lcov-report HTML de jest contiene .js que
33
+ contaminaría el LOC), `.next`, `.turbo`. **Trade-off documentado**: el set es
34
+ global — un paquete fuente legítimamente llamado `coverage/` en cualquier type
35
+ queda fuera del LOC (métrica advisory; la detección de lcov es por glob y no
36
+ se afecta).
37
+ - `LICENSE` (MIT) agregada al repo y copiada dentro de `dev-loop-kit/` para que
38
+ el zip la lleve.
39
+
40
+ ## Config / docs
41
+
42
+ - `test_command_node` + repo de ejemplo `web-app`; version 1.5.0 (triple sync).
43
+ - Kit README: tabla TypeScript/JS; SKILL.md types + `npm test`; docs ES/EN
44
+ actualizan tipos y versión vigente.
45
+
46
+ ## Smoke (tests/smoke-engine.sh)
47
+
48
+ - `repo-d` (`type: node`): lcov 9/10 → 90.0%, junit envuelto → 7 tests,
49
+ LOC prod=4/test=2 (.ts vs .test.ts), eslint (security floor + null fatal HIGH,
50
+ prefer-const MEDIUM → reported=3 gated=2), tsc error → HIGH,
51
+ UNMEASURED pre-ingest → medido post-ingest.
52
+
53
+ ## Criterio pendiente (mismo contrato que el adapter python)
54
+
55
+ El adapter node NO tiene todavía un repo real de dogfooding detrás — entra por decisión
56
+ del autor pensando en el release público (audiencia TS/JS). El dry-run de solo lectura
57
+ contra un repo TS/JS real (coverage vs jest, test count exacto, eslint/tsc) queda como
58
+ criterio abierto — igual que el dry-run pendiente del adapter python (1.4.0).
59
+
60
+ ## Diferido consciente (sin cambios)
61
+
62
+ - rebuild: densidad de asserts por test-file. Perfiles A-E mecanizados (`--profile`).
63
+ - Roadmap de adapters: Go (golangci-lint emite formato checkstyle — reusable),
64
+ Rust (cargo-llvm-cov emite Cobertura — reusable). Entran cuando haya repo real.
@@ -0,0 +1,57 @@
1
+ # dev-loop-kit 1.6.0 — adapter `type: go` (2026-07-02)
2
+
3
+ Tercer adapter consecutivo donde el costo baja porque el engine habla FORMATOS, no
4
+ lenguajes: el linter reusa `parse_checkstyle` (golangci-lint emite checkstyle) y los
5
+ tests reusan `junit_test_count` (gotestsum emite JUnit). Lo único nuevo: el parser del
6
+ cover profile nativo de Go. Smoke suite: 37/37 (31 previos intactos + 6 go).
7
+
8
+ ## Engine (qa_ledger.py)
9
+
10
+ - **`type: go`**.
11
+ - **`go_coverage()`** — cover profile nativo (`go test -coverprofile=coverage.out`):
12
+ formato `import/path/file.go:S.C,E.C numStatements hitCount`; cobertura por
13
+ **SENTENCIAS** (la convención Go, no líneas). First match: `coverage.out`,
14
+ `cover.out`, `reports/coverage.out`. Profile vacío (solo línea `mode:`) → 0.0
15
+ fail-closed, nunca un verde inventado.
16
+ - Test count: **reusa `junit_test_count`** vía `gotestsum --junitfile` (root envuelto
17
+ ya soportado).
18
+ - Linter: **reusa `parse_checkstyle` parametrizado** (`tool="golangci"`) — golangci-lint
19
+ `--out-format checkstyle`; error→HIGH, warning→MEDIUM; gosec entra por el mismo
20
+ reporte si está habilitado en la config del linter. Los IDs de finding llevan
21
+ `golangci:` (los de Java siguen `checkstyle:` — sin migración de IDs).
22
+ - `ingest-gate`: branch go (`reports/golangci.xml` / `golangci.xml`, o `--golangci`);
23
+ combinado = `go-qa-gate`. Mismo contrato de ausencia.
24
+ - Guard UNMEASURED: `maven`/`python`/`node`/`go`.
25
+ - LOC go: test = sufijo `_test.go` (la convención — los tests viven junto al código);
26
+ prod = el resto. `SKIP_DIRS` += `vendor` (dependencias vendorizadas explotarían el
27
+ LOC) y `testdata` (fixtures no compilables).
28
+
29
+ ## Config / docs
30
+
31
+ - `test_command_go` (gotestsum + coverprofile) + repo de ejemplo `api-svc`;
32
+ version 1.6.0 (triple sync).
33
+ - Kit README: tabla Go; SKILL.md types + `go test`; docs ES/EN tipos y versión.
34
+
35
+ ## Smoke (tests/smoke-engine.sh)
36
+
37
+ - `repo-e` (`type: go`): cover profile 3/5 sentencias → 60.0%, gotestsum junit → 4
38
+ tests, LOC prod=4/test=2 (`_test.go` junto al código), golangci checkstyle
39
+ (error gateado HIGH + warning MEDIUM), UNMEASURED pre-ingest → medido post-ingest.
40
+
41
+ ## Nota estructural (regla de tres cumplida)
42
+
43
+ Con 5 types, los dispatchers siguen siendo ifs de 3 ramas y cada adapter nuevo reusó
44
+ parsers existentes — la presión para refactorizar `type` → perfiles de reporte
45
+ (cobertura+junit+linters como config, no código) existe pero todavía no duele. Si
46
+ entra un 6º type o un type necesita MEZCLAR formatos, ese es el momento.
47
+
48
+ ## Criterio pendiente (mismo contrato que python/node)
49
+
50
+ Dry-run de solo lectura contra un repo Go real — el adapter entra sin repo de
51
+ dogfooding detrás, mismo criterio abierto que 1.4.0/1.5.0.
52
+
53
+ ## Diferido consciente (sin cambios)
54
+
55
+ - rebuild: densidad de asserts por test-file. Perfiles A-E mecanizados (`--profile`).
56
+ - Rust: cargo-llvm-cov emite Cobertura (reusa `python_coverage`) + cargo-nextest emite
57
+ JUnit — entra cuando haya repo real.
@@ -0,0 +1,74 @@
1
+ # dev-loop-kit 1.7.0 — adapters `type: rust` y `type: dotnet` (2026-07-02)
2
+
3
+ > Publicado junto con 1.8.0 y 1.9.0 en un único commit (no existe commit
4
+ > standalone de 1.7.0); la suite final de ese commit es 63/63. Los fixes que
5
+ > el review fresco de este adapter encontró (blocker de clippy incluido)
6
+ > están aplicados y documentados en CHANGELOG-1.9.0.
7
+
8
+ Dos adapters en un release porque ambos REUSAN la infraestructura existente:
9
+ coverage vía Cobertura (`cobertura_coverage`, renombrado desde `python_coverage`) y
10
+ tests vía `junit_test_count`. Nuevo de verdad: `parse_clippy` (JSONL de cargo) y
11
+ `parse_sarif` (SARIF v2 — el formato universal de análisis estático). Incluye los
12
+ fixes del review fresco de 1.6.0.
13
+
14
+ ## Rust (`type: rust`)
15
+
16
+ - Coverage: `cargo llvm-cov --cobertura --output-path reports/coverage.xml` —
17
+ **reusa `cobertura_coverage`** sin cambios.
18
+ - Tests: cargo-nextest con reporter junit — **reusa `junit_test_count`**.
19
+ - **`parse_clippy()`** — `cargo clippy --message-format=json` (JSON Lines):
20
+ `error`→HIGH, `warning`→MEDIUM; **`code: null`** (error de compilación de rustc,
21
+ no un lint) → HIGH siempre — la lección del ruff/tsc, tercera aplicación.
22
+ Los IDs preservan el path del span primario (`_mk_id_rel`).
23
+ - LOC: `tests/` = integración; los tests inline `#[cfg(test)]` cuentan como prod
24
+ (limitación documentada de la clasificación por path). `target/` ya estaba en
25
+ SKIP_DIRS (cargo y maven comparten el nombre).
26
+
27
+ ## C#/.NET (`type: dotnet`)
28
+
29
+ - Coverage: coverlet.msbuild → Cobertura — **reusa `cobertura_coverage`**.
30
+ - Tests: `dotnet test --logger "junit;LogFilePath=reports/junit.xml"`
31
+ (JUnitXml.TestLogger) — **reusa `junit_test_count`**.
32
+ - **`parse_sarif()`** — SARIF v2 (Roslyn vía `dotnet build /p:ErrorLog=...`):
33
+ `error`→HIGH, `warning`→MEDIUM, `note`→INFO. El parser sirve para CUALQUIER
34
+ herramienta que emita SARIF (semgrep, CodeQL, etc.) — inversión, no solo .NET.
35
+ - LOC: proyectos `*.Tests/` y archivos `*Test(s).cs` = test. SKIP_DIRS += `obj`,
36
+ `TestResults`.
37
+
38
+ ## Fixes del review fresco de 1.6.0 (incluidos acá)
39
+
40
+ - **BLOCKER**: la tabla Go del kit README había pisado el heading `## Instalación`
41
+ — restaurado.
42
+ - **HIGH**: el comando documentado de golangci-lint usaba el flag v1
43
+ (`--out-format`), **eliminado en golangci-lint v2** (2025) — documentado el flag
44
+ v2 (`--output.checkstyle.path=...`) con fallback v1.
45
+ - **MEDIUM**: gotestsum pone `errors` SOLO en el root `<testsuites>` (un paquete
46
+ que no compila tenía errors>0 invisibles) — `junit_test_count` ahora toma
47
+ `max(root, suma de hijos)` por contador.
48
+ - **LOW**: `go_coverage` dedupea bloques repetidos (perfiles `-coverpkg` merged);
49
+ IDs de golangci preservan el path repo-relativo (basename colisionaba en repos
50
+ Go idiomáticos); nota de severidades de golangci en README.
51
+
52
+ ## Trade-off documentado (global, igual que `coverage/` en 1.5.0)
53
+
54
+ `vendor/`, `testdata/`, `obj/`, `TestResults/` en SKIP_DIRS son globales: un paquete
55
+ fuente legítimamente llamado así en OTRO type queda fuera del LOC (métrica advisory;
56
+ la detección de reportes es por glob/paths y no se afecta).
57
+
58
+ ## Nota estructural (regla de tres, re-evaluada en el 7º type)
59
+
60
+ Siete types y los dispatchers siguen siendo ifs por FORMATO (jacoco / cobertura /
61
+ lcov / cover-profile · surefire / junit / aproximado). El refactor a perfiles
62
+ declarativos sigue sin pagar: no eliminaría los parsers (el costo real) y agregaría
63
+ indirección. Se re-evalúa si un type necesita MEZCLAR formatos por config.
64
+
65
+ ## Criterio pendiente (mismo contrato que python/node/go)
66
+
67
+ Dry-run de solo lectura contra repos Rust/.NET reales — ambos adapters entran sin
68
+ repo de dogfooding detrás.
69
+
70
+ ## Diferido consciente (sin cambios)
71
+
72
+ - rebuild: densidad de asserts. Perfiles A-E mecanizados.
73
+ - C++: viable (gcovr emite Cobertura/lcov — parsers existentes; gtest/catch2 emiten
74
+ JUnit; clang-tidy necesitaría un parser tipo mypy) — entra cuando haya repo real.
@@ -0,0 +1,46 @@
1
+ # dev-loop-kit 1.8.0 — adapter `type: cpp` (2026-07-02)
2
+
3
+ > Publicado junto con 1.7.0 y 1.9.0 en un único commit (no existe commit
4
+ > standalone de 1.8.0); la suite final de ese commit es 63/63. Los fixes que el
5
+ > review fresco de este adapter encontró están aplicados y documentados en
6
+ > CHANGELOG-1.9.0.
7
+
8
+ Séptimo lenguaje, UN parser nuevo: clang-tidy. Coverage vía gcovr→Cobertura (reusa
9
+ `cobertura_coverage`) y tests vía ctest `--output-junit`/GoogleTest (reusa
10
+ `junit_test_count` — el fixture de smoke usa el root PLANO `<testsuite>` para
11
+ ejercitar la rama que los demás types no tocan).
12
+
13
+ ## Engine (qa_ledger.py)
14
+
15
+ - **`type: cpp`** — extensiones `.cpp .cc .cxx .c .h .hpp .hh .hxx`.
16
+ - Coverage: `gcovr --cobertura reports/coverage.xml` — **reusa `cobertura_coverage`**.
17
+ - Tests: `ctest --test-dir build --output-junit ../reports/junit.xml` (CMake ≥3.21;
18
+ root plano) o `--gtest_output=xml:` (root envuelto) — **reusa `junit_test_count`**,
19
+ ambas formas de root ya soportadas.
20
+ - **`parse_clang_tidy()`** — texto (`file:line:col: warning: msg [check-a,check-b]`):
21
+ error→HIGH, warning→MEDIUM; checks de seguridad (`cert-*`, o que contengan
22
+ `security`) con floor HIGH — misma disciplina que findsecbugs/eslint-security.
23
+ Líneas sin `[check]` (diagnósticos crudos del compilador) → rule `diagnostic`.
24
+ IDs con path preservado (`_mk_id_rel`).
25
+ - `ingest-gate`: branch cpp (`reports/clang-tidy.txt`, o `--clang-tidy`);
26
+ combinado = `cpp-qa-gate`. Mismo contrato de ausencia.
27
+ - Guard UNMEASURED: los 7 types linteables.
28
+ - LOC: `test`/`tests` en el path, prefijo `test_` o sufijos `_test.cpp/.cc/.cxx`,
29
+ `Test.cpp`. `SKIP_DIRS` += `cmake-build-debug`, `cmake-build-release`, `_deps`
30
+ (FetchContent) — mismo trade-off global documentado.
31
+
32
+ ## Nota honesta sobre C++
33
+
34
+ El ecosistema no tiene UNA convención de build (CMake/Bazel/make) — el kit no la
35
+ elige: pide que los REPORTES existan (Cobertura + JUnit + texto de clang-tidy) y el
36
+ cómo se generan vive en el adapter por-repo (`CLAUDE.md` del proyecto). Es el
37
+ contrato de siempre, explícito.
38
+
39
+ ## Criterio pendiente (mismo contrato que python/node/go/rust/dotnet)
40
+
41
+ Dry-run de solo lectura contra un repo C++ real — entra sin repo de dogfooding.
42
+
43
+ ## Diferido consciente
44
+
45
+ - rebuild: densidad de asserts. Perfiles A-E mecanizados. cppcheck (`--xml`) como
46
+ segundo linter cpp si un repo real lo pide.
@@ -0,0 +1,112 @@
1
+ # dev-loop-kit 1.9.0 — adapters `type: gradle` (Kotlin/JVM) y `type: swift` + fixes de los reviews 1.7.0/1.8.0 (2026-07-02)
2
+
3
+ Los dos adapters más baratos hasta ahora: **CERO parsers nuevos**. Todo es reuso
4
+ de formatos ya soportados en paths nuevos. Nota clave: **Kotlin sobre Maven ya
5
+ funcionaba con `type: maven`** (`.kt` está en SOURCE_EXT maven desde 1.0) —
6
+ `gradle` cubre el caso común Gradle. Smoke suite: 63/63.
7
+
8
+ ## Engine (qa_ledger.py)
9
+
10
+ ### `type: gradle` (Kotlin/JVM + Java-sobre-Gradle)
11
+
12
+ - Extensiones `.kt .kts .java`. LOC: `src/main` = prod; `src/test` y source
13
+ sets custom (`src/*Test`: integrationTest, functionalTest) = test.
14
+ - Coverage: **reusa el parser JaCoCo de maven** (`_jacoco_line_counter`) en los
15
+ paths Gradle (`build/reports/jacoco/test/jacocoTestReport.xml`).
16
+ - Tests: **reusa la suma per-clase de surefire** — `maven_test_count` y
17
+ `gradle_test_count` ahora comparten `_perclass_xml_count()`; los
18
+ `build/test-results/**/TEST-*.xml` de Gradle son el mismo formato.
19
+ - Linter: **detekt** emite formato checkstyle — `parse_checkstyle(tool="detekt")`
20
+ parametrizado, igual que golangci. `--detekt` explícito o por type;
21
+ combinado = `gradle-qa-gate`.
22
+
23
+ ### `type: swift`
24
+
25
+ - Extensión `.swift`. LOC: convención SwiftPM (`Sources/` prod, `Tests/` +
26
+ `*Tests.swift`/`*Test.swift` test).
27
+ - Coverage: **reusa el parser lcov** (Flutter/node) — `llvm-cov export
28
+ -format=lcov` sobre el `.profdata` de `swift test --enable-code-coverage`.
29
+ - Tests: **reusa `junit_test_count`** — `swift test --xunit-output
30
+ reports/junit.xml` emite JUnit directo; el archivo hermano
31
+ `junit-swift-testing.xml` (Swift Testing) se SUMA (ver fixes 1.9.0).
32
+ - Linter: **SwiftLint** también emite checkstyle —
33
+ `parse_checkstyle(tool="swiftlint")`. `--swiftlint` o por type;
34
+ combinado = `swift-qa-gate`.
35
+
36
+ ## Fixes del review fresco de 1.7.0 (rust/dotnet)
37
+
38
+ - **BLOCKER — clippy contaba los summaries de rustc como HIGH fantasma**:
39
+ `1 warning emitted` / `aborting due to N previous errors` son diagnósticos
40
+ REALES (level warning/error, `code:null`, `spans:[]`) que el branch code-null
41
+ convertía en `compile-error` HIGH — el gate rust no podía converger nunca con
42
+ un solo warning MEDIUM vivo. Fix: diagnósticos SIN primary span se saltean
43
+ ANTES del check de code (los compile errors reales siempre traen span).
44
+ Además dedupe por finding ID (cargo re-emite por target lib/bin/test).
45
+ - **SARIF**: Roslyn `ErrorLog` emite **v1** salvo `,version=2` — se documenta el
46
+ comando correcto Y el parser gana fallback v1 (`resultFile`/`analysisTarget`);
47
+ resultados suprimidos (`suppressions`/`suppressionStates`) se ignoran (un
48
+ `#pragma warning disable` no puede gatear un build limpio); URIs absolutas
49
+ `file:///` se relativizan contra el repo (IDs estables entre máquinas).
50
+ - **Paths dotnet**: `LogFilePath`/`CoverletOutput` relativos resuelven contra el
51
+ PROYECTO DE TEST, no la raíz — comando de ejemplo ahora ancla con `$PWD` y el
52
+ README lo advierte (+ `MergeWith` para multi-proyecto).
53
+ - **Junit de nextest**: no existe sin `[profile.default.junit]` en
54
+ `.config/nextest.toml` + copy desde `target/nextest/default/junit.xml` —
55
+ documentado y agregado al `test_command_rust`.
56
+ - **Regla 5 saldada**: los fixes de 1.6.0 que entraron sin check propio ahora
57
+ tienen smoke — junit root-max (gotestsum errors solo en root), dedupe de
58
+ bloques del cover profile (-coverpkg), y el summary/dup de clippy (T20/T25).
59
+
60
+ ## Fixes del review fresco de 1.8.0 (cpp)
61
+
62
+ - **`backtest.cpp` ya no cuenta como test LOC**: el sufijo bare `test.cpp`
63
+ (lowercased) tragaba `backtest/protest/contest.cpp`; ahora el patrón CamelCase
64
+ gtest (`FooTest.cpp`) se matchea case-sensitive, como hace dotnet con
65
+ `Test.cs`. Smoke T25 lo pinnea.
66
+ - **`cmake-build-<perfil>` de CLion**: el skip ahora es por prefijo
67
+ (`cmake-build-*`), no solo debug/release — el README ya lo prometía y el
68
+ código no lo cumplía.
69
+ - **clang-tidy con paths absolutos**: se relativizan contra el repo
70
+ (`_node_rel`), IDs estables entre checkouts; extensiones `.tpp/.ipp/.inl/
71
+ .mm/.cu` entran al regex (un cert-* en un template header ya no desaparece
72
+ en silencio).
73
+
74
+ ## Fixes del review fresco de 1.9.0 (gradle/swift)
75
+
76
+ - **detekt y SwiftLint imprimen paths ABSOLUTOS por default** — el branch
77
+ isabs de `parse_checkstyle` colapsaba esos IDs a basename (colisiones entre
78
+ módulos Gradle y archivos homónimos Swift, corrompiendo fixed/new/oscillation
79
+ que se computan sobre sets de IDs). Fix: `parse_checkstyle` gana `base` y
80
+ relativiza SIEMPRE para tools no-java (`_node_rel`), igual que
81
+ eslint/tsc/clang-tidy; `golangci` también lo recibe. `_node_rel` dejó de
82
+ depender de `os.path.isabs` (su veredicto para paths `/posix` en Windows
83
+ cambió entre versiones de Python): intenta `relpath` y descarta si escapa
84
+ del repo. Smokes T27/T28 ahora usan paths absolutos (el default real) y
85
+ asserten IDs repo-relativos.
86
+ - **Swift Testing era invisible**: `--xunit-output` escribe XCTest en
87
+ `junit.xml` y Swift Testing en un SEGUNDO `junit-swift-testing.xml` que el
88
+ counter no leía — un paquete Swift 6 (donde Swift Testing es el default)
89
+ medía `tests=0` con `report_found=True` y un failure real no disparaba el
90
+ veto "measured beats narrated": fail-open en la garantía central. Fix:
91
+ `junit_test_count` gana `extra_files` (reportes full-run de sets DISJUNTOS
92
+ que SÍ se suman) y el dispatch swift pasa ambos archivos. Smoke con failure
93
+ real en el archivo swift-testing.
94
+ - **detekt fuera del test command**: su default `maxIssues: 0` corta el build
95
+ con UN finding — la corrida de tests leería roja aunque los tests pasen,
96
+ mezclando el gate de lint (que tiene su canal propio) con el de tests.
97
+ `test_command_gradle` queda `./gradlew test jacocoTestReport`.
98
+ - **Source sets custom de Gradle**: `src/integrationTest/`, `src/functionalTest/`
99
+ (cualquier `src/*Test`) ahora cuentan como test LOC, no prod.
100
+ - README swift: variante Linux de `llvm-cov` (sin `xcrun`) + path del binario
101
+ de tests documentados.
102
+
103
+ ## Contrato de siempre
104
+
105
+ Reporte ausente = el gate no corrió (jamás acredita fixes). Guard UNMEASURED:
106
+ los 9 types linteables. Criterio pendiente por adapter: dry-run de solo lectura
107
+ contra un repo real de cada lenguaje.
108
+
109
+ ## Diferido consciente
110
+
111
+ - rebuild: densidad de asserts. Perfiles A-E mecanizados. Android (Gradle +
112
+ variantes) queda explícitamente FUERA de `type: gradle` hasta un repo real.