@andresmassello/uscha 1.56.0 → 1.59.0
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 +3 -3
- package/package.json +2 -1
- package/uscha-kit/.claude/skills/uscha-devloop/SKILL.md +21 -0
- package/uscha-kit/.claude/skills/uscha-devloop/qa_ledger.py +454 -7
- package/uscha-kit/.claude/skills/uscha-mirador/SKILL.md +11 -0
- package/uscha-kit/.claude/skills/uscha-mirador/mirador.template.html +39 -1
- package/uscha-kit/.claude/skills/uscha-status/SKILL.md +8 -0
- package/uscha-kit/.claude-plugin/plugin.json +2 -2
- package/uscha-kit/.codex-plugin/plugin.json +1 -1
- package/uscha-kit/README.md +54 -1
- package/uscha-kit/VERSION +1 -1
- package/uscha-kit/reports/junit/.fastpath-cases.json +1 -0
- package/uscha-kit/reports/junit/.specdrift-cases.json +1 -0
- package/uscha-kit/skills/uscha-devloop/SKILL.md +21 -0
- package/uscha-kit/skills/uscha-devloop/qa_ledger.py +454 -7
- package/uscha-kit/skills/uscha-mirador/SKILL.md +11 -0
- package/uscha-kit/skills/uscha-mirador/mirador.template.html +39 -1
- package/uscha-kit/skills/uscha-status/SKILL.md +8 -0
- package/uscha-kit/uscha.config.json +15 -1
|
@@ -278,6 +278,11 @@ svg{display:block;width:100%;height:auto}
|
|
|
278
278
|
The verdict above is all you need to look at. These roll up into it; they're here if you want to dig.
|
|
279
279
|
</p>
|
|
280
280
|
</div>
|
|
281
|
+
|
|
282
|
+
<div class="card col6" id="card-modes" hidden>
|
|
283
|
+
<h2>Fast-path · Spec-drift <span class="n" id="modes-n">—</span></h2>
|
|
284
|
+
<div class="speclist" id="modes"></div>
|
|
285
|
+
</div>
|
|
281
286
|
</section>
|
|
282
287
|
|
|
283
288
|
<section class="telemetry" id="telemetry" hidden>
|
|
@@ -654,6 +659,39 @@ function renderTelemetry(){
|
|
|
654
659
|
if(sec)sec.hidden=false;
|
|
655
660
|
}
|
|
656
661
|
|
|
662
|
+
function renderModes(){
|
|
663
|
+
/* fast_path: latest measured verdict per repo (ADR-003). spec_drift: latest advisory
|
|
664
|
+
run (ADR-005) -- advisory means it never feeds readiness; this card only makes the
|
|
665
|
+
ledger fact visible. BOTH keys are conditional in dashboard --json, so a project
|
|
666
|
+
that never used either sees no card at all (absent block = identical view). */
|
|
667
|
+
const card=$("#card-modes");if(!card)return;
|
|
668
|
+
const fp=DATA.fast_path,sd=DATA.spec_drift;
|
|
669
|
+
if(!fp&&!sd){card.hidden=true;return;}
|
|
670
|
+
const host=$("#modes");host.replaceChildren();
|
|
671
|
+
const parts=[];
|
|
672
|
+
if(fp){
|
|
673
|
+
const cls={ALLOW:"st-done",ESCALATED:"st-prog",DENY:"st-block"};
|
|
674
|
+
Object.keys(fp).forEach(r=>{const e=fp[r]||{};
|
|
675
|
+
append(host,append(node("div","spec"),node("span","id","fast-path"),
|
|
676
|
+
node("span","t",r+(e.intent?(" — "+e.intent):"")),
|
|
677
|
+
node("span","chip "+(cls[e.verdict]||"st-todo"),String(e.verdict||"?"))));});
|
|
678
|
+
parts.push(Object.keys(fp).length+" repo"+(Object.keys(fp).length>1?"s":""));
|
|
679
|
+
}
|
|
680
|
+
if(sd){
|
|
681
|
+
const cls={SPEC_STALE:"st-block",CLEAN:"st-done",UNMAPPED:"st-todo",UNTRACKED:"st-todo"};
|
|
682
|
+
const res=sd.results||[];
|
|
683
|
+
res.forEach(d=>{const extra=d.verdict==="SPEC_STALE"
|
|
684
|
+
?(" — "+(d.newer_files_total||0)+" file(s) newer than the spec"):"";
|
|
685
|
+
append(host,append(node("div","spec"),node("span","id","spec-drift"),
|
|
686
|
+
node("span","t",String(d.file||"")+extra),
|
|
687
|
+
node("span","chip "+(cls[d.verdict]||"st-todo"),String(d.verdict||"?"))));});
|
|
688
|
+
const stale=res.filter(d=>d.verdict==="SPEC_STALE").length;
|
|
689
|
+
parts.push(stale?stale+" stale spec"+(stale>1?"s":""):"no drift measured");
|
|
690
|
+
host.appendChild(emptyHint("spec-drift is ADVISORY (ADR-005): it prompts a human look, it never gates."));
|
|
691
|
+
}
|
|
692
|
+
$("#modes-n").textContent=parts.join(" · ");
|
|
693
|
+
card.hidden=false;
|
|
694
|
+
}
|
|
657
695
|
/* ==== status story: cómo viene / qué lo traba / qué sigue — all from measured DATA ==== */
|
|
658
696
|
function renderStatus(){
|
|
659
697
|
const sec=$("#status");if(!sec)return;
|
|
@@ -741,7 +779,7 @@ function applySnapshot(i){
|
|
|
741
779
|
scrub.addEventListener("input",()=>applySnapshot(+scrub.value));
|
|
742
780
|
|
|
743
781
|
/* ==== init ==== */
|
|
744
|
-
renderStatus();renderAcceptance();renderAdrs();renderInv();renderHeat();renderLoops();renderSubs();renderExecutionPolicy();renderTelemetry();
|
|
782
|
+
renderStatus();renderModes();renderAcceptance();renderAdrs();renderInv();renderHeat();renderLoops();renderSubs();renderExecutionPolicy();renderTelemetry();
|
|
745
783
|
$("#asof").textContent=snaps.length?snaps[snaps.length-1].date:"no history yet (readiness --record)";
|
|
746
784
|
renderTrail(null);
|
|
747
785
|
</script>
|
|
@@ -86,6 +86,14 @@ Line guide:
|
|
|
86
86
|
WHEN the evidence was captured — facts do not move without new evidence. If the
|
|
87
87
|
field is absent (nothing recorded yet), omit the line; never guess a timestamp.
|
|
88
88
|
|
|
89
|
+
**Fast-path mode (ADR-003):** if the ledger carries `fast_path` entries, add ONE line to the
|
|
90
|
+
block with the latest verdict per repo (`fast-path: ALLOW (intent...)` / `ESCALATED`). Absent
|
|
91
|
+
entries → no line at all: silence is honest when no mode was requested.
|
|
92
|
+
|
|
93
|
+
**Spec-drift (ADR-005):** if the ledger carries a `spec_drift` run, add ONE line:
|
|
94
|
+
`spec-drift: N stale / M docs (advisory)` — or `spec-drift: no drift measured` when zero are
|
|
95
|
+
stale. Always label it advisory; it never explains a blocked phase. Absent key → no line.
|
|
96
|
+
|
|
89
97
|
## Degradation (honest, specific)
|
|
90
98
|
|
|
91
99
|
- `measured` missing entirely → print: *"No measurement recorded yet — the trail
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "uscha",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.59.0",
|
|
5
5
|
"displayName": "Uscha",
|
|
6
|
-
"description": "Spec-driven development for LLM coding agents: 9 skills (discovery, adr-refine, reverse-discovery, characterize, devloop, sysdoc, rubric, mirador, status) + a stdlib measurement engine (qa_ledger.py,
|
|
6
|
+
"description": "Spec-driven development for LLM coding agents: 9 skills (discovery, adr-refine, reverse-discovery, characterize, devloop, sysdoc, rubric, mirador, status) + a stdlib measurement engine (qa_ledger.py, 31 subcommands + universal installer + npm/npx router). Facts block, guesses advise; the human approves.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Andres Massello",
|
|
9
9
|
"url": "https://github.com/andresmassello"
|
package/uscha-kit/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# uscha-kit
|
|
2
2
|
|
|
3
|
-
**Kit version:** v1.
|
|
3
|
+
**Kit version:** v1.59.0 <!-- uscha:version --> · **[uscha.dev](https://uscha.dev)**
|
|
4
4
|
|
|
5
5
|
Spec-driven orchestrator + multi-repo QA for Claude Code, with a deterministic ledger.
|
|
6
6
|
**Nine skills** (`uscha-discovery`, `uscha-adr-refine`, `uscha-devloop`, `uscha-sysdoc`, `uscha-reverse-discovery`,
|
|
@@ -38,6 +38,59 @@ uscha-kit/
|
|
|
38
38
|
└─ uscha-rubric/ # (optional) rubric grading — thin adapter; the core is agnostic
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
## Fast-path (ADR-003) — measured entry for trivial changes
|
|
42
|
+
|
|
43
|
+
A one-line fix should not pay the ceremony of a schema migration. `fastpath-eval` grants the
|
|
44
|
+
shortcut from **measured signals**, never from anyone's opinion of "small":
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python qa_ledger.py fastpath-eval --repo <name> --json # dry-run: verdict only
|
|
48
|
+
python qa_ledger.py fastpath-eval --repo <name> --intent "fix: x" # records it in the ledger
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Config (`defaults.fast_path`; absent block = feature off, behavior identical to earlier
|
|
52
|
+
releases): `max_files_changed` (3) and `max_loc_delta` (80) measured from
|
|
53
|
+
`git diff --numstat` against the merge-base with `origin/main` (fallback `main`) **plus
|
|
54
|
+
untracked files** — a new file is still a change; `protected_paths` globs deny regardless of
|
|
55
|
+
size; `require_asserting_test` caps readiness while a fast-path run has no measured test.
|
|
56
|
+
|
|
57
|
+
**Fail-closed:** no git, no resolvable base, no config → `DENY` with the reason named.
|
|
58
|
+
"Could not measure" never grants the shortcut. **Escalation:** re-running `fastpath-eval`
|
|
59
|
+
mid-run (the devloop does, before the PR step) with thresholds now exceeded flips the run to
|
|
60
|
+
`ESCALATED` through the standard escalation machinery — the derived phase blocks `pr-ready`
|
|
61
|
+
and readiness is capped until a human runs `resolve-escalation`, after producing the ADR +
|
|
62
|
+
ACCEPTANCE the change turned out to deserve. **The override is asymmetric (INV-RIGOR-02):**
|
|
63
|
+
you can always force the full path; nothing can force `ALLOW` over a measured `DENY`.
|
|
64
|
+
|
|
65
|
+
## Spec-drift (ADR-005) — the spec maintenance tax, made visible
|
|
66
|
+
|
|
67
|
+
Specs rot silently: the code moves and `SPEC.md` stays where it was. `spec-drift` detects
|
|
68
|
+
that lag **mechanically** — and reports it as **advisory, never a gate**, because whether an
|
|
69
|
+
older spec still covers newer code is a relevance judgment, and a guess advises.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
python qa_ledger.py spec-drift --repo <name> --json # advisory report; exit 0 always
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Map specs to code with a `governs:` glob list in the frontmatter of `SPEC.md` and each
|
|
76
|
+
`docs/adr/*.md`:
|
|
77
|
+
|
|
78
|
+
```markdown
|
|
79
|
+
---
|
|
80
|
+
governs:
|
|
81
|
+
- src/payments/**
|
|
82
|
+
- db/**
|
|
83
|
+
---
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Per document: **`SPEC_STALE`** when governed code outran the spec by more than
|
|
87
|
+
`defaults.spec_drift.max_lag_days` (default 30), listing the newer files; **`CLEAN`** when it
|
|
88
|
+
did not; **`UNMAPPED`** when there is no `governs:` frontmatter *or its globs match nothing*
|
|
89
|
+
— absence of a mapping is absence of measurement, not "no drift"; **`UNTRACKED`** when the
|
|
90
|
+
spec has no commit date to compare. The latest run lands in the ledger (`spec_drift`) so the
|
|
91
|
+
mirador can surface it. No readiness impact, no exit-code gate: a stale spec is a prompt for
|
|
92
|
+
a human conversation, not a blocked pipeline.
|
|
93
|
+
|
|
41
94
|
## End-to-end flow
|
|
42
95
|
|
|
43
96
|
`uscha-discovery` is the front for something new (you only have the idea); `uscha-adr-refine` is the front
|
package/uscha-kit/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
uscha-kit 1.
|
|
1
|
+
uscha-kit 1.59.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"AC-FP-01": true, "AC-FP-11": true, "AC-FP-07": true, "AC-FP-06": true, "AC-FP-05": true, "AC-FP-02": true, "AC-FP-03": true, "AC-FP-09": true, "AC-FP-10": true, "AC-FP-08": true}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"AC-SD-01": true, "AC-SD-03": true, "AC-SD-02": true, "AC-SD-04": true}
|
|
@@ -169,6 +169,27 @@ install `hooks/block-approved-writes.py` as a `PreToolUse` hook in
|
|
|
169
169
|
`settings.json`, and add `*.approved.* binary` to `.gitattributes` (ships in
|
|
170
170
|
`templates/.gitattributes`) so line endings can't lie in the byte-compare.
|
|
171
171
|
|
|
172
|
+
## Phase 0a — Fast-path check (ADR-003; run FIRST, before planning ceremony)
|
|
173
|
+
|
|
174
|
+
If `defaults.fast_path` exists in config, run the measured classifier before demanding a
|
|
175
|
+
full spec package:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
python qa_ledger.py fastpath-eval --repo <name> --json # dry-run first
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
- **ALLOW** and the operator wants the shortcut: re-run with
|
|
182
|
+
`--intent "<one sentence: what and why>"` to record it, then skip Phase 0's full-package
|
|
183
|
+
demand. The micro-contract replaces it: the recorded INTENT plus at least one new/modified
|
|
184
|
+
asserting test (readiness stays capped until that test shows up in measured evidence).
|
|
185
|
+
- **DENY**: state WHICH measured signal denied it — echo the engine's breakdown verbatim.
|
|
186
|
+
The skill wires; it never computes and never argues with the verdict. Proceed with the
|
|
187
|
+
normal full path. The operator may always choose the full path over an ALLOW; nothing —
|
|
188
|
+
operator, agent or flag — can force ALLOW over a DENY (INV-RIGOR-02).
|
|
189
|
+
- **Re-evaluate before the PR step** (same command, same intent): thresholds exceeded mid-run
|
|
190
|
+
flip the run to `ESCALATED` — the derived phase blocks pr-ready and readiness is capped.
|
|
191
|
+
Produce the ADR + ACCEPTANCE the change turned out to deserve, then `resolve-escalation`.
|
|
192
|
+
|
|
172
193
|
## Phase 0 — Plan (ADR-first)
|
|
173
194
|
|
|
174
195
|
- **Read `CONSTITUTION.md` first (if present).** It lists the project invariants no SPEC
|