@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.
- package/README.md +6 -1
- package/package.json +3 -2
- package/uscha-kit/.claude/skills/uscha-mirador/SKILL.md +11 -4
- package/uscha-kit/.claude/skills/uscha-mirador/mirador-render.py +16 -8
- package/uscha-kit/.claude-plugin/plugin.json +2 -2
- package/uscha-kit/.codex-plugin/plugin.json +2 -2
- package/uscha-kit/INSTALL.md +3 -0
- package/uscha-kit/README.md +1 -1
- package/uscha-kit/VERSION +1 -1
- package/uscha-kit/install-uscha.py +9 -3
- package/uscha-kit/skills/uscha-mirador/SKILL.md +11 -4
- package/uscha-kit/skills/uscha-mirador/mirador-render.py +16 -8
- package/uscha-kit/uscha.config.json +1 -1
- package/uscha-kit/CHANGELOG-1.10.0.md +0 -84
- package/uscha-kit/CHANGELOG-1.11.0.md +0 -67
- package/uscha-kit/CHANGELOG-1.12.0.md +0 -46
- package/uscha-kit/CHANGELOG-1.13.0.md +0 -33
- package/uscha-kit/CHANGELOG-1.14.0.md +0 -42
- package/uscha-kit/CHANGELOG-1.15.0.md +0 -58
- package/uscha-kit/CHANGELOG-1.16.0.md +0 -55
- package/uscha-kit/CHANGELOG-1.17.0.md +0 -44
- package/uscha-kit/CHANGELOG-1.18.0.md +0 -42
- package/uscha-kit/CHANGELOG-1.19.0.md +0 -41
- package/uscha-kit/CHANGELOG-1.2.2.md +0 -16
- package/uscha-kit/CHANGELOG-1.2.3.md +0 -20
- package/uscha-kit/CHANGELOG-1.2.4.md +0 -10
- package/uscha-kit/CHANGELOG-1.2.5.md +0 -23
- package/uscha-kit/CHANGELOG-1.2.6.md +0 -11
- package/uscha-kit/CHANGELOG-1.2.7.md +0 -15
- package/uscha-kit/CHANGELOG-1.2.8.md +0 -24
- package/uscha-kit/CHANGELOG-1.2.9.md +0 -4
- package/uscha-kit/CHANGELOG-1.20.0.md +0 -29
- package/uscha-kit/CHANGELOG-1.21.0.md +0 -33
- package/uscha-kit/CHANGELOG-1.22.0.md +0 -60
- package/uscha-kit/CHANGELOG-1.23.0.md +0 -75
- package/uscha-kit/CHANGELOG-1.24.0.md +0 -50
- package/uscha-kit/CHANGELOG-1.25.0.md +0 -55
- package/uscha-kit/CHANGELOG-1.26.0.md +0 -70
- package/uscha-kit/CHANGELOG-1.27.0.md +0 -45
- package/uscha-kit/CHANGELOG-1.28.0.md +0 -35
- package/uscha-kit/CHANGELOG-1.29.0.md +0 -20
- package/uscha-kit/CHANGELOG-1.3.0.md +0 -74
- package/uscha-kit/CHANGELOG-1.30.0.md +0 -46
- package/uscha-kit/CHANGELOG-1.31.0.md +0 -59
- package/uscha-kit/CHANGELOG-1.32.0.md +0 -50
- package/uscha-kit/CHANGELOG-1.33.0.md +0 -46
- package/uscha-kit/CHANGELOG-1.34.0.md +0 -55
- package/uscha-kit/CHANGELOG-1.35.0.md +0 -30
- package/uscha-kit/CHANGELOG-1.36.0.md +0 -33
- package/uscha-kit/CHANGELOG-1.37.0.md +0 -41
- package/uscha-kit/CHANGELOG-1.38.0.md +0 -11
- package/uscha-kit/CHANGELOG-1.39.0.md +0 -14
- package/uscha-kit/CHANGELOG-1.4.0.md +0 -68
- package/uscha-kit/CHANGELOG-1.40.0.md +0 -16
- package/uscha-kit/CHANGELOG-1.40.1.md +0 -11
- package/uscha-kit/CHANGELOG-1.40.2.md +0 -13
- package/uscha-kit/CHANGELOG-1.41.0.md +0 -18
- package/uscha-kit/CHANGELOG-1.41.1.md +0 -53
- package/uscha-kit/CHANGELOG-1.41.2.md +0 -34
- package/uscha-kit/CHANGELOG-1.41.3.md +0 -30
- package/uscha-kit/CHANGELOG-1.42.0.md +0 -41
- package/uscha-kit/CHANGELOG-1.43.0.md +0 -37
- package/uscha-kit/CHANGELOG-1.44.0.md +0 -90
- package/uscha-kit/CHANGELOG-1.44.1.md +0 -26
- package/uscha-kit/CHANGELOG-1.45.0.md +0 -58
- package/uscha-kit/CHANGELOG-1.46.0.md +0 -50
- package/uscha-kit/CHANGELOG-1.46.1.md +0 -35
- package/uscha-kit/CHANGELOG-1.47.0.md +0 -45
- package/uscha-kit/CHANGELOG-1.48.0.md +0 -35
- package/uscha-kit/CHANGELOG-1.48.1.md +0 -55
- package/uscha-kit/CHANGELOG-1.48.2.md +0 -47
- package/uscha-kit/CHANGELOG-1.49.0.md +0 -45
- package/uscha-kit/CHANGELOG-1.5.0.md +0 -64
- package/uscha-kit/CHANGELOG-1.50.0.md +0 -52
- package/uscha-kit/CHANGELOG-1.50.1.md +0 -52
- package/uscha-kit/CHANGELOG-1.50.2.md +0 -62
- package/uscha-kit/CHANGELOG-1.51.0.md +0 -44
- package/uscha-kit/CHANGELOG-1.51.1.md +0 -33
- package/uscha-kit/CHANGELOG-1.6.0.md +0 -57
- package/uscha-kit/CHANGELOG-1.7.0.md +0 -74
- package/uscha-kit/CHANGELOG-1.8.0.md +0 -46
- 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.
|