@andresmassello/uscha 1.51.1 → 1.51.3

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 (82) hide show
  1. package/README.md +6 -1
  2. package/package.json +3 -2
  3. package/uscha-kit/.claude/skills/uscha-mirador/SKILL.md +11 -4
  4. package/uscha-kit/.claude/skills/uscha-mirador/mirador-render.py +16 -8
  5. package/uscha-kit/.claude-plugin/plugin.json +2 -2
  6. package/uscha-kit/.codex-plugin/plugin.json +2 -2
  7. package/uscha-kit/INSTALL.md +3 -0
  8. package/uscha-kit/README.md +1 -1
  9. package/uscha-kit/VERSION +1 -1
  10. package/uscha-kit/install-uscha.py +9 -3
  11. package/uscha-kit/skills/uscha-mirador/SKILL.md +11 -4
  12. package/uscha-kit/skills/uscha-mirador/mirador-render.py +16 -8
  13. package/uscha-kit/uscha.config.json +1 -1
  14. package/uscha-kit/CHANGELOG-1.10.0.md +0 -84
  15. package/uscha-kit/CHANGELOG-1.11.0.md +0 -67
  16. package/uscha-kit/CHANGELOG-1.12.0.md +0 -46
  17. package/uscha-kit/CHANGELOG-1.13.0.md +0 -33
  18. package/uscha-kit/CHANGELOG-1.14.0.md +0 -42
  19. package/uscha-kit/CHANGELOG-1.15.0.md +0 -58
  20. package/uscha-kit/CHANGELOG-1.16.0.md +0 -55
  21. package/uscha-kit/CHANGELOG-1.17.0.md +0 -44
  22. package/uscha-kit/CHANGELOG-1.18.0.md +0 -42
  23. package/uscha-kit/CHANGELOG-1.19.0.md +0 -41
  24. package/uscha-kit/CHANGELOG-1.2.2.md +0 -16
  25. package/uscha-kit/CHANGELOG-1.2.3.md +0 -20
  26. package/uscha-kit/CHANGELOG-1.2.4.md +0 -10
  27. package/uscha-kit/CHANGELOG-1.2.5.md +0 -23
  28. package/uscha-kit/CHANGELOG-1.2.6.md +0 -11
  29. package/uscha-kit/CHANGELOG-1.2.7.md +0 -15
  30. package/uscha-kit/CHANGELOG-1.2.8.md +0 -24
  31. package/uscha-kit/CHANGELOG-1.2.9.md +0 -4
  32. package/uscha-kit/CHANGELOG-1.20.0.md +0 -29
  33. package/uscha-kit/CHANGELOG-1.21.0.md +0 -33
  34. package/uscha-kit/CHANGELOG-1.22.0.md +0 -60
  35. package/uscha-kit/CHANGELOG-1.23.0.md +0 -75
  36. package/uscha-kit/CHANGELOG-1.24.0.md +0 -50
  37. package/uscha-kit/CHANGELOG-1.25.0.md +0 -55
  38. package/uscha-kit/CHANGELOG-1.26.0.md +0 -70
  39. package/uscha-kit/CHANGELOG-1.27.0.md +0 -45
  40. package/uscha-kit/CHANGELOG-1.28.0.md +0 -35
  41. package/uscha-kit/CHANGELOG-1.29.0.md +0 -20
  42. package/uscha-kit/CHANGELOG-1.3.0.md +0 -74
  43. package/uscha-kit/CHANGELOG-1.30.0.md +0 -46
  44. package/uscha-kit/CHANGELOG-1.31.0.md +0 -59
  45. package/uscha-kit/CHANGELOG-1.32.0.md +0 -50
  46. package/uscha-kit/CHANGELOG-1.33.0.md +0 -46
  47. package/uscha-kit/CHANGELOG-1.34.0.md +0 -55
  48. package/uscha-kit/CHANGELOG-1.35.0.md +0 -30
  49. package/uscha-kit/CHANGELOG-1.36.0.md +0 -33
  50. package/uscha-kit/CHANGELOG-1.37.0.md +0 -41
  51. package/uscha-kit/CHANGELOG-1.38.0.md +0 -11
  52. package/uscha-kit/CHANGELOG-1.39.0.md +0 -14
  53. package/uscha-kit/CHANGELOG-1.4.0.md +0 -68
  54. package/uscha-kit/CHANGELOG-1.40.0.md +0 -16
  55. package/uscha-kit/CHANGELOG-1.40.1.md +0 -11
  56. package/uscha-kit/CHANGELOG-1.40.2.md +0 -13
  57. package/uscha-kit/CHANGELOG-1.41.0.md +0 -18
  58. package/uscha-kit/CHANGELOG-1.41.1.md +0 -53
  59. package/uscha-kit/CHANGELOG-1.41.2.md +0 -34
  60. package/uscha-kit/CHANGELOG-1.41.3.md +0 -30
  61. package/uscha-kit/CHANGELOG-1.42.0.md +0 -41
  62. package/uscha-kit/CHANGELOG-1.43.0.md +0 -37
  63. package/uscha-kit/CHANGELOG-1.44.0.md +0 -90
  64. package/uscha-kit/CHANGELOG-1.44.1.md +0 -26
  65. package/uscha-kit/CHANGELOG-1.45.0.md +0 -58
  66. package/uscha-kit/CHANGELOG-1.46.0.md +0 -50
  67. package/uscha-kit/CHANGELOG-1.46.1.md +0 -35
  68. package/uscha-kit/CHANGELOG-1.47.0.md +0 -45
  69. package/uscha-kit/CHANGELOG-1.48.0.md +0 -35
  70. package/uscha-kit/CHANGELOG-1.48.1.md +0 -55
  71. package/uscha-kit/CHANGELOG-1.48.2.md +0 -47
  72. package/uscha-kit/CHANGELOG-1.49.0.md +0 -45
  73. package/uscha-kit/CHANGELOG-1.5.0.md +0 -64
  74. package/uscha-kit/CHANGELOG-1.50.0.md +0 -52
  75. package/uscha-kit/CHANGELOG-1.50.1.md +0 -52
  76. package/uscha-kit/CHANGELOG-1.50.2.md +0 -62
  77. package/uscha-kit/CHANGELOG-1.51.0.md +0 -44
  78. package/uscha-kit/CHANGELOG-1.51.1.md +0 -33
  79. package/uscha-kit/CHANGELOG-1.6.0.md +0 -57
  80. package/uscha-kit/CHANGELOG-1.7.0.md +0 -74
  81. package/uscha-kit/CHANGELOG-1.8.0.md +0 -46
  82. package/uscha-kit/CHANGELOG-1.9.0.md +0 -112
@@ -1,35 +0,0 @@
1
- # uscha-kit 1.46.1 — the statusline reads MEASURED acceptance, never narrated (2026-07-23)
2
-
3
- A doctrinal fix to the statusline shipped in 1.46.0, found by dogfooding it on this very repo.
4
- Smoke suite: 375/375.
5
-
6
- ## The smell
7
- The progress hook counted checkboxes (`- [x]`) in `ACCEPTANCE.md` — the **narrated** progress.
8
- The engine's readiness counts green `AC-nn` testcases — the **measured** acceptance. The two can
9
- diverge, and on this repo they diverged badly: the statusline said **0%** while the ledger said
10
- **83.3% measured** (criteria tested green but not ticked). For a kit whose flag is *measured
11
- beats narrated*, its own at-a-glance summary was reading the wrong side — and could contradict
12
- the mirador.
13
-
14
- ## The fix (write-once by the engine, read-many by the hook)
15
- The statusline must summarize the ledger, so it now reads the ledger's own truth:
16
-
17
- - **Engine**: `readiness --record` persists a compact measured summary into
18
- `ledger["measured"]` — `{at, score, band, acceptance_done, acceptance_total, next}` — where
19
- `next` is the first criterion NOT yet closed by a green test. Same opt-in write as
20
- `readiness_history` (readiness stays read-only by default). `qa_ledger.py:cmd_readiness`.
21
- - **Hook** (`uscha_progress.py`): prefers `ledger["measured"]` — the exact truth the mirador
22
- shows, so the two can never disagree. Counting checkboxes remains only as the **fallback** for
23
- a fresh project that has not recorded a measurement yet. The hook stays fast: it never re-runs
24
- the engine.
25
-
26
- Considered and rejected: having the Stop hook run the engine itself (breaks the "fast hook,
27
- never runs tests" design and couples it to locating `qa_ledger.py`), and merely relabeling the
28
- checkbox count as "marked" (honest, but keeps the narrated number as the headline).
29
-
30
- Regression: smoke **T90** — checkbox fallback shows 0/2; after `readiness --record` the
31
- statusline shows the MEASURED 1/2, agreeing with the ledger to the decimal.
32
-
33
- ## Note on the test suite
34
- The installer/npm smoke checks still hardcode the version string, so this bump updated those
35
- literals.
@@ -1,45 +0,0 @@
1
- # uscha-kit 1.47.0 — the trail feeds itself: loop odometer + measured phase (2026-07-23)
2
-
3
- Field feedback on the coordinator's real pain: with all the method's loops and passes, the
4
- human steering the work loses track of *where the method is* and *how it is going*. The data
5
- to answer that already existed in the ledger — derived phase (FSM 1.18.0), loop passes
6
- (`iterations`), plateau (1.14.0), history (`readiness --record`) — but nothing surfaced it,
7
- and the history starved because `--record` was opt-in and nobody ran it. Smoke suite: 376/376.
8
-
9
- ## Doctrine first: what "how much is left" may honestly mean
10
- "Where am I" and "how many passes happened" are FACTS the engine already derives. "How much
11
- is left" is a forecast — the QA loop converges, it does not count down — so the kit never
12
- prints an ETA. The honest answers it CAN give: the derived phase, the pass count, and the
13
- convergence signal (findings going down vs. plateau). That is what this release surfaces.
14
-
15
- ## The feed (the fix that unblocks everything)
16
- `uscha-devloop` now closes **every** pass with `readiness … --record`. One flag, two feeds:
17
- `readiness_history` (the mirador time-lapse stops saying "no history yet") and
18
- `ledger["measured"]`, which now also carries the **loop odometer** per repo:
19
-
20
- ```jsonc
21
- "measured": { …, "repos": { "backend-api": { "phase": "qa", "loops": 3, "stalled": false } } }
22
- ```
23
-
24
- All facts the readiness run ALREADY computed — `--record` only persists them (append-only,
25
- never a gate). `qa_ledger.py:cmd_readiness`.
26
-
27
- ## The odometer in the mirador trail
28
- The trail's "QA loop" node now shows a measured badge via the `count` field the template
29
- always supported (it was emitted `null` since 1.32.0): `"3 loops"`, plus `"1 escalado"` when
30
- escalations are open, and the convergence verdict — `"plateau"` when the stall detector fires,
31
- `"convergido"` when every repo converged. `cmd_dashboard` fills it from the same `loops` +
32
- `advice` facts it already emitted; no new derivation.
33
-
34
- ## The phase token in the statusline (A-lite)
35
- One compact token after the label: `qa×3` (derived phase × pass count), with a yellow
36
- `plateau` warning when iterating stopped helping. Read from `ledger["measured"]` — the Stop
37
- hook still never runs the engine. The phase shows even for projects with no AC-IDs (the
38
- phase is its own fact, independent of acceptance).
39
-
40
- Deferred (needs a phase-transition history the ledger does not keep, by design so far):
41
- counting Build↔Verify re-entries, and the odometer over time in the time-lapse.
42
-
43
- Regression: smoke **T91** — after two `log-step` passes and one `readiness --record`, the
44
- measured summary carries `{phase: qa, loops: 2, stalled: false}`, the mirador `qa` node badge
45
- reads `"2 loops"`, and the statusline renders `qa×2`.
@@ -1,35 +0,0 @@
1
- # uscha-kit 1.48.0 — uscha-status: the statusline on demand, in chat (2026-07-23)
2
-
3
- Ninth skill. The statusline (1.46.0) is passive and terminal-bound — some surfaces
4
- (Claude Desktop, Codex, plain terminals, CI logs) may never show it. `uscha-status` is the
5
- same truth, printed in chat when the human asks. Smoke suite: 377/377.
6
-
7
- ## Three zoom levels, each datum shown once
8
- - **Push** — one readiness verdict line at every pass close (the devloop already does this;
9
- 1.25.0 single-verdict + 1.47.0 `--record`).
10
- - **Pull** — `/uscha-status`: one compact block on demand (this release).
11
- - **Bird's-eye** — the mirador, when the human wants everything.
12
-
13
- Explicitly rejected: having every command print status blocks as it goes. The push line
14
- already exists; repeating the same truth several times per pass is ceremony.
15
-
16
- ## What the skill does
17
- Renders ONE fenced block (max ~8 lines): label · derived phase `×loops` · measured
18
- acceptance bar · tests · coverage · `next →` criterion · per-repo loop odometer (multi-repo
19
- only, with `plateau ⚠`) · roadmap · `recorded` timestamp + score/band. Sources, in order:
20
- run `uscha_progress.py` if installed (fast, never runs tests) and read
21
- `.claude/uscha-progress.json`; else read `QA-LEDGER.json` `measured` directly; no config →
22
- not a uscha project, say so.
23
-
24
- Read-only by contract: it never runs `readiness`, tests or gates. No `measured` recorded →
25
- it says so and points at the feed (`readiness --record`, automatic at pass close since
26
- 1.47.0); a checkbox-only count is shown labeled **narrated**, never passed off as measured.
27
-
28
- ## Registration
29
- Installer `SKILLS` roster, doctor `USCHA_SKILLS` (T57 no-drift guard forced it), both
30
- plugin manifests, marketplace, READMEs (eight → nine), and the project template
31
- `CLAUDE.md` command list.
32
-
33
- Regression: smoke **T92** — `install-uscha.py` SKILLS must match the skill dirs on disk
34
- (same no-drift spirit as T57, for the installer), and `uscha-status/SKILL.md` carries a
35
- valid frontmatter and its read-only contract.
@@ -1,55 +0,0 @@
1
- # uscha-kit 1.48.1 — one derivation: narrated is labeled, escalation agrees across views (2026-07-23)
2
-
3
- Found by running the kit's own QA loop (code-review · judgment-day · improve) over releases
4
- 1.46.1–1.48.0 while dogfooding it on this repo. code-review came back clean; the other two
5
- passes returned four gated findings, all real. This is the fix cycle. Smoke suite: 378/378.
6
-
7
- ## The mirador and the statusline disagreed — twice
8
- `cmd_dashboard` built a repo's state from two ad-hoc sources while `_derive_phase` — the FSM
9
- the statusline's odometer reads — used one. Both halves were wrong:
10
-
11
- - **Escalation.** The dashboard scanned `ledger["escalations"]` alone; `_derive_phase` also
12
- treats an open **spec-doubt**, an open **spec-change-request** and a gated **production
13
- finding** as escalations. A repo blocked by a spec-doubt read `escalated` in the statusline
14
- and `active`/`converged` in the mirador.
15
- - **Convergence.** The dashboard took `converged` from `_converged()` alone, which ACCEPTS
16
- agent-reported `tests_passed` when no snapshot was ever taken; `_derive_phase` requires
17
- MEASURED green tests before it will say `pr-ready`. So a repo whose tests were only narrated
18
- read **"convergido"** on the mirador — the view a human reads before deciding a merge —
19
- while the statusline correctly withheld readiness.
20
-
21
- The second one was found by the loop's own second cycle, after the first fix closed only the
22
- escalation half. The dashboard now derives its state ENTIRELY from `_derive_phase`. One
23
- derivation, one truth — including about what "converged" is allowed to mean.
24
-
25
- ## A checkbox no longer wears a measured face
26
- The statusline rendered `AC 5/6` identically whether it came from `ledger["measured"]` (closed
27
- by a green test) or from counting `- [x]` in ACCEPTANCE.md. The 1.46.1 headline said "never
28
- narrated", but the fallback was real and silent. It now travels with its provenance:
29
- `uscha_progress.py` records `acceptance_source` (`measured` | `narrated`) and the renderer
30
- marks the fallback on screen. Measured is the norm and stays unlabeled; narrated always says
31
- so.
32
-
33
- ## The fast path now carries what its consumers need
34
- `.claude/uscha-progress.json` gained `measured_at`, `score`, `band` and the full per-repo
35
- `repos` odometer. The `uscha-status` skill documented a `recorded <at> · score <band>` line and
36
- a multi-repo `loops` line that its own primary source could not produce — inviting an agent to
37
- either break the declared source order or invent a timestamp. Now the documented output is
38
- fully producible from the fast path, and the SKILL.md says which field feeds each line.
39
-
40
- ## One helper instead of five copies
41
- "Max iteration for a repo" was computed independently in five places (mirador badge, readiness
42
- churn, persisted odometer, first-time-yield, regression-close). A change to any one of them
43
- would have silently desynced the others — the root-cause pattern behind the bugs above.
44
- Extracted `_repo_loop_count`; every site calls it.
45
-
46
- Regression: smoke **T93** — the checkbox fallback is labeled `narrado` on screen; narrated
47
- tests never read as `convergido` on the mirador; and an open spec-doubt reports `escalated` in
48
- BOTH the mirador badge and the persisted odometer.
49
-
50
- ## How this release was found
51
- Not by an audit — by running the kit's own loop on itself. Cycle 1: `code-review` clean,
52
- `judgment-day` 3 gated, `improve` 1 gated. Fix. Cycle 2: `code-review` clean, `judgment-day`
53
- clean (all three closed), `improve` found the FIRST FIX WAS PARTIAL and reproduced the
54
- remaining contradiction live. Fix. That is the loop doing exactly what it exists to do: a
55
- second pass that refuses to rubber-stamp the first one.
@@ -1,47 +0,0 @@
1
- # uscha-kit 1.48.2 — the engine measures its own coverage (2026-07-23)
2
-
3
- `ACCEPTANCE.md` had left an open fork since 1.44.0: instrument the engine's coverage, or
4
- declare the exemption in config with provenance. **Resolved by measuring.** A measurement kit
5
- that cannot measure its own coverage is a weak calling card. Smoke suite: 379/379.
6
-
7
- ## The problem was the shape of the suite, not the will
8
- The engine is exercised almost entirely through **subprocess** — the smoke is bash, and it
9
- drives `qa_ledger.py` ~370 times. Instrumenting that normally means the `COVERAGE_PROCESS_START`
10
- + `.pth` dance in site-packages. It was unnecessary here: the suite already funnels every one
11
- of those calls through a single `run()` choke point. Wrapping that one function measures
12
- everything.
13
-
14
- ```bash
15
- USCHA_COVERAGE=1 bash uscha-kit/tests/smoke-engine.sh
16
- coverage combine && coverage xml -o reports/coverage.xml
17
- ```
18
-
19
- **Opt-in by design.** Off by default, so the normal suite stays fast and `coverage.py` is NOT
20
- a dependency of running the tests. `--source` is ONE absolute path (the suite runs from a temp
21
- sandbox): git-bash rewrites only the first POSIX path embedded in an argument, so a
22
- comma-joined list would reach Windows Python half-translated — and coverage.py does not report
23
- never-imported files anyway, so extra roots buy nothing. The distributable twin
24
- `uscha-kit/skills` is absent from the report because the suite never executes it (`$QL` points
25
- at `.claude/skills`), not because a flag excluded it.
26
-
27
- ## The number, and what it does NOT cover
28
- **84.2%** on the engine, against a threshold of 60 now DECLARED in `uscha.config.json`
29
- (provenance: a human requirement, not the kit's default opinion — kit 1.17.0). The report is
30
- Cobertura at `reports/coverage.xml`, exactly the path and shape `cobertura_coverage()` already
31
- ingests; nothing new had to be taught to the engine.
32
-
33
- Honest limit, recorded in config as `defaults._coverage_scope` and in `ISSUES-DEFERRED.md`
34
- (D-03): the number covers **`qa_ledger.py`**. The auxiliary scripts (`templates/scripts`, the
35
- mirador renderer) are exercised by the suite but invoked outside the `run()` seam, so they
36
- contribute nothing to it. Calling 84.2% "the kit's coverage" would be exactly the inflated
37
- claim this project exists to remove.
38
-
39
- ## Self-application: 35 → 100
40
- With coverage measured, every readiness dimension is now MEASURED for this repo and the
41
- score reads **100.0 — READY**: acceptance 6/6 (green tests, not checkboxes), ADR complete,
42
- coverage 84.2%, static gate clean (ruff + mypy), convergence closed by a real 3-cycle QA loop.
43
- The path was: stale snapshot cleared (35 → 50) → static gate wired (→ 72.2) → QA loop
44
- converged (→ 83.3) → coverage instrumented (→ 100).
45
-
46
- Regression: smoke **T94** — instrumentation stays opt-in (the suite runs with no `coverage.py`
47
- present), and a Cobertura report at `reports/coverage.xml` is ingested as MEASURED.
@@ -1,45 +0,0 @@
1
- # uscha-kit 1.49.0 — the mirador tells what the ledger knows (2026-07-23)
2
-
3
- Operator feedback, twice over: a field session reported "empty panels with no reason given",
4
- and dogfooding on this repo landed the same verdict — *"me sabe a poco o confuso"*. Looking at
5
- the rendered page made both complaints concrete: the biggest block was kit metadata identical
6
- across projects, the method's crown jewel (measured acceptance) had no panel at all, and the
7
- loop's story — the reason the kit exists — was one line. Smoke suite: 380/380.
8
-
9
- ## ACCEPTANCE replaces "Specifications"
10
- The `specs` field had been a hardcoded `[]` since 1.32.0 (comment: the engine tracks AC-nn,
11
- not SPEC-nnn) — a panel that could never fill, on every project, with a hint that read like a
12
- todo. Gone. In its place, the engine's real currency: an **Acceptance — measured** panel,
13
- criterion by criterion, with the status the engine computes (the template paints, never
14
- derives):
15
-
16
- - **measured** — closed by a green named test;
17
- - **narrated — sin test** — ticked by hand, no green test backs it;
18
- - **open** — neither.
19
-
20
- The data was already inside `cmd_dashboard` (it captures `readiness --json` verbatim); the
21
- release adds per-criterion `items` to the readiness acceptance block and passes the panel
22
- through. When JUnit evidence is stale, the panel says how many reports were discarded and asks
23
- for a re-run — it never presents stale green as current.
24
-
25
- ## The loop tells its story: burn-down per cycle
26
- `loops[]` entries now carry the repo's REAL derived phase (`_derive_phase`, closing deferred
27
- finding D-01) and a per-cycle `series`: findings reported / gated / fixed / deferred, straight
28
- from the recorded steps. The mirador draws one line per cycle — `gated` hot while > 0, cool at
29
- 0 — so reading left to right answers *"is iterating helping?"* without opening the ledger.
30
- **Agent steps only** (same filter as `_converged`): static-gate ingests carry below-gate noise
31
- (a linter's 35 LOWs re-reported on every refresh) that drowned the trend, and the gates
32
- already speak through readiness's own gate line.
33
-
34
- ## Less noise, honest voids
35
- - **Execution policy demoted** to a collapsed drawer (same mechanism as sub-scores): it is
36
- kit routing, identical across projects — policy, not project state. It was the largest
37
- block on the page.
38
- - **Constitution invariants**: a `null` status now renders dashed with "sin gate — no medido".
39
- A silent blank read as "fine", which is the exact lie the truth-pass forbids.
40
- - The sample fallback DATA in the template was realigned to the real contract (it still
41
- showed `specs` and loop states the engine never emits).
42
-
43
- Regressions: smoke **T95** — per-AC statuses measured/narrated/open, `specs` absent from the
44
- contract, loops carry phase + an agent-only series that a static-gate ingest cannot pollute.
45
- The dashboard contract check (T-mirador) updated to the new shape.
@@ -1,64 +0,0 @@
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.
@@ -1,52 +0,0 @@
1
- # uscha-kit 1.50.0 — receipts: every number cites its evidence (2026-07-23)
2
-
3
- The mirador's footer has said *"click a milestone to see its receipt"* since 1.32.0 — and the
4
- engine fed that drawer a hardcoded `{}`. The machinery waited eighteen releases for data that
5
- never came. This release wires it, and extends the same principle to every number on the page:
6
- **a figure you cannot trace to a file and a timestamp is narration wearing a suit.**
7
- Smoke suite: 381/381.
8
-
9
- ## The provenance was always in scope — and being thrown away
10
- Three separate places computed a fact while holding its receipt, then discarded it:
11
-
12
- - `_ac_tags` read the testcase NAME and the report PATH to tally green/red per criterion —
13
- and kept only the integers. Now each tag carries `cases: [{test, report, ok}]` (capped at 8:
14
- a receipt cites evidence, it is not a dump), aggregated across repos and carried through
15
- `readiness --json` into the dashboard's per-criterion items.
16
- - Every coverage backend (maven, lcov, cobertura, go, gradle, ant) matched its report file
17
- and returned only the numbers. Now the returned dict carries `reports: [paths]`.
18
- - `_mirador_adrs` derived id/title/status from the file it was reading and dropped the
19
- filename. Now each ADR row carries `file`.
20
-
21
- ## The drawer, fed
22
- `cmd_dashboard` now builds `evidence` from recorded facts only:
23
-
24
- - **spec** — the ACCEPTANCE file, criteria counts, measured tally, untagged warning.
25
- - **adr** — every ADR with its status and source file.
26
- - **build** — per repo, the last snapshot: when it was taken, prod/test LOC, tests verdict
27
- with report paths + mtimes + freshness, coverage pct with its report path.
28
- - **qa** — cycles per repo with the burn-down tail, plus which gates are persisted.
29
- - **verify** — the last `readiness --record` entry, the current score/band, any active cap,
30
- and how many history records back the time-lapse.
31
- - **prod** — a receipt for the ABSENCE of evidence: the method stops at the PR; merging and
32
- deploying are human acts the ledger does not record, by design.
33
- - **ac:AC-nn** — one receipt per criterion, **cases-driven, never status-driven**: a measured
34
- one cites the green testcases that carry its name and the reports they live in; a criterion
35
- with green AND red testcases shows BOTH and names the veto (*"veto rojo"* — fail-closed, a
36
- hidden vetoed green would be the receipt lying about the evidence it exists to cite); a
37
- narrated one with no cases says *"tildado a mano — ningún testcase lo respalda"*; an open
38
- one says nothing backs it yet. User-authored strings (testcase names, paths, titles) are
39
- escaped before entering the drawer, so a crafted name cannot paint fake verdict tokens.
40
-
41
- A phase with no recorded facts gets NO key — and clicking it now opens an honest "sin recibo"
42
- drawer instead of the old silent no-op (a click that does nothing reads as broken; a missing
43
- receipt is itself a fact worth stating).
44
-
45
- ## Acceptance rows are clickable
46
- Each row in the Acceptance panel opens its criterion's receipt (keyboard-accessible,
47
- `role=button`). The drawer is the same one the trail uses — one mechanism, more keys.
48
-
49
- Regression: smoke **T96** — the AC receipt cites testcase + report; the mixed green+red
50
- criterion does not close AND its receipt shows both cases naming the veto; narrated says so;
51
- build cites the coverage path; ADRs carry their file; a facts-less phase has no evidence key.
52
- The 1.10.0 edge-case check updated: tags now assert counts by key (the dict grew `cases`).
@@ -1,52 +0,0 @@
1
- # uscha-kit 1.50.1 — the privacy fix reaches the package (2026-07-23)
2
-
3
- A packaging and documentation release: 1.50.0 shipped a tarball that still carried what
4
- 1.50.0's own repo had already stopped carrying. Smoke suite: 381/381.
5
-
6
- ## What the published package was still doing
7
- The AC-03 leak detector — the criterion that forbids naming client or private projects
8
- anywhere in the kit — had those names **hardcoded as its own regex**. The guard published
9
- the very list it exists to keep out, in a public repo and inside the npm tarball. That was
10
- fixed in the repo, but a fix that never reaches the distributed artifact is not a fix.
11
-
12
- - The names now live in **`.uscha-private-names`** at the repo root: untracked, one name or
13
- regex per line. Without it AC-03 emits `<skipped/>` → the criterion reads **UNMEASURED**,
14
- never a silent pass. Absence is not success, applied to the guard itself.
15
- - **`uscha-kit/tests/`** is excluded from the npm package. The suite is the kit's own
16
- regression harness; a consumer never runs it, and it was more than half the download.
17
- Tarball: 449.6 kB → 388.9 kB (123 → 121 files).
18
-
19
- ## A README written for whoever lands on it
20
- The published README was the repo's development map: it opened with the directory tree and
21
- buried the install command eighty lines down. Rewritten user-first — what the method is,
22
- the install line above the fold, the nine skills, the loop, and what makes it different —
23
- with the repo-development detail moved to the end. The same file serves GitHub and npm; the
24
- registry only captures it at publish time, which is why this release exists.
25
-
26
- Every number in it was verified against the engine rather than recalled: nine skills, 29
27
- subcommands, eleven language stacks, the readiness weights.
28
-
29
- ## Published docs: a truth-pass to 1.50.0
30
- The decks were frozen around 1.27.0 and had drifted from stale into **false**:
31
-
32
- - All four team-pitch decks instructed `cp uscha-kit/uscha-devloop.config.json` — a filename
33
- that has never existed. The same typo was corrected in the READMEs by 1.31.0 and never
34
- reached the decks. The whole obsolete block (manual skill copying, a superseded doctor
35
- script) is now the three-command npx flow.
36
- - The one-pagers offered a `uscha-kit-1.21.0.zip`: wrong version, and a distribution
37
- mechanism replaced by npm/npx at 1.39.0–1.40.0. No published doc mentioned npx at all.
38
- - The long deck stated three different skill counts in one file (seven in prose, eight in
39
- chips, eight in a table); none was nine. Counts corrected and `/uscha-status` added to the
40
- visible roster.
41
- - The skills reference carried a `spec-loop · skills` wordmark — pre-1.29.0 branding, and
42
- also the name of an unrelated third-party project this repo explicitly cites as distinct.
43
- - The paper (both the arXiv `.tex` and its parallel HTML source): seven → nine skills, with
44
- `mirador` and `status` added to the skills table; 24 → 29 subcommands; nine → eleven
45
- language stacks; nine → thirteen linter formats; 161 → 381 checks; five → six release
46
- artifacts; the self-application arc extended from 1.27 to 1.50.
47
-
48
- ## Known gap
49
- `docs/paper/uscha-paper.pdf` is now older than the sources it was rendered from, and the
50
- repository documents no command to regenerate it. The `.tex` is the canonical arXiv source
51
- and the `.html` is a parallel hand-authored render source; nothing enforces that the three
52
- agree. Recorded here rather than papered over.
@@ -1,62 +0,0 @@
1
- # uscha-kit 1.50.2 — cross-platform: the kit runs, and proves it runs, on macOS and Linux (2026-07-24)
2
-
3
- The engine was already portable; the *wiring* around it assumed Windows. A field discovery
4
- run installed and ran the whole kit on real Linux and confirmed it: **0 engine defects**, and
5
- a short list of cabling bugs — plus one the static audit had missed because it only shows up
6
- on direct execution. This release fixes them and, more importantly, makes the claim
7
- **measured**: a CI matrix now runs the 385-check suite on ubuntu / macOS / windows × Python
8
- 3.8 and 3.13. By the kit's own rule, "cross-platform" was narration until that grid was green.
9
-
10
- ## The measurement (this is the durable fix)
11
- `.github/workflows/smoke.yml` runs the suite on all three OSes. Its very first run earned its
12
- keep: green on Windows, **red on Linux and macOS**, on a single check — and not one of the
13
- audited findings. The npm-router test asserted `! grep -Fq "$ROUTER_FALLBACK"`, a **substring**
14
- match: on POSIX the fallback `python` is a prefix of the primary `python3`, so the check
15
- believed the fallback had run when it had not (on Windows the fallback is `py -3`, not a
16
- substring, so it always passed). The router is correct; the test now counts installer
17
- invocations. A Windows-only assumption baked into the kit's own cross-platform test, invisible
18
- to every prior local run.
19
-
20
- ## Runtime fixes
21
- - **The INV-GOLDEN hook shipped with a UTF-8 BOM before its shebang** (and CRLF in Windows /
22
- zip working trees). On direct execution the
23
- kernel does not see the shebang, hands the file to `/bin/sh`, which syntax-errors and exits
24
- 2 — the exact PreToolUse *block* code. The guard looked installed while blocking **every**
25
- tool call on POSIX. Stripped BOM, normalized to LF, set the exec bit. Smoke **T98** now runs
26
- the hook DIRECTLY (not via an interpreter, which is what masked it), asserting `allow→0`,
27
- `block→2` on POSIX.
28
- - **The doctor misdiagnosed a healthy hook.** It checked only for the `.ps1` and required
29
- `powershell`, so a `.py` install (what `uscha install` wires) read as broken on every OS,
30
- worst on mac/Linux. It now accepts either hook and ties interpretability to the one actually
31
- registered — `.py` needs only the engine's Python. Smoke **T97**.
32
- - **The statusline wired a bare `python`.** Stock macOS/Linux ship only `python3`; the status
33
- line and Stop hook died silently. Now OS-resolved (`python3` on POSIX, `python` on Windows),
34
- with an ours-by-suffix auto-refresh for an upgraded install. Smoke **T89**, now OS-aware.
35
- - **The plugin `hooks.json` invoked PowerShell only** — INV-GOLDEN-01 dead on the plugin path
36
- on mac/Linux. It now invokes the portable `.py` directly (shebang+exec on POSIX, the `.py`
37
- association on Windows, enabled by the BOM/exec fix); the `.ps1` was deleted. Smoke **T100**.
38
- *Honest limit:* CI cannot exercise Claude Code's plugin-marketplace hook execution — that
39
- path is proven by a real plugin install; `npx uscha install` is the CI-verified path.
40
- - **Reinstall left dead hook entries** (found by simulation): the hook command carries an
41
- absolute `sys.executable`, so an interpreter move stranded a dead PreToolUse entry while the
42
- fresh one was merely appended. The installer now prunes any prior uscha hook by script
43
- basename before adding, preserving a user's foreign hooks. Smoke **T99**.
44
-
45
- ## Hygiene, made structural
46
- - **Root `.gitattributes`** enforces `* text=auto eol=lf` (`.ps1` stays CRLF, goldens binary),
47
- so a CRLF can never again ship in a shebang regardless of a contributor's `core.autocrlf`.
48
- The kit's `.sh` files gained the exec bit.
49
- - **`AGENTS.md`** — the context file Codex and pi read (not `CLAUDE.md`) — was cp1252 mojibake,
50
- had drifted, and was not emitted by `init`. Replaced with a thin pointer to `CLAUDE.md` (one
51
- canonical source) and added to the `init` template set.
52
- - The repo's versioning rule said "five" surfaces; T44 checks **six** — corrected. The
53
- self-application `uscha.config.json` was bumped off its stale `1.44.0`.
54
-
55
- ## Not broken (recorded so it is not re-audited)
56
- The field run confirmed the engine's own hygiene: paths via `os.path`/`pathlib`, tool
57
- detection via `shutil.which`, all I/O `utf-8`-explicit; the ledger checksum is EOL-immune (it
58
- hashes canonical JSON, not raw bytes); the install transaction never crosses filesystems.
59
- `bin/uscha.js`, the installer's junction/symlink and browser-opening, and `workbench-doctor.sh`
60
- were already correctly cross-platform.
61
-
62
- The `pi` target (Earendil) and its design decisions are a **1.51.0** feature, out of this patch.
@@ -1,44 +0,0 @@
1
- # uscha-kit 1.51.0 — a third install target: pi (Earendil) (2026-07-24)
2
-
3
- After the 1.50.2 portability patch made the kit run everywhere, this adds **pi** — Earendil's
4
- `@earendil-works/pi-coding-agent` — as a first-class install target alongside `codex` and
5
- `claude`. The 9 skills implement the Agent Skills standard, so they load into pi unmodified.
6
- Smoke suite: 386/386.
7
-
8
- ## The target
9
- `install --target pi` places the 9 `uscha-*` skill directories flat under **`~/.agents/skills/`**
10
- — the harness-neutral sibling of the `codex` target's `~/.agents/plugins/` — plus an install
11
- marker so the doctor can measure presence and version. Skills only: no plugin manifest, no
12
- `settings.json`, no hook. It uses the same transactional install as the Claude target (stage →
13
- back up → atomic replace → marker last), so a late failure rolls the target back with nothing
14
- lost.
15
-
16
- - **`--target all`** installs codex + claude + pi in one run. **`both` stays a legacy alias**
17
- for codex + claude, so existing scripts and users keep their exact prior behavior.
18
- - `doctor --target pi` reports `healthy`, `version_match`, and the skill roster, exactly like
19
- the other targets.
20
-
21
- ## `golden_guard`: INV-GOLDEN-01 per target, reported honestly
22
- The doctor now reports, per target, whether the golden-write guard is **mechanical** or
23
- **advisory** — the field `golden_guard`:
24
-
25
- - **claude: `enforced`** when the PreToolUse hook is registered (else `advisory`).
26
- - **codex: `advisory`** — the Codex plugin has no hooks mechanism, so INV-GOLDEN-01 has always
27
- been advisory there (this just makes it visible).
28
- - **pi: `advisory`.** pi's blocking `tool_call` event can enforce the invariant, and the guard
29
- ships as a precompiled extension — `uscha-kit/pi/golden-guard.js`, plain JS, no build step, to
30
- respect the kit's stdlib-only limit. But the kit reports **advisory, not enforced**, because
31
- the block has not been measured against a real pi run — the same honesty the plugin
32
- `hooks.json` gap follows. A verified pi run flips it to enforced; until then the kit does not
33
- claim enforcement it hasn't seen.
34
-
35
- ## Not done (out of this release)
36
- Verified against a real pi install: the extension's actual load path and the `tool_call` block
37
- (the `advisory` above is exactly this gap). Also unverified: the Codex install roots on POSIX,
38
- and a real macOS end-to-end `npx` install (the CI matrix runs the suite on macOS runners, but
39
- not a user-style install flow). `AGENTS.md` (shipped 1.50.2) is the context file pi reads.
40
-
41
- Regression: smoke **T101** — pi installs 9 skills + marker under `~/.agents/skills`; doctor
42
- reads healthy for a real install and unhealthy for an empty home; `golden_guard` is `advisory`
43
- for pi and `enforced` for a hook-registered Claude install; `--target all` resolves to all
44
- three while `both` stays codex+claude.
@@ -1,33 +0,0 @@
1
- # uscha-kit 1.51.1 — the doctor recognizes the golden hook across an interpreter change (2026-07-24)
2
-
3
- A patch on 1.51.0, surfaced by the CI matrix's new real-installer end-to-end step (install
4
- `--target all` → doctor on every OS). On **Windows / Python 3.8** the step failed: `doctor
5
- --target claude` reported `hook_registered: False` for a hook that was, in fact, correctly
6
- installed. Not a CI artifact — a real doctor false-negative. Smoke suite: 388/388.
7
-
8
- ## Root cause
9
- The installer writes the INV-GOLDEN PreToolUse hook command via
10
- `list2cmdline([sys.executable, <hook path>])` — an **absolute interpreter path** plus the hook
11
- script. The doctor's `hook_registered` then checked for that command by **exact string
12
- equality**. When install-time and doctor-time run under different interpreters — a bare
13
- `python` that resolves to a different `sys.executable` between two invocations, as happened on
14
- the Windows 3.8 runner — the two command strings differ only in their interpreter prefix, and
15
- the exact-match reported the healthy hook as unregistered. That flipped `healthy` and the
16
- `golden_guard` trust signal to a false negative on an otherwise correct install.
17
-
18
- ## Fix
19
- `hook_registered` now matches a registered `"*"` hook by the **guard script it references**,
20
- not by the full command string — the same robustness the N-1 reinstall prune already uses. The
21
- match is **path-anchored** (`/block-approved-writes.py` or `\block-approved-writes.py`) so a
22
- foreign command that merely *mentions* the name — `...not-block-approved-writes.py`, or the
23
- literal inside a `-c` snippet — cannot read as our enforced guard, since this check feeds
24
- `golden_guard`. Exact command stays a fallback; a `None` command entry is skipped, not a crash.
25
-
26
- The interpreter-path sensitivity was latent before 1.51.0 (any Python move after install would
27
- trip it); the real-installer CI step is what finally measured it. This is the step doing its
28
- job — the same way the first matrix run caught the T75 router substring bug.
29
-
30
- Regression: smoke **T103** — `hook_registered` matches across an interpreter change (install
31
- wrote interpreter A, doctor computes B, same script → recognized), and does **not** match a
32
- suffix-collision script, a bare `-c` mention, a foreign hook, a `None` command, or empty
33
- settings. The CI real-installer step (all three OSes × Python 3.8/3.13) is the end-to-end proof.
@@ -1,57 +0,0 @@
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.