@andresmassello/uscha 1.51.1 → 1.53.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.
Files changed (98) hide show
  1. package/README.md +24 -5
  2. package/package.json +3 -2
  3. package/uscha-kit/.claude/skills/uscha-adr-refine/SKILL.md +203 -161
  4. package/uscha-kit/.claude/skills/uscha-characterize/SKILL.md +40 -0
  5. package/uscha-kit/.claude/skills/uscha-devloop/SKILL.md +40 -0
  6. package/uscha-kit/.claude/skills/uscha-discovery/SKILL.md +203 -161
  7. package/uscha-kit/.claude/skills/uscha-mirador/SKILL.md +192 -161
  8. package/uscha-kit/.claude/skills/uscha-mirador/mirador-render.py +16 -8
  9. package/uscha-kit/.claude/skills/uscha-reverse-discovery/SKILL.md +42 -0
  10. package/uscha-kit/.claude/skills/uscha-rubric/SKILL.md +119 -79
  11. package/uscha-kit/.claude/skills/uscha-status/SKILL.md +24 -0
  12. package/uscha-kit/.claude/skills/uscha-sysdoc/SKILL.md +128 -88
  13. package/uscha-kit/.claude-plugin/plugin.json +2 -2
  14. package/uscha-kit/.codex-plugin/plugin.json +2 -2
  15. package/uscha-kit/INSTALL.md +3 -0
  16. package/uscha-kit/README.md +1 -1
  17. package/uscha-kit/VERSION +1 -1
  18. package/uscha-kit/install-uscha.py +77 -43
  19. package/uscha-kit/skills/uscha-adr-refine/SKILL.md +203 -161
  20. package/uscha-kit/skills/uscha-characterize/SKILL.md +40 -0
  21. package/uscha-kit/skills/uscha-devloop/SKILL.md +40 -0
  22. package/uscha-kit/skills/uscha-discovery/SKILL.md +203 -161
  23. package/uscha-kit/skills/uscha-mirador/SKILL.md +192 -161
  24. package/uscha-kit/skills/uscha-mirador/mirador-render.py +16 -8
  25. package/uscha-kit/skills/uscha-reverse-discovery/SKILL.md +42 -0
  26. package/uscha-kit/skills/uscha-rubric/SKILL.md +119 -79
  27. package/uscha-kit/skills/uscha-status/SKILL.md +24 -0
  28. package/uscha-kit/skills/uscha-sysdoc/SKILL.md +128 -88
  29. package/uscha-kit/uscha.config.json +1 -1
  30. package/uscha-kit/CHANGELOG-1.10.0.md +0 -84
  31. package/uscha-kit/CHANGELOG-1.11.0.md +0 -67
  32. package/uscha-kit/CHANGELOG-1.12.0.md +0 -46
  33. package/uscha-kit/CHANGELOG-1.13.0.md +0 -33
  34. package/uscha-kit/CHANGELOG-1.14.0.md +0 -42
  35. package/uscha-kit/CHANGELOG-1.15.0.md +0 -58
  36. package/uscha-kit/CHANGELOG-1.16.0.md +0 -55
  37. package/uscha-kit/CHANGELOG-1.17.0.md +0 -44
  38. package/uscha-kit/CHANGELOG-1.18.0.md +0 -42
  39. package/uscha-kit/CHANGELOG-1.19.0.md +0 -41
  40. package/uscha-kit/CHANGELOG-1.2.2.md +0 -16
  41. package/uscha-kit/CHANGELOG-1.2.3.md +0 -20
  42. package/uscha-kit/CHANGELOG-1.2.4.md +0 -10
  43. package/uscha-kit/CHANGELOG-1.2.5.md +0 -23
  44. package/uscha-kit/CHANGELOG-1.2.6.md +0 -11
  45. package/uscha-kit/CHANGELOG-1.2.7.md +0 -15
  46. package/uscha-kit/CHANGELOG-1.2.8.md +0 -24
  47. package/uscha-kit/CHANGELOG-1.2.9.md +0 -4
  48. package/uscha-kit/CHANGELOG-1.20.0.md +0 -29
  49. package/uscha-kit/CHANGELOG-1.21.0.md +0 -33
  50. package/uscha-kit/CHANGELOG-1.22.0.md +0 -60
  51. package/uscha-kit/CHANGELOG-1.23.0.md +0 -75
  52. package/uscha-kit/CHANGELOG-1.24.0.md +0 -50
  53. package/uscha-kit/CHANGELOG-1.25.0.md +0 -55
  54. package/uscha-kit/CHANGELOG-1.26.0.md +0 -70
  55. package/uscha-kit/CHANGELOG-1.27.0.md +0 -45
  56. package/uscha-kit/CHANGELOG-1.28.0.md +0 -35
  57. package/uscha-kit/CHANGELOG-1.29.0.md +0 -20
  58. package/uscha-kit/CHANGELOG-1.3.0.md +0 -74
  59. package/uscha-kit/CHANGELOG-1.30.0.md +0 -46
  60. package/uscha-kit/CHANGELOG-1.31.0.md +0 -59
  61. package/uscha-kit/CHANGELOG-1.32.0.md +0 -50
  62. package/uscha-kit/CHANGELOG-1.33.0.md +0 -46
  63. package/uscha-kit/CHANGELOG-1.34.0.md +0 -55
  64. package/uscha-kit/CHANGELOG-1.35.0.md +0 -30
  65. package/uscha-kit/CHANGELOG-1.36.0.md +0 -33
  66. package/uscha-kit/CHANGELOG-1.37.0.md +0 -41
  67. package/uscha-kit/CHANGELOG-1.38.0.md +0 -11
  68. package/uscha-kit/CHANGELOG-1.39.0.md +0 -14
  69. package/uscha-kit/CHANGELOG-1.4.0.md +0 -68
  70. package/uscha-kit/CHANGELOG-1.40.0.md +0 -16
  71. package/uscha-kit/CHANGELOG-1.40.1.md +0 -11
  72. package/uscha-kit/CHANGELOG-1.40.2.md +0 -13
  73. package/uscha-kit/CHANGELOG-1.41.0.md +0 -18
  74. package/uscha-kit/CHANGELOG-1.41.1.md +0 -53
  75. package/uscha-kit/CHANGELOG-1.41.2.md +0 -34
  76. package/uscha-kit/CHANGELOG-1.41.3.md +0 -30
  77. package/uscha-kit/CHANGELOG-1.42.0.md +0 -41
  78. package/uscha-kit/CHANGELOG-1.43.0.md +0 -37
  79. package/uscha-kit/CHANGELOG-1.44.0.md +0 -90
  80. package/uscha-kit/CHANGELOG-1.44.1.md +0 -26
  81. package/uscha-kit/CHANGELOG-1.45.0.md +0 -58
  82. package/uscha-kit/CHANGELOG-1.46.0.md +0 -50
  83. package/uscha-kit/CHANGELOG-1.46.1.md +0 -35
  84. package/uscha-kit/CHANGELOG-1.47.0.md +0 -45
  85. package/uscha-kit/CHANGELOG-1.48.0.md +0 -35
  86. package/uscha-kit/CHANGELOG-1.48.1.md +0 -55
  87. package/uscha-kit/CHANGELOG-1.48.2.md +0 -47
  88. package/uscha-kit/CHANGELOG-1.49.0.md +0 -45
  89. package/uscha-kit/CHANGELOG-1.5.0.md +0 -64
  90. package/uscha-kit/CHANGELOG-1.50.0.md +0 -52
  91. package/uscha-kit/CHANGELOG-1.50.1.md +0 -52
  92. package/uscha-kit/CHANGELOG-1.50.2.md +0 -62
  93. package/uscha-kit/CHANGELOG-1.51.0.md +0 -44
  94. package/uscha-kit/CHANGELOG-1.51.1.md +0 -33
  95. package/uscha-kit/CHANGELOG-1.6.0.md +0 -57
  96. package/uscha-kit/CHANGELOG-1.7.0.md +0 -74
  97. package/uscha-kit/CHANGELOG-1.8.0.md +0 -46
  98. package/uscha-kit/CHANGELOG-1.9.0.md +0 -112
@@ -1,161 +1,192 @@
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, acceptance, 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`, writes the file, prints its
79
- absolute path (`OPEN IT: ...`), and opens it in the default browser. From the project root,
80
- with no long paths — `--engine` and `--template` default to the renderer's sibling skill
81
- files (kit 1.41.2):
82
- ```bash
83
- python3 <skill-dir>/mirador-render.py --ledger QA-LEDGER.json
84
- ```
85
- The engine stays model-agnostic — telemetry is merged by the renderer (the adapter), NOT
86
- by `dashboard`. If the ledger is missing, warn and stop (run `uscha-devloop` first). The
87
- time-lapse feeds from `qa_ledger.py readiness --record` — since kit 1.47.0 the dev-loop
88
- records at every pass close, so history accumulates without extra ceremony.
89
-
90
- 5. **Where to look:** the renderer already opened `mirador.html` and printed its absolute path
91
- on the `OPEN IT:` line — surface that path to the operator. Pass `--no-open` to write the
92
- file without opening a browser (headless/CI, or the watch loop below, which passes it).
93
-
94
- ## For a human at a terminal: `uscha mirador`
95
-
96
- The one-liner above is what THIS skill runs. A human who just wants the dashboard — without a
97
- Claude Code session — has a zero-friction verb instead (kit 1.43.0): from the project root,
98
-
99
- ```bash
100
- uscha mirador # render + open, defaults to the QA-LEDGER.json convention
101
- uscha mirador --watch # live second-screen view (auto-refresh, one self-reloading tab)
102
- npx @andresmassello/uscha mirador # same, no install
103
- ```
104
-
105
- No python, no paths: the verb resolves the engine, template and ledger on its own.
106
-
107
- ## Live second-screen view
108
-
109
- For a mirador that updates while you keep coding in the terminal, run the watch loop in a
110
- spare terminal and open `mirador.html` on a second monitor (or just use `uscha mirador --watch`):
111
-
112
- ```bash
113
- # Windows: powershell -NoProfile -File <skill-dir>\mirador-watch.ps1 -Interval 30
114
- # Unix: bash <skill-dir>/mirador-watch.sh 30
115
- ```
116
-
117
- It re-renders `mirador.html` every N seconds (default 30) from the current ledger, rendered
118
- with a matching `<meta http-equiv="refresh">` so the open page reloads on its own — **no
119
- server**. It is only as live as the **ledger**: the picture changes at the dev-loop's
120
- measured checkpoints (snapshots, gates, `readiness --record`), not per keystroke. Honest by
121
- design — it shows measured state, which changes at measured moments.
122
-
123
- ## Session telemetry — sidecar contract
124
-
125
- `.uscha/telemetry.jsonl` is an **append-only** file, one JSON object per session/run:
126
-
127
- ```jsonl
128
- {"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"}
129
- ```
130
-
131
- `effort` and `note` are **optional** and are NOT produced by `telemetry-extract.py` (a Claude
132
- Code transcript carries no reliable effort label) — add them by hand if you want them; the
133
- strip shows `—` when `effort` is absent. `by_model` is per-**tokens** (session-level `ms` only).
134
-
135
- - **Who writes it:** the AGENT / operator, NEVER the engine (the engine is model-agnostic
136
- and cannot see tokens). Two honest sources:
137
- - **`telemetry-extract.py <transcript.jsonl>`** (shipped in this skill folder): parses a
138
- Claude Code session transcript — each assistant turn carries a `usage` block
139
- (`input_tokens` / `cache_*_input_tokens` / `output_tokens`) and a `model` — sums per
140
- model, computes wall time from the timestamps, and appends one line (with a `by_model`
141
- breakdown). Real, vendor-native data. Best-effort: unknown/older schemas degrade, never
142
- crash; no usage found → nothing appended.
143
- - **Manual append**: paste the session's numbers from Claude Code's own cost view.
144
- - **Persistence:** the file itself IS the history (portable, diffable, greppable). Add it to
145
- the project's `.gitignore` — it is per-machine telemetry, not repo state. (A pure-client
146
- alternative is `localStorage` inside `mirador.html`, but the sidecar survives regeneration,
147
- so it is the default.)
148
- - **Boundary (non-negotiable):** this is the ONLY place vendor-narrated numbers enter the
149
- mirador. They are shown, never gated, never fed into readiness — telemetry answers "what
150
- did it cost", the measured panels answer "is it correct". Keep them apart.
151
-
152
- ## Rules
153
-
154
- - **Zero computation in the skill.** The numbers come from the ledger. If a panel looks
155
- empty, it's because the engine doesn't have that source yet (truth-pass), not because
156
- painting it was missed.
157
- - **Read-only.** The skill does not run gates or modify the ledger. `dashboard` is
158
- read-only; the only one that writes (opt-in) is `readiness --record`, and that is the
159
- loop's decision, not this skill's.
160
- - **`mirador.html` is a generated artifact** — suggest adding it to the project's
161
- `.gitignore`; it regenerates whenever you want.
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
+ ## Orientation markers (non-negotiable)
21
+
22
+ The operator must never have to ask "where am I?" or "what happens now?".
23
+
24
+ This skill is a **one-shot read-only readout**: its block IS the answer. It therefore does NOT
25
+ take the conversational close block — that would be exactly the padding this skill forbids.
26
+ It carries the two minimal markers instead.
27
+
28
+ **Open with a breadcrumb:**
29
+
30
+ `[uscha · mirador · step <n> → <target>]`
31
+
32
+ **End with the two routing lines, and nothing else:**
33
+
34
+ ```
35
+ Next: <the next action, derived from what this readout just showed>
36
+ Run: <the exact command or skill to invoke>
37
+ ```
38
+
39
+ `Next` is **derived** from the state you just read — never copied from a fixed route,
40
+ including any `Flow:` line in this file. If nothing is actionable, say that plainly rather
41
+ than inventing a step. Keep the CONTENT in the conversation's language and the labels
42
+ (`Next`, `Run`) verbatim — the smoke suite checks for them.
43
+
44
+ ## Contract
45
+
46
+ - **Source of truth:** `qa_ledger.py dashboard --json` — aggregates ONLY state the
47
+ ledger already has (readiness, subscores, phases, acceptance, adrs, inv, layers, loops,
48
+ snapshots, evidence). A field with no source comes out `null`/`[]`; the template
49
+ degrades. **A datum is never invented.**
50
+ - **Template:** `mirador.template.html` (in this folder). The data block lives between
51
+ `/*MIRADOR_DATA_START*/` and `/*MIRADOR_DATA_END*/`; the sample `const DATA` it ships
52
+ with is the **offline fallback**. The skill replaces ONLY that region; it does not
53
+ touch the rest of the HTML.
54
+ - **Project name (top):** the mirador shows the project name prominently at the top, from
55
+ `config.project` in `uscha.config.json` (set it in `uscha-discovery`); if unset, it falls
56
+ back to the joined repo names (truth-pass — no invented name).
57
+ - **Execution policy (bird's-eye):** `dashboard --json` also exposes
58
+ `execution_policy` plus `phase.execution` for each trail node. This is routing metadata
59
+ from `config.defaults.execution_policy` (`method`, `tier`, `model`, `effort`,
60
+ `uncorrelated`), not a score. The template renders it as a separate panel so the human
61
+ sees which methodology/model/effort is selected per phase without mixing it into
62
+ readiness.
63
+ - **Discovery intake:** `dashboard --json` carries `discovery_intake` from readiness:
64
+ open `production_findings`, `spec_doubts`, and `spec_change_requests`. The current template keeps the top-level
65
+ verdict honest through readiness/caps; future panels may render the intake directly.
66
+ - **ADR experiments:** ADR rows may carry `adr_status: "experiment"`, `review_by`,
67
+ `review_trigger`, `experiment_valid`, `experiment_missing`, and `expired`; top-level
68
+ `adr_experiments` summarizes open/malformed/expired experiments. This is advisory
69
+ visibility for measured hypotheses, not readiness scoring.
70
+ - **Session telemetry (optional, vendor-reported):** if `.uscha/telemetry.jsonl` exists,
71
+ the skill aggregates it and MERGES a `telemetry` object into `DATA`. This is the ONE
72
+ panel that is **narrated by the vendor (Claude Code), not measured by the engine** —
73
+ tokens, wall time, model, effort — so it renders in a **segregated strip** labeled as
74
+ such, never mixed with the measured panels. `dashboard --json` NEVER emits it; it enters
75
+ only through this adapter. Absent file → no strip (degrades). This is the doctrinal
76
+ line: telemetry is neither a fact-gate nor a guess-advisor, so it must not dilute
77
+ *measured beats narrated*.
78
+
79
+ ## Flow
80
+
81
+ 1. **Resolve the engine.** Use the first one that exists:
82
+ - `./.claude/skills/uscha-devloop/qa_ledger.py` (per-project install)
83
+ - `~/plugins/uscha/skills/uscha-devloop/qa_ledger.py` (Codex plugin install)
84
+ - `~/.codex/skills/uscha-devloop/qa_ledger.py` (Codex raw-skills install)
85
+ - `~/.claude/skills/uscha-devloop/qa_ledger.py` (Claude global install)
86
+
87
+ 2. **Resolve the ledger.** `QA-LEDGER.json` in the cwd (or the `--ledger` the user
88
+ points to). If it does not exist, warn: you have to run `uscha-devloop` (or
89
+ `qa_ledger.py init`) first — with no ledger there is no state to look at.
90
+
91
+ (`<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.)
92
+
93
+ 3. **(Optional) record this session's telemetry.** For the vendor-telemetry strip, append
94
+ the current Claude Code session to the sidecar:
95
+ ```bash
96
+ python3 <skill-dir>/telemetry-extract.py <path-to-CC-transcript.jsonl>
97
+ ```
98
+ It **upserts by session** (safe to re-run — a watch loop won't inflate the totals). Skip
99
+ this for a pure measured view.
100
+
101
+ 4. **Render `mirador.html`** with the standalone renderer — it runs `dashboard --json`,
102
+ merges the sidecar telemetry if present, injects `const DATA`, writes the file, prints its
103
+ absolute path (`OPEN IT: ...`), and opens it in the default browser **the first time it
104
+ creates the file** (see step 5 — a re-render updates the open tab instead). From the project root,
105
+ with no long paths — `--engine` and `--template` default to the renderer's sibling skill
106
+ files (kit 1.41.2):
107
+ ```bash
108
+ python3 <skill-dir>/mirador-render.py --ledger QA-LEDGER.json
109
+ ```
110
+ The engine stays model-agnostic — telemetry is merged by the renderer (the adapter), NOT
111
+ by `dashboard`. If the ledger is missing, warn and stop (run `uscha-devloop` first). The
112
+ time-lapse feeds from `qa_ledger.py readiness --record` — since kit 1.47.0 the dev-loop
113
+ records at every pass close, so history accumulates without extra ceremony.
114
+
115
+ 5. **Where to look:** the renderer opens `mirador.html` **once — only the first time it is
116
+ created** — and always prints its absolute path on the `OPEN IT:` line; surface that path to
117
+ the operator. Re-rendering (this skill invoked again on a later pass, or the watch loop)
118
+ rewrites the SAME file, and the page's built-in auto-refresh reloads the already-open tab in
119
+ place, so a re-render never spawns a new browser tab. On a fresh session the file is already
120
+ on disk, so nothing pops — open the printed path once, or pass `--open` (`uscha mirador
121
+ --open`) to force a reopen when you closed the tab. `--no-open` suppresses opening
122
+ everywhere and wins over `--open` (headless/CI, or the watch loop, which passes it);
123
+ `--refresh 0` writes a frozen snapshot with no auto-reload.
124
+
125
+ ## For a human at a terminal: `uscha mirador`
126
+
127
+ The one-liner above is what THIS skill runs. A human who just wants the dashboard — without a
128
+ Claude Code session — has a zero-friction verb instead (kit 1.43.0): from the project root,
129
+
130
+ ```bash
131
+ uscha mirador # render + open, defaults to the QA-LEDGER.json convention
132
+ uscha mirador --watch # live second-screen view (auto-refresh, one self-reloading tab)
133
+ npx @andresmassello/uscha mirador # same, no install
134
+ ```
135
+
136
+ No python, no paths: the verb resolves the engine, template and ledger on its own.
137
+
138
+ ## Live second-screen view
139
+
140
+ For a mirador that updates while you keep coding in the terminal, run the watch loop in a
141
+ spare terminal and open `mirador.html` on a second monitor (or just use `uscha mirador --watch`):
142
+
143
+ ```bash
144
+ # Windows: powershell -NoProfile -File <skill-dir>\mirador-watch.ps1 -Interval 30
145
+ # Unix: bash <skill-dir>/mirador-watch.sh 30
146
+ ```
147
+
148
+ It re-renders `mirador.html` every N seconds (default 30) from the current ledger, rendered
149
+ with a matching `<meta http-equiv="refresh">` so the open page reloads on its own — **no
150
+ server**. It is only as live as the **ledger**: the picture changes at the dev-loop's
151
+ measured checkpoints (snapshots, gates, `readiness --record`), not per keystroke. Honest by
152
+ design — it shows measured state, which changes at measured moments.
153
+
154
+ ## Session telemetry — sidecar contract
155
+
156
+ `.uscha/telemetry.jsonl` is an **append-only** file, one JSON object per session/run:
157
+
158
+ ```jsonl
159
+ {"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"}
160
+ ```
161
+
162
+ `effort` and `note` are **optional** and are NOT produced by `telemetry-extract.py` (a Claude
163
+ Code transcript carries no reliable effort label) — add them by hand if you want them; the
164
+ strip shows `—` when `effort` is absent. `by_model` is per-**tokens** (session-level `ms` only).
165
+
166
+ - **Who writes it:** the AGENT / operator, NEVER the engine (the engine is model-agnostic
167
+ and cannot see tokens). Two honest sources:
168
+ - **`telemetry-extract.py <transcript.jsonl>`** (shipped in this skill folder): parses a
169
+ Claude Code session transcript — each assistant turn carries a `usage` block
170
+ (`input_tokens` / `cache_*_input_tokens` / `output_tokens`) and a `model` — sums per
171
+ model, computes wall time from the timestamps, and appends one line (with a `by_model`
172
+ breakdown). Real, vendor-native data. Best-effort: unknown/older schemas degrade, never
173
+ crash; no usage found → nothing appended.
174
+ - **Manual append**: paste the session's numbers from Claude Code's own cost view.
175
+ - **Persistence:** the file itself IS the history (portable, diffable, greppable). Add it to
176
+ the project's `.gitignore` — it is per-machine telemetry, not repo state. (A pure-client
177
+ alternative is `localStorage` inside `mirador.html`, but the sidecar survives regeneration,
178
+ so it is the default.)
179
+ - **Boundary (non-negotiable):** this is the ONLY place vendor-narrated numbers enter the
180
+ mirador. They are shown, never gated, never fed into readiness — telemetry answers "what
181
+ did it cost", the measured panels answer "is it correct". Keep them apart.
182
+
183
+ ## Rules
184
+
185
+ - **Zero computation in the skill.** The numbers come from the ledger. If a panel looks
186
+ empty, it's because the engine doesn't have that source yet (truth-pass), not because
187
+ painting it was missed.
188
+ - **Read-only.** The skill does not run gates or modify the ledger. `dashboard` is
189
+ read-only; the only one that writes (opt-in) is `readiness --record`, and that is the
190
+ loop's decision, not this skill's.
191
+ - **`mirador.html` is a generated artifact** — suggest adding it to the project's
192
+ `.gitignore`; it regenerates whenever you want.
@@ -94,10 +94,13 @@ def main():
94
94
  help="path to mirador.template.html (default: the sibling template)")
95
95
  ap.add_argument("--out", default="mirador.html")
96
96
  ap.add_argument("--sidecar", default=os.path.join(".uscha", "telemetry.jsonl"))
97
- ap.add_argument("--refresh", type=int, default=0,
98
- help="if >0, the page auto-reloads every N seconds (live second-screen view)")
97
+ ap.add_argument("--refresh", type=int, default=10,
98
+ help="auto-reload the open tab every N seconds so a SINGLE tab stays live "
99
+ "(0 = frozen snapshot; default 10)")
99
100
  ap.add_argument("--no-open", action="store_true",
100
101
  help="write the file but do not open it in a browser")
102
+ ap.add_argument("--open", dest="force_open", action="store_true",
103
+ help="open the browser even if the file already existed (the tab was closed)")
101
104
  args = ap.parse_args()
102
105
 
103
106
  try:
@@ -127,10 +130,12 @@ def main():
127
130
  out = re.sub(r"/\*MIRADOR_DATA_START\*/.*?/\*MIRADOR_DATA_END\*/",
128
131
  lambda m: payload, tpl, count=1, flags=re.S)
129
132
  if args.refresh and args.refresh > 0:
130
- # opt-in live reload: a meta-refresh, injected only when watching
133
+ # a meta-refresh so the ONE open tab reloads itself in place (on by default) --
134
+ # pass --refresh 0 for a frozen snapshot
131
135
  out = out.replace("</head>",
132
136
  f'<meta http-equiv="refresh" content="{args.refresh}">\n</head>', 1)
133
- live = bool(args.refresh and args.refresh > 0)
137
+ # capture BEFORE writing: auto-open fires only when the mirador is FIRST materialized
138
+ pre_existed = os.path.exists(args.out)
134
139
  with open(args.out, "w", encoding="utf-8") as f:
135
140
  f.write(out)
136
141
  score = (data.get("readiness") or {}).get("score")
@@ -139,10 +144,13 @@ def main():
139
144
  print(f"[mirador-render] readiness {score} | telemetry {'on' if tel else 'off'} | "
140
145
  f"refresh {args.refresh or 'off'}")
141
146
  print(f"[mirador-render] OPEN IT: {out_abs}")
142
- # Auto-open only a ONE-SHOT view. In live mode (--refresh) the page carries its own
143
- # meta-refresh and reloads in the SAME tab, so opening on every render cycle would
144
- # spawn a new browser tab each time (kit 1.41.3). Open it once by hand and let it live.
145
- if not args.no_open and not live:
147
+ # Auto-open ONLY the first time the file is created. Repeated renders (the agent per
148
+ # pass, or a watch loop) rewrite the SAME file, and the meta-refresh reloads the already-
149
+ # open tab in place -- so a re-render never spawns a new browser tab (kit 1.51.2; 1.41.3
150
+ # gated this on --refresh, which spammed a tab per one-shot re-render). `pre_existed`
151
+ # tracks the FILE, not a live tab, so --open forces a reopen when the tab was closed;
152
+ # --no-open suppresses everywhere and wins over it. The OPEN IT path is always printed.
153
+ if not args.no_open and (args.force_open or not pre_existed):
146
154
  _open_best_effort(out_abs)
147
155
  return 0
148
156
 
@@ -18,6 +18,46 @@ disable-model-invocation: false
18
18
  opposite. The system already runs; its observable behavior is the ground truth. **You do
19
19
  not invent anything — you characterize what is already there, as facts.**
20
20
 
21
+ ## Orientation markers (non-negotiable)
22
+
23
+ The operator must never have to ask "where am I?" or "what happens now?". Two markers, always.
24
+ They are navigation, not ceremony: one line per turn, one block at the end.
25
+
26
+ **Open every turn with a breadcrumb**, then the content:
27
+
28
+ `[uscha · reverse-discovery · <step> → <target>]`
29
+
30
+ - `<step>` — `Q<n>` for a question, `pass <n>` for a loop iteration, `step <n>` otherwise.
31
+ Count what has actually happened. **Never write a denominator** (`Q4/12`): this phase
32
+ converges, its length is not known in advance, and an invented total is exactly the kind of
33
+ narrated number the method forbids. **When the ledger already measures the count** (the QA
34
+ loop's `loop_count`), use the measured number — never keep a parallel tally of your own.
35
+ - `<target>` — the artifact this turn feeds (`SPEC`, `ADR-003`, `ACCEPTANCE`, `LEDGER`,
36
+ `RECEIVED`, ...). Drop `→ <target>` only when the turn genuinely feeds none.
37
+
38
+ **Close with the close block ONCE, when the skill finishes** — not on every turn. Ending
39
+ without it is a defect, even when the phase converged cleanly:
40
+
41
+ ```
42
+ [uscha · reverse-discovery · CLOSED]
43
+ Produced: <files actually written, or "nothing">
44
+ Blocks: <what stands between here and the next phase, or "nothing">
45
+ Next: <the next action, and why it is that one>
46
+ Run: <the exact command or skill to invoke>
47
+ ```
48
+
49
+ This is **not** the implementation handoff some skills also emit: that one is a prompt for
50
+ whoever implements next, this one is navigation for the human operator, and both can appear.
51
+
52
+ `Blocks` and `Next` are **derived from the state you just produced** — never copied from a
53
+ fixed route, **including any `Flow:` line in this file**. Those lines are the nominal path;
54
+ open ADR experiments, an unclosed spike, an unapproved golden or a red gate all change what
55
+ genuinely comes next, and the derived answer wins. If the next phase cannot start yet, name it
56
+ and say exactly what unblocks it.
57
+
58
+ Keep the CONTENT in the conversation's language, but keep the labels (`CLOSED`, `Produced`,
59
+ `Blocks`, `Next`, `Run`) verbatim — they are the method's vocabulary and the smoke checks them.
60
+
21
61
  ## The one non-negotiable: produce ONLY facts
22
62
 
23
63
  A system map (from static analysis) and a golden suite (byte-captured) are FACTS —
@@ -94,6 +134,8 @@ Flow (migration): `uscha-reverse-discovery` (facts) → human writes SPEC + `/us
94
134
  module decisions) → `/uscha-devloop` (restructure; `golden-diff` + `ApplicationModules.verify()`
95
135
  stay green the whole way) → readiness + human gate.
96
136
 
137
+ That route is the **nominal** one, not the answer: the `Next:`/`Run:` you emit in the close block are DERIVED from the state you actually produced, and override it whenever an open experiment, an unclosed spike, an unapproved golden or a red gate stands in between.
138
+
97
139
  ## Relationship to the other skills
98
140
 
99
141
  - **discovery** (greenfield): you PROPOSE the shape from an idea. **reverse-discovery**