@andresmassello/uscha 1.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +104 -0
- package/bin/README.md +6 -0
- package/bin/uscha.js +28 -0
- package/package.json +38 -0
- package/uscha-kit/.claude/skills/uscha-adr-refine/SKILL.md +161 -0
- package/uscha-kit/.claude/skills/uscha-characterize/SKILL.md +124 -0
- package/uscha-kit/.claude/skills/uscha-devloop/SKILL.md +490 -0
- package/uscha-kit/.claude/skills/uscha-devloop/qa_ledger.py +5602 -0
- package/uscha-kit/.claude/skills/uscha-discovery/SKILL.md +161 -0
- package/uscha-kit/.claude/skills/uscha-mirador/SKILL.md +145 -0
- package/uscha-kit/.claude/skills/uscha-mirador/mirador-render.py +112 -0
- package/uscha-kit/.claude/skills/uscha-mirador/mirador-watch.ps1 +22 -0
- package/uscha-kit/.claude/skills/uscha-mirador/mirador-watch.sh +26 -0
- package/uscha-kit/.claude/skills/uscha-mirador/mirador.template.html +586 -0
- package/uscha-kit/.claude/skills/uscha-mirador/telemetry-extract.py +130 -0
- package/uscha-kit/.claude/skills/uscha-reverse-discovery/SKILL.md +116 -0
- package/uscha-kit/.claude/skills/uscha-rubric/SKILL.md +79 -0
- package/uscha-kit/.claude/skills/uscha-sysdoc/SKILL.md +88 -0
- package/uscha-kit/.claude-plugin/plugin.json +24 -0
- package/uscha-kit/.codex-plugin/plugin.json +37 -0
- package/uscha-kit/CHANGELOG-1.10.0.md +84 -0
- package/uscha-kit/CHANGELOG-1.11.0.md +67 -0
- package/uscha-kit/CHANGELOG-1.12.0.md +46 -0
- package/uscha-kit/CHANGELOG-1.13.0.md +33 -0
- package/uscha-kit/CHANGELOG-1.14.0.md +42 -0
- package/uscha-kit/CHANGELOG-1.15.0.md +58 -0
- package/uscha-kit/CHANGELOG-1.16.0.md +55 -0
- package/uscha-kit/CHANGELOG-1.17.0.md +44 -0
- package/uscha-kit/CHANGELOG-1.18.0.md +42 -0
- package/uscha-kit/CHANGELOG-1.19.0.md +41 -0
- package/uscha-kit/CHANGELOG-1.2.2.md +16 -0
- package/uscha-kit/CHANGELOG-1.2.3.md +20 -0
- package/uscha-kit/CHANGELOG-1.2.4.md +10 -0
- package/uscha-kit/CHANGELOG-1.2.5.md +23 -0
- package/uscha-kit/CHANGELOG-1.2.6.md +11 -0
- package/uscha-kit/CHANGELOG-1.2.7.md +15 -0
- package/uscha-kit/CHANGELOG-1.2.8.md +24 -0
- package/uscha-kit/CHANGELOG-1.2.9.md +4 -0
- package/uscha-kit/CHANGELOG-1.20.0.md +29 -0
- package/uscha-kit/CHANGELOG-1.21.0.md +33 -0
- package/uscha-kit/CHANGELOG-1.22.0.md +60 -0
- package/uscha-kit/CHANGELOG-1.23.0.md +75 -0
- package/uscha-kit/CHANGELOG-1.24.0.md +50 -0
- package/uscha-kit/CHANGELOG-1.25.0.md +55 -0
- package/uscha-kit/CHANGELOG-1.26.0.md +70 -0
- package/uscha-kit/CHANGELOG-1.27.0.md +45 -0
- package/uscha-kit/CHANGELOG-1.28.0.md +35 -0
- package/uscha-kit/CHANGELOG-1.29.0.md +20 -0
- package/uscha-kit/CHANGELOG-1.3.0.md +74 -0
- package/uscha-kit/CHANGELOG-1.30.0.md +46 -0
- package/uscha-kit/CHANGELOG-1.31.0.md +59 -0
- package/uscha-kit/CHANGELOG-1.32.0.md +50 -0
- package/uscha-kit/CHANGELOG-1.33.0.md +46 -0
- package/uscha-kit/CHANGELOG-1.34.0.md +55 -0
- package/uscha-kit/CHANGELOG-1.35.0.md +30 -0
- package/uscha-kit/CHANGELOG-1.36.0.md +33 -0
- package/uscha-kit/CHANGELOG-1.37.0.md +41 -0
- package/uscha-kit/CHANGELOG-1.38.0.md +11 -0
- package/uscha-kit/CHANGELOG-1.39.0.md +14 -0
- package/uscha-kit/CHANGELOG-1.4.0.md +68 -0
- package/uscha-kit/CHANGELOG-1.40.0.md +16 -0
- package/uscha-kit/CHANGELOG-1.40.1.md +11 -0
- package/uscha-kit/CHANGELOG-1.5.0.md +64 -0
- package/uscha-kit/CHANGELOG-1.6.0.md +57 -0
- package/uscha-kit/CHANGELOG-1.7.0.md +74 -0
- package/uscha-kit/CHANGELOG-1.8.0.md +46 -0
- package/uscha-kit/CHANGELOG-1.9.0.md +112 -0
- package/uscha-kit/LICENSE +21 -0
- package/uscha-kit/README.md +497 -0
- package/uscha-kit/VERSION +1 -0
- package/uscha-kit/WORKBENCH.md +178 -0
- package/uscha-kit/hooks/block-approved-writes.ps1 +46 -0
- package/uscha-kit/hooks/hooks.json +15 -0
- package/uscha-kit/install-uscha.py +344 -0
- package/uscha-kit/skills/uscha-adr-refine/SKILL.md +161 -0
- package/uscha-kit/skills/uscha-characterize/SKILL.md +124 -0
- package/uscha-kit/skills/uscha-devloop/SKILL.md +490 -0
- package/uscha-kit/skills/uscha-devloop/qa_ledger.py +5602 -0
- package/uscha-kit/skills/uscha-discovery/SKILL.md +161 -0
- package/uscha-kit/skills/uscha-mirador/SKILL.md +145 -0
- package/uscha-kit/skills/uscha-mirador/mirador-render.py +112 -0
- package/uscha-kit/skills/uscha-mirador/mirador-watch.ps1 +22 -0
- package/uscha-kit/skills/uscha-mirador/mirador-watch.sh +26 -0
- package/uscha-kit/skills/uscha-mirador/mirador.template.html +586 -0
- package/uscha-kit/skills/uscha-mirador/telemetry-extract.py +130 -0
- package/uscha-kit/skills/uscha-reverse-discovery/SKILL.md +116 -0
- package/uscha-kit/skills/uscha-rubric/SKILL.md +79 -0
- package/uscha-kit/skills/uscha-sysdoc/SKILL.md +88 -0
- package/uscha-kit/templates/.gitattributes +6 -0
- package/uscha-kit/templates/CLAUDE.md +56 -0
- package/uscha-kit/templates/CONSTITUTION.md +149 -0
- package/uscha-kit/templates/RUBRIC.md +38 -0
- package/uscha-kit/templates/docs/adr/README.md +19 -0
- package/uscha-kit/templates/rubric-grader-prompt.md +63 -0
- package/uscha-kit/tests/smoke-engine.sh +1739 -0
- package/uscha-kit/uscha.config.json +181 -0
- package/uscha-kit/workbench-doctor.sh +45 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: uscha-discovery
|
|
3
|
+
description: >
|
|
4
|
+
Front of the methodology for greenfield work. From a general idea (plus optional
|
|
5
|
+
reference docs, URLs, PDFs, or an existing codebase), grill the user ONE question at
|
|
6
|
+
a time — each with your recommended answer — PROPOSING the system shape (domain model,
|
|
7
|
+
API/operation surface, architecture options) so the human approves instead of authors.
|
|
8
|
+
Write the spec package (CONTEXT.md, SPEC.md, docs/adr/*.md, ACCEPTANCE.md, RISKS.md,
|
|
9
|
+
HANDOFF.md) directly to the repo as decisions crystallize. Invoke for "discovery",
|
|
10
|
+
"modelá esto desde una idea", "solo tengo la idea, no sé el cómo todavía".
|
|
11
|
+
allowed-tools: Read, Write, Glob, Grep, WebFetch
|
|
12
|
+
disable-model-invocation: false
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# discovery — from a bare idea to a writable spec package
|
|
16
|
+
|
|
17
|
+
The human brings the idea, the constraints and the reference material. **You bring the
|
|
18
|
+
shape.** Your job is to interrogate until there is a shared system shape, and to write
|
|
19
|
+
the documents as you go — not to ask the human to design the system for you.
|
|
20
|
+
|
|
21
|
+
## Non-negotiable principles
|
|
22
|
+
|
|
23
|
+
1. **One question at a time, each WITH your recommended answer.** This is the inversion
|
|
24
|
+
that makes discovery work: you propose (entities, endpoints, architecture, a default
|
|
25
|
+
decision), the human confirms or corrects. Never dump a list of 20 questions, and
|
|
26
|
+
never ask the human to supply structure you can propose yourself.
|
|
27
|
+
2. **Explore instead of asking.** If a reference doc/URL/PDF, the existing codebase, or
|
|
28
|
+
an existing `CONTEXT.md`/`docs/adr/` can answer a question, read it first. Only ask
|
|
29
|
+
the human what genuinely requires their judgment.
|
|
30
|
+
3. **Propose the shape.** From the idea + references, propose the core entities, the
|
|
31
|
+
operation/API surface, and 2–3 architecture options with trade-offs. Walk the design
|
|
32
|
+
tree branch by branch, resolving dependencies between decisions one at a time.
|
|
33
|
+
4. **Grill, don't agree.** Surface contradictions, fuzzy/overloaded terms, missing
|
|
34
|
+
failure modes and unstated constraints. A discovery where you agreed with everything
|
|
35
|
+
failed.
|
|
36
|
+
5. **Write files lazily and inline.** Create a file only when you have something real to
|
|
37
|
+
write, and update it the moment a decision crystallizes — don't batch to the end.
|
|
38
|
+
|
|
39
|
+
## Inputs
|
|
40
|
+
|
|
41
|
+
- **The idea** (required): what the human wants to achieve. It doesn't have to be
|
|
42
|
+
complete or well-formed.
|
|
43
|
+
- **Reference material** (optional): manuals, PDFs, URLs, API specs. Read them with
|
|
44
|
+
WebFetch/Read before proposing — they are how you propose an accurate shape.
|
|
45
|
+
- **Existing codebase** (optional): if present, explore it; respect `CONTEXT.md` and
|
|
46
|
+
existing ADRs.
|
|
47
|
+
|
|
48
|
+
## Domain awareness (explore first)
|
|
49
|
+
|
|
50
|
+
- Look for `CONTEXT.md` (domain glossary), `CONSTITUTION.md` (project invariants) and
|
|
51
|
+
`docs/adr/` and read them. The CONSTITUTION constrains every shape you propose — never
|
|
52
|
+
propose anything that would violate it.
|
|
53
|
+
- **Sharpen fuzzy language.** When the human uses a vague or overloaded term, propose a
|
|
54
|
+
precise canonical term: "You say 'account' — the Customer or the User? They're different things."
|
|
55
|
+
- **Maintain a glossary in `CONTEXT.md`**, updated inline as each term is resolved. Only
|
|
56
|
+
terms meaningful to domain experts; don't couple it to implementation details.
|
|
57
|
+
|
|
58
|
+
## The grilling agenda (walk the tree, propose at each step)
|
|
59
|
+
|
|
60
|
+
Resolve these in order; for each, propose first, then ask. Skip what references already
|
|
61
|
+
answer.
|
|
62
|
+
|
|
63
|
+
0. **Project name (first).** The very first thing you ask: "What do we call this?" Write the
|
|
64
|
+
answer to `uscha.config.json` as `"project": "<name>"` (create the config if absent). The
|
|
65
|
+
mirador shows it prominently at the top and the dashboard reads it from there; if unset it
|
|
66
|
+
falls back to the joined repo names.
|
|
67
|
+
1. **Purpose / value / why now.** What job does this remove? Cost of not doing it?
|
|
68
|
+
2. **Domain model.** Propose the core entities and their relationships. ("From the domain
|
|
69
|
+
I deduce these core entities: … do they work for you, or is one missing?")
|
|
70
|
+
3. **Operation / API surface.** Propose the endpoints/operations and their
|
|
71
|
+
contracts (idempotency, status codes).
|
|
72
|
+
4. **Big decisions (→ ADR).** Propose 2–3 architecture options with trade-offs and
|
|
73
|
+
a recommended default: persistence, protocol, idempotency, sync/async, multi-tenancy.
|
|
74
|
+
5. **Behavior and dirty cases.** Happy path, then failures, retries, partial states,
|
|
75
|
+
concurrency, what must NOT happen.
|
|
76
|
+
6. **Inviolable constraints (→ `CONSTITUTION.md`).** Domain + security + operation
|
|
77
|
+
rules that can't be broken (money to the cent, no numbering gaps, never cross
|
|
78
|
+
environments, secrets never logged, auth). Write/extend `CONSTITUTION.md` with these —
|
|
79
|
+
one invariant per line, with a CWE reference where it maps. They feed the severity gate
|
|
80
|
+
downstream, and a breach is a BLOCKER, never a trade-off.
|
|
81
|
+
7. **Out of scope.** Explicit boundaries with forward references.
|
|
82
|
+
8. **Acceptance / Definition of Done.** Concrete, checkable criteria + success metrics.
|
|
83
|
+
9. **Quality bar (→ config, kit 1.17.0).** "What level of quality is ENOUGH here, and which
|
|
84
|
+
dimensions are negotiable (coverage, perf, security)?" Propose thresholds fit for
|
|
85
|
+
the risk profile (a payments core is not an internal dashboard). What the human
|
|
86
|
+
declares goes into `uscha.config.json` (`defaults.coverage_threshold`,
|
|
87
|
+
`defaults.readiness_caps`, `defaults.simplicity`) — a declared threshold reads as
|
|
88
|
+
**requerimiento (config)** in the engine's output; an undeclared one stays a kit
|
|
89
|
+
default (opinion) and is labeled as such. Declaring is committing the config.
|
|
90
|
+
10. **Residual risks and dependencies.** What's uncertain, what must exist first.
|
|
91
|
+
For each HIGH-uncertainty risk, ask (kit 1.19.0, Tip 21 'Prototype to Learn'):
|
|
92
|
+
"Does it warrant a time-boxed spike before freezing the SPEC?" A spike runs on a
|
|
93
|
+
`spike/*` branch and its ONLY legitimate output is an **ADR with lessons**
|
|
94
|
+
(facts that feed the SPEC) — never mergeable code. The contract is executable:
|
|
95
|
+
`phase --require pr-ready` refuses any `spike/*` branch, INV-GOLDEN-01 style.
|
|
96
|
+
|
|
97
|
+
## Files to write (lazily, inline)
|
|
98
|
+
|
|
99
|
+
- **`CONTEXT.md`** — domain glossary; create on first resolved term, update as you go.
|
|
100
|
+
- **`CONSTITUTION.md`** — project invariants no ADR/SPEC may violate (security with CWE
|
|
101
|
+
refs, domain rules, operation). Create/extend from the "restricciones inviolables" step.
|
|
102
|
+
This is the layer above the ADRs; a breach is a BLOCKER finding downstream.
|
|
103
|
+
- **`DOMAIN-MODEL.md`** — the proposed core entities and their relationships (the "shape"
|
|
104
|
+
you proposed and the human approved). Distinct from the glossary: this is the model, not
|
|
105
|
+
the vocabulary.
|
|
106
|
+
- **`SPEC.md`** — objective/value, risk, scope/out-of-scope, behavior,
|
|
107
|
+
inputs/outputs/errors, acceptance, test plan, operation, rollback.
|
|
108
|
+
- **`docs/adr/ADR-NNN-<slug>.md`** — one per durable decision. Format: Status
|
|
109
|
+
(proposed/accepted/**experiment**/deprecated/superseded) · Context · Alternatives · Decision ·
|
|
110
|
+
Consequences · **Implementation Plan** (affected paths, patterns to follow, tests to
|
|
111
|
+
write) · **Verification** (`- [ ]` checkboxes a coding agent can check). The
|
|
112
|
+
Implementation Plan makes the ADR an executable spec: the agent implements it without
|
|
113
|
+
asking follow-ups. Number from the highest existing ADR. Use `Status: Experiment` only
|
|
114
|
+
for a bounded, reversible hypothesis that needs real feedback; include `Hypothesis`,
|
|
115
|
+
`Feedback Signal`, `Review By` or `Review Trigger`, `Promote Criteria`, and
|
|
116
|
+
`Rollback / Supersede Criteria`.
|
|
117
|
+
- **`ACCEPTANCE.md`** — Definition of Done as `- [ ]` checkboxes + success metrics. This
|
|
118
|
+
is the file the readiness KPI measures downstream. Give EVERY criterion a stable
|
|
119
|
+
traceable ID: `- [ ] AC-01 — when X then Y` (sequential, never reused). Downstream,
|
|
120
|
+
a criterion only closes MEASURED when a green testcase carries its tag in the name
|
|
121
|
+
(`test_ac1_x` / `testAC01X` / `"AC-01: ..."`) — write criteria so each one is
|
|
122
|
+
coverable by at least one named test.
|
|
123
|
+
- **`RISKS.md`** — residual risks, assumptions, points needing human approval.
|
|
124
|
+
- **`HANDOFF.md`** — what to read before coding + hard "no hacer" rules + required evidence.
|
|
125
|
+
|
|
126
|
+
## Offer ADRs sparingly
|
|
127
|
+
|
|
128
|
+
Only write an ADR when ALL three hold (otherwise it's noise that buries the important ones):
|
|
129
|
+
1. **Hard to reverse** — changing your mind later has real cost.
|
|
130
|
+
2. **Surprising without context** — a future reader will wonder "why this way?".
|
|
131
|
+
3. **A real trade-off** — there were genuine alternatives and you chose one for reasons.
|
|
132
|
+
|
|
133
|
+
## Convergence — finish when
|
|
134
|
+
|
|
135
|
+
A shared system shape exists: entities, operations and big decisions taken (or recorded
|
|
136
|
+
as explicit assumptions), every failure mode has defined behavior, out-of-scope is
|
|
137
|
+
explicit, and the DoD is checkable. State plainly that it converged, then write/finalize
|
|
138
|
+
the package and the handoff.
|
|
139
|
+
|
|
140
|
+
## Handoff
|
|
141
|
+
|
|
142
|
+
End with the implementation handoff (works for a human, an agent, or CI):
|
|
143
|
+
|
|
144
|
+
> "Read CONTEXT.md, SPEC.md, docs/adr/*.md and ACCEPTANCE.md. Before touching code:
|
|
145
|
+
> 1) summarize the expected behavior, 2) flag ambiguities or contradictions,
|
|
146
|
+
> 3) propose a file plan + tests. Implement only the SPEC's scope. Do not change
|
|
147
|
+
> contracts outside the SPEC, and do not edit the SPEC to make your implementation look correct."
|
|
148
|
+
|
|
149
|
+
Flow: `/uscha-discovery` (idea → package) → `/uscha-devloop` (build + QA + evidence) → human gate.
|
|
150
|
+
|
|
151
|
+
## Relationship to adr-refine
|
|
152
|
+
|
|
153
|
+
`uscha-discovery` is the greenfield front: you only have an idea, so the skill PROPOSES the
|
|
154
|
+
shape. `uscha-adr-refine` is the same interview applied to a KNOWN feature where the shape is
|
|
155
|
+
already clear and you only need precision. Both emit the same package; pick by starting
|
|
156
|
+
point.
|
|
157
|
+
|
|
158
|
+
## Tracked-markdown protocol
|
|
159
|
+
|
|
160
|
+
If a target `.md` already exists and is tracked, ask for its current version before
|
|
161
|
+
overwriting — never silently replace real progress.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: uscha-mirador
|
|
3
|
+
description: >
|
|
4
|
+
Bird's-eye status view ("mirador") of a spec-loop project: one glance at
|
|
5
|
+
readiness, sub-scores, the phase path, invariants, QA loops and a readiness
|
|
6
|
+
time-lapse. Runs `qa_ledger.py dashboard --json` (read-only, deterministic,
|
|
7
|
+
zero LLM narration), injects that JSON into mirador.template.html, writes
|
|
8
|
+
mirador.html at the project root and opens it. The skill WIRES, it does not
|
|
9
|
+
calculate — every number comes from the ledger (truth-pass: a field with no
|
|
10
|
+
source shows null and the template degrades, never invented). Invoke for
|
|
11
|
+
"mirador", "vista de estado", "bird's-eye", "dashboard del proyecto".
|
|
12
|
+
allowed-tools: Read, Write, Glob, Grep, Bash
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# uscha-mirador — bird's-eye status view
|
|
16
|
+
|
|
17
|
+
Paints the REAL state of the project at a glance. It does not narrate or estimate: it
|
|
18
|
+
wires the JSON the engine emits into the template. Read-only.
|
|
19
|
+
|
|
20
|
+
## Contract
|
|
21
|
+
|
|
22
|
+
- **Source of truth:** `qa_ledger.py dashboard --json` — aggregates ONLY state the
|
|
23
|
+
ledger already has (readiness, subscores, phases, specs, adrs, inv, layers, loops,
|
|
24
|
+
snapshots, evidence). A field with no source comes out `null`/`[]`; the template
|
|
25
|
+
degrades. **A datum is never invented.**
|
|
26
|
+
- **Template:** `mirador.template.html` (in this folder). The data block lives between
|
|
27
|
+
`/*MIRADOR_DATA_START*/` and `/*MIRADOR_DATA_END*/`; the sample `const DATA` it ships
|
|
28
|
+
with is the **offline fallback**. The skill replaces ONLY that region; it does not
|
|
29
|
+
touch the rest of the HTML.
|
|
30
|
+
- **Project name (top):** the mirador shows the project name prominently at the top, from
|
|
31
|
+
`config.project` in `uscha.config.json` (set it in `uscha-discovery`); if unset, it falls
|
|
32
|
+
back to the joined repo names (truth-pass — no invented name).
|
|
33
|
+
- **Execution policy (bird's-eye):** `dashboard --json` also exposes
|
|
34
|
+
`execution_policy` plus `phase.execution` for each trail node. This is routing metadata
|
|
35
|
+
from `config.defaults.execution_policy` (`method`, `tier`, `model`, `effort`,
|
|
36
|
+
`uncorrelated`), not a score. The template renders it as a separate panel so the human
|
|
37
|
+
sees which methodology/model/effort is selected per phase without mixing it into
|
|
38
|
+
readiness.
|
|
39
|
+
- **Discovery intake:** `dashboard --json` carries `discovery_intake` from readiness:
|
|
40
|
+
open `production_findings`, `spec_doubts`, and `spec_change_requests`. The current template keeps the top-level
|
|
41
|
+
verdict honest through readiness/caps; future panels may render the intake directly.
|
|
42
|
+
- **ADR experiments:** ADR rows may carry `adr_status: "experiment"`, `review_by`,
|
|
43
|
+
`review_trigger`, `experiment_valid`, `experiment_missing`, and `expired`; top-level
|
|
44
|
+
`adr_experiments` summarizes open/malformed/expired experiments. This is advisory
|
|
45
|
+
visibility for measured hypotheses, not readiness scoring.
|
|
46
|
+
- **Session telemetry (optional, vendor-reported):** if `.uscha/telemetry.jsonl` exists,
|
|
47
|
+
the skill aggregates it and MERGES a `telemetry` object into `DATA`. This is the ONE
|
|
48
|
+
panel that is **narrated by the vendor (Claude Code), not measured by the engine** —
|
|
49
|
+
tokens, wall time, model, effort — so it renders in a **segregated strip** labeled as
|
|
50
|
+
such, never mixed with the measured panels. `dashboard --json` NEVER emits it; it enters
|
|
51
|
+
only through this adapter. Absent file → no strip (degrades). This is the doctrinal
|
|
52
|
+
line: telemetry is neither a fact-gate nor a guess-advisor, so it must not dilute
|
|
53
|
+
*measured beats narrated*.
|
|
54
|
+
|
|
55
|
+
## Flow
|
|
56
|
+
|
|
57
|
+
1. **Resolve the engine.** Use the first one that exists:
|
|
58
|
+
- `./.claude/skills/uscha-devloop/qa_ledger.py` (per-project install)
|
|
59
|
+
- `~/plugins/uscha/skills/uscha-devloop/qa_ledger.py` (Codex plugin install)
|
|
60
|
+
- `~/.codex/skills/uscha-devloop/qa_ledger.py` (Codex raw-skills install)
|
|
61
|
+
- `~/.claude/skills/uscha-devloop/qa_ledger.py` (Claude global install)
|
|
62
|
+
|
|
63
|
+
2. **Resolve the ledger.** `QA-LEDGER.json` in the cwd (or the `--ledger` the user
|
|
64
|
+
points to). If it does not exist, warn: you have to run `uscha-devloop` (or
|
|
65
|
+
`qa_ledger.py init`) first — with no ledger there is no state to look at.
|
|
66
|
+
|
|
67
|
+
(`<skill-dir>` below = this folder: `.claude/skills/uscha-mirador/` per-project, or `~/plugins/uscha/skills/uscha-mirador/` / `~/.codex/skills/uscha-mirador/` / `~/.claude/skills/uscha-mirador/` global.)
|
|
68
|
+
|
|
69
|
+
3. **(Optional) record this session's telemetry.** For the vendor-telemetry strip, append
|
|
70
|
+
the current Claude Code session to the sidecar:
|
|
71
|
+
```bash
|
|
72
|
+
python3 <skill-dir>/telemetry-extract.py <path-to-CC-transcript.jsonl>
|
|
73
|
+
```
|
|
74
|
+
It **upserts by session** (safe to re-run — a watch loop won't inflate the totals). Skip
|
|
75
|
+
this for a pure measured view.
|
|
76
|
+
|
|
77
|
+
4. **Render `mirador.html`** with the standalone renderer — it runs `dashboard --json`,
|
|
78
|
+
merges the sidecar telemetry if present, injects `const DATA`, and writes the file:
|
|
79
|
+
```bash
|
|
80
|
+
python3 <skill-dir>/mirador-render.py --engine <engine> --ledger QA-LEDGER.json \
|
|
81
|
+
--template <skill-dir>/mirador.template.html --out mirador.html
|
|
82
|
+
```
|
|
83
|
+
The engine stays model-agnostic — telemetry is merged by the renderer (the adapter), NOT
|
|
84
|
+
by `dashboard`. If the ledger is missing, warn and stop (run `uscha-devloop` first). For
|
|
85
|
+
the time-lapse, `qa_ledger.py readiness --record` (opt-in) fills the history prospectively.
|
|
86
|
+
|
|
87
|
+
5. **Open it** (best-effort; never fail — headless/CI): `start "" mirador.html` (Windows) /
|
|
88
|
+
`open mirador.html` (macOS) / `xdg-open mirador.html` (Linux). ALWAYS print the absolute
|
|
89
|
+
path of `mirador.html`.
|
|
90
|
+
|
|
91
|
+
## Live second-screen view
|
|
92
|
+
|
|
93
|
+
For a mirador that updates while you keep coding in the terminal, run the watch loop in a
|
|
94
|
+
spare terminal and open `mirador.html` on a second monitor:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Windows: powershell -NoProfile -File <skill-dir>\mirador-watch.ps1 -Interval 30
|
|
98
|
+
# Unix: bash <skill-dir>/mirador-watch.sh 30
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
It re-renders `mirador.html` every N seconds (default 30) from the current ledger, rendered
|
|
102
|
+
with a matching `<meta http-equiv="refresh">` so the open page reloads on its own — **no
|
|
103
|
+
server**. It is only as live as the **ledger**: the picture changes at the dev-loop's
|
|
104
|
+
measured checkpoints (snapshots, gates, `readiness --record`), not per keystroke. Honest by
|
|
105
|
+
design — it shows measured state, which changes at measured moments.
|
|
106
|
+
|
|
107
|
+
## Session telemetry — sidecar contract
|
|
108
|
+
|
|
109
|
+
`.uscha/telemetry.jsonl` is an **append-only** file, one JSON object per session/run:
|
|
110
|
+
|
|
111
|
+
```jsonl
|
|
112
|
+
{"at": "2026-07-05T23:40:00Z", "model": "claude-opus-4-8", "tokens_in": 240000, "tokens_out": 41000, "ms": 4200000, "by_model": [{"model": "claude-opus-4-8", "tokens_in": 240000, "tokens_out": 41000}], "note": "mirador build"}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`effort` and `note` are **optional** and are NOT produced by `telemetry-extract.py` (a Claude
|
|
116
|
+
Code transcript carries no reliable effort label) — add them by hand if you want them; the
|
|
117
|
+
strip shows `—` when `effort` is absent. `by_model` is per-**tokens** (session-level `ms` only).
|
|
118
|
+
|
|
119
|
+
- **Who writes it:** the AGENT / operator, NEVER the engine (the engine is model-agnostic
|
|
120
|
+
and cannot see tokens). Two honest sources:
|
|
121
|
+
- **`telemetry-extract.py <transcript.jsonl>`** (shipped in this skill folder): parses a
|
|
122
|
+
Claude Code session transcript — each assistant turn carries a `usage` block
|
|
123
|
+
(`input_tokens` / `cache_*_input_tokens` / `output_tokens`) and a `model` — sums per
|
|
124
|
+
model, computes wall time from the timestamps, and appends one line (with a `by_model`
|
|
125
|
+
breakdown). Real, vendor-native data. Best-effort: unknown/older schemas degrade, never
|
|
126
|
+
crash; no usage found → nothing appended.
|
|
127
|
+
- **Manual append**: paste the session's numbers from Claude Code's own cost view.
|
|
128
|
+
- **Persistence:** the file itself IS the history (portable, diffable, greppable). Add it to
|
|
129
|
+
the project's `.gitignore` — it is per-machine telemetry, not repo state. (A pure-client
|
|
130
|
+
alternative is `localStorage` inside `mirador.html`, but the sidecar survives regeneration,
|
|
131
|
+
so it is the default.)
|
|
132
|
+
- **Boundary (non-negotiable):** this is the ONLY place vendor-narrated numbers enter the
|
|
133
|
+
mirador. They are shown, never gated, never fed into readiness — telemetry answers "what
|
|
134
|
+
did it cost", the measured panels answer "is it correct". Keep them apart.
|
|
135
|
+
|
|
136
|
+
## Rules
|
|
137
|
+
|
|
138
|
+
- **Zero computation in the skill.** The numbers come from the ledger. If a panel looks
|
|
139
|
+
empty, it's because the engine doesn't have that source yet (truth-pass), not because
|
|
140
|
+
painting it was missed.
|
|
141
|
+
- **Read-only.** The skill does not run gates or modify the ledger. `dashboard` is
|
|
142
|
+
read-only; the only one that writes (opt-in) is `readiness --record`, and that is the
|
|
143
|
+
loop's decision, not this skill's.
|
|
144
|
+
- **`mirador.html` is a generated artifact** — suggest adding it to the project's
|
|
145
|
+
`.gitignore`; it regenerates whenever you want.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""mirador-render.py -- render mirador.html from the live ledger (uscha-kit 1.34.0).
|
|
3
|
+
|
|
4
|
+
Standalone renderer used by the uscha-mirador skill AND by mirador-watch (the live
|
|
5
|
+
second-screen loop). It:
|
|
6
|
+
1. runs `qa_ledger.py dashboard --json` (read-only engine call),
|
|
7
|
+
2. if the telemetry sidecar exists, aggregates it (per-model merge) and merges a
|
|
8
|
+
`telemetry` object into DATA -- vendor-reported, NEVER from the engine,
|
|
9
|
+
3. injects `const DATA` between the template's markers and writes mirador.html.
|
|
10
|
+
|
|
11
|
+
The engine stays model-agnostic: telemetry is merged HERE, in the adapter, never in
|
|
12
|
+
qa_ledger.py.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
python3 mirador-render.py --engine <qa_ledger.py> --ledger QA-LEDGER.json \
|
|
16
|
+
--template mirador.template.html --out mirador.html \
|
|
17
|
+
[--sidecar .uscha/telemetry.jsonl] [--refresh 30]
|
|
18
|
+
"""
|
|
19
|
+
import argparse
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import re
|
|
23
|
+
import subprocess
|
|
24
|
+
import sys
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def aggregate_telemetry(path):
|
|
28
|
+
"""Sum the sidecar's per-session lines into one telemetry object, merging by_model
|
|
29
|
+
(tokens per model) across sessions. Returns None if the file is absent/empty."""
|
|
30
|
+
if not path or not os.path.isfile(path):
|
|
31
|
+
return None
|
|
32
|
+
lines = []
|
|
33
|
+
for line in open(path, encoding="utf-8"):
|
|
34
|
+
line = line.strip()
|
|
35
|
+
if not line:
|
|
36
|
+
continue
|
|
37
|
+
try:
|
|
38
|
+
lines.append(json.loads(line))
|
|
39
|
+
except ValueError:
|
|
40
|
+
continue
|
|
41
|
+
if not lines:
|
|
42
|
+
return None
|
|
43
|
+
tin = sum(l.get("tokens_in") or 0 for l in lines)
|
|
44
|
+
tout = sum(l.get("tokens_out") or 0 for l in lines)
|
|
45
|
+
ms = sum(l.get("ms") or 0 for l in lines)
|
|
46
|
+
by = {}
|
|
47
|
+
for l in lines:
|
|
48
|
+
for m in l.get("by_model") or []:
|
|
49
|
+
agg = by.setdefault(m.get("model", "unknown"), [0, 0])
|
|
50
|
+
agg[0] += m.get("tokens_in") or 0
|
|
51
|
+
agg[1] += m.get("tokens_out") or 0
|
|
52
|
+
by_model = [{"model": k, "tokens_in": v[0], "tokens_out": v[1]}
|
|
53
|
+
for k, v in sorted(by.items())]
|
|
54
|
+
effort = next((l.get("effort") for l in reversed(lines) if l.get("effort")), None)
|
|
55
|
+
model = (by_model[0]["model"] if len(by_model) == 1
|
|
56
|
+
else "+".join(m["model"] for m in by_model) if by_model else None)
|
|
57
|
+
return {"source": "Claude Code", "sessions": len(lines),
|
|
58
|
+
"tokens_in": tin, "tokens_out": tout, "ms": ms or None,
|
|
59
|
+
"model": model, "effort": effort, "by_model": by_model}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def main():
|
|
63
|
+
ap = argparse.ArgumentParser(description="render mirador.html from the ledger (+ optional vendor telemetry)")
|
|
64
|
+
ap.add_argument("--engine", required=True, help="path to qa_ledger.py")
|
|
65
|
+
ap.add_argument("--ledger", default="QA-LEDGER.json")
|
|
66
|
+
ap.add_argument("--template", required=True, help="path to mirador.template.html")
|
|
67
|
+
ap.add_argument("--out", default="mirador.html")
|
|
68
|
+
ap.add_argument("--sidecar", default=os.path.join(".uscha", "telemetry.jsonl"))
|
|
69
|
+
ap.add_argument("--refresh", type=int, default=0,
|
|
70
|
+
help="if >0, the page auto-reloads every N seconds (live second-screen view)")
|
|
71
|
+
args = ap.parse_args()
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
raw = subprocess.check_output(
|
|
75
|
+
[sys.executable, args.engine, "dashboard", "--ledger", args.ledger, "--json"],
|
|
76
|
+
text=True, encoding="utf-8")
|
|
77
|
+
except (subprocess.CalledProcessError, OSError) as e:
|
|
78
|
+
print(f"[mirador-render] dashboard failed: {e}", file=sys.stderr)
|
|
79
|
+
return 1
|
|
80
|
+
try:
|
|
81
|
+
data = json.loads(raw)
|
|
82
|
+
except ValueError as e:
|
|
83
|
+
print(f"[mirador-render] dashboard returned invalid JSON: {e}", file=sys.stderr)
|
|
84
|
+
return 1
|
|
85
|
+
|
|
86
|
+
tel = aggregate_telemetry(args.sidecar)
|
|
87
|
+
if tel:
|
|
88
|
+
data["telemetry"] = tel # vendor-reported, merged in the adapter -- never by the engine
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
tpl = open(args.template, encoding="utf-8").read()
|
|
92
|
+
except OSError as e:
|
|
93
|
+
print(f"[mirador-render] cannot read template {args.template}: {e}", file=sys.stderr)
|
|
94
|
+
return 1
|
|
95
|
+
payload = ("/*MIRADOR_DATA_START*/\nconst DATA = "
|
|
96
|
+
+ json.dumps(data, ensure_ascii=False) + ";\n/*MIRADOR_DATA_END*/")
|
|
97
|
+
out = re.sub(r"/\*MIRADOR_DATA_START\*/.*?/\*MIRADOR_DATA_END\*/",
|
|
98
|
+
lambda m: payload, tpl, count=1, flags=re.S)
|
|
99
|
+
if args.refresh and args.refresh > 0:
|
|
100
|
+
# opt-in live reload: a meta-refresh, injected only when watching
|
|
101
|
+
out = out.replace("</head>",
|
|
102
|
+
f'<meta http-equiv="refresh" content="{args.refresh}">\n</head>', 1)
|
|
103
|
+
with open(args.out, "w", encoding="utf-8") as f:
|
|
104
|
+
f.write(out)
|
|
105
|
+
score = (data.get("readiness") or {}).get("score")
|
|
106
|
+
print(f"[mirador-render] wrote {args.out} (readiness {score}, "
|
|
107
|
+
f"telemetry {'on' if tel else 'off'}, refresh {args.refresh or 'off'})")
|
|
108
|
+
return 0
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
sys.exit(main())
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# mirador-watch.ps1 -- live second-screen mirador (uscha-kit 1.34.0), Windows.
|
|
2
|
+
# Regenerates mirador.html every N seconds from the current ledger; the page is rendered
|
|
3
|
+
# with a meta-refresh at the same interval, so a browser open on it updates on its own.
|
|
4
|
+
#
|
|
5
|
+
# Usage (run in a spare terminal, from the project root):
|
|
6
|
+
# powershell -NoProfile -File <kit>\.claude\skills\uscha-mirador\mirador-watch.ps1 [-Interval 30]
|
|
7
|
+
# then open mirador.html in a browser on your second screen. Ctrl-C to stop.
|
|
8
|
+
#
|
|
9
|
+
# Overridable via env: ENGINE, LEDGER, TEMPLATE, OUT, PYTHON.
|
|
10
|
+
param([int]$Interval = 30)
|
|
11
|
+
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
12
|
+
$engine = if ($env:ENGINE) { $env:ENGINE } else { Join-Path $here "..\uscha-devloop\qa_ledger.py" }
|
|
13
|
+
$ledger = if ($env:LEDGER) { $env:LEDGER } else { "QA-LEDGER.json" }
|
|
14
|
+
$template = if ($env:TEMPLATE) { $env:TEMPLATE } else { Join-Path $here "mirador.template.html" }
|
|
15
|
+
$out = if ($env:OUT) { $env:OUT } else { "mirador.html" }
|
|
16
|
+
$py = if ($env:PYTHON) { $env:PYTHON } else { "python" }
|
|
17
|
+
Write-Host "mirador-watch: regenerating $out every ${Interval}s (Ctrl-C to stop). Open $out in a browser."
|
|
18
|
+
while ($true) {
|
|
19
|
+
& $py (Join-Path $here "mirador-render.py") --engine $engine --ledger $ledger --template $template --out $out --refresh $Interval
|
|
20
|
+
if ($LASTEXITCODE -ne 0) { Write-Host "mirador-watch: render failed (ledger missing? run uscha-devloop first) -- retrying" }
|
|
21
|
+
Start-Sleep -Seconds $Interval
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# mirador-watch.sh -- live second-screen mirador (uscha-kit 1.34.0).
|
|
3
|
+
# Regenerates mirador.html every N seconds from the current ledger; the page is rendered
|
|
4
|
+
# with a meta-refresh at the same interval, so a browser open on it updates on its own.
|
|
5
|
+
#
|
|
6
|
+
# Usage (run in a spare terminal, from the project root):
|
|
7
|
+
# bash <kit>/.claude/skills/uscha-mirador/mirador-watch.sh [interval_seconds]
|
|
8
|
+
# then open mirador.html in a browser on your second screen. Ctrl-C to stop.
|
|
9
|
+
#
|
|
10
|
+
# Overridable via env: ENGINE, LEDGER, TEMPLATE, OUT, PYTHON.
|
|
11
|
+
set -u
|
|
12
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
13
|
+
INTERVAL="${1:-30}"
|
|
14
|
+
ENGINE="${ENGINE:-$HERE/../uscha-devloop/qa_ledger.py}"
|
|
15
|
+
LEDGER="${LEDGER:-QA-LEDGER.json}"
|
|
16
|
+
TEMPLATE="${TEMPLATE:-$HERE/mirador.template.html}"
|
|
17
|
+
OUT="${OUT:-mirador.html}"
|
|
18
|
+
PY="${PYTHON:-python3}"
|
|
19
|
+
command -v "$PY" >/dev/null 2>&1 || PY=python
|
|
20
|
+
echo "mirador-watch: regenerating $OUT every ${INTERVAL}s (Ctrl-C to stop). Open $OUT in a browser."
|
|
21
|
+
while true; do
|
|
22
|
+
"$PY" "$HERE/mirador-render.py" --engine "$ENGINE" --ledger "$LEDGER" \
|
|
23
|
+
--template "$TEMPLATE" --out "$OUT" --refresh "$INTERVAL" || \
|
|
24
|
+
echo "mirador-watch: render failed (ledger missing? run uscha-devloop first) -- retrying"
|
|
25
|
+
sleep "$INTERVAL"
|
|
26
|
+
done
|