@andresmassello/uscha 2.1.0 → 2.2.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 +21 -4
- package/package.json +1 -1
- package/uscha-kit/.claude/skills/uscha-adr-refine/SKILL.md +2 -0
- package/uscha-kit/.claude/skills/uscha-characterize/SKILL.md +2 -0
- package/uscha-kit/.claude/skills/uscha-devloop/SKILL.md +141 -8
- package/uscha-kit/.claude/skills/uscha-devloop/qa_ledger.py +1946 -74
- package/uscha-kit/.claude/skills/uscha-discovery/SKILL.md +58 -4
- package/uscha-kit/.claude/skills/uscha-mirador/SKILL.md +2 -0
- package/uscha-kit/.claude/skills/uscha-reverse-discovery/SKILL.md +2 -0
- package/uscha-kit/.claude/skills/uscha-rubric/SKILL.md +2 -0
- package/uscha-kit/.claude/skills/uscha-status/SKILL.md +40 -0
- package/uscha-kit/.claude/skills/uscha-sysdoc/SKILL.md +2 -0
- package/uscha-kit/.claude-plugin/plugin.json +2 -2
- package/uscha-kit/.codex-plugin/plugin.json +1 -1
- package/uscha-kit/README.md +229 -5
- package/uscha-kit/VERSION +1 -1
- package/uscha-kit/install-uscha.py +18 -2
- package/uscha-kit/skills/uscha-adr-refine/SKILL.md +2 -0
- package/uscha-kit/skills/uscha-characterize/SKILL.md +2 -0
- package/uscha-kit/skills/uscha-devloop/SKILL.md +141 -8
- package/uscha-kit/skills/uscha-devloop/qa_ledger.py +1946 -74
- package/uscha-kit/skills/uscha-discovery/SKILL.md +58 -4
- package/uscha-kit/skills/uscha-mirador/SKILL.md +2 -0
- package/uscha-kit/skills/uscha-reverse-discovery/SKILL.md +2 -0
- package/uscha-kit/skills/uscha-rubric/SKILL.md +2 -0
- package/uscha-kit/skills/uscha-status/SKILL.md +40 -0
- package/uscha-kit/skills/uscha-sysdoc/SKILL.md +2 -0
- package/uscha-kit/templates/CLAUDE.md +16 -0
- package/uscha-kit/templates/CONSTITUTION.md +45 -0
- package/uscha-kit/templates/docs/adr/README.md +15 -0
- package/uscha-kit/templates/scripts/smoke-report-example.json +24 -0
- package/uscha-kit/uscha.config.json +6 -1
package/README.md
CHANGED
|
@@ -10,6 +10,10 @@ never what was claimed.
|
|
|
10
10
|
**[uscha.dev](https://uscha.dev)** — the method, the five rules, the skills, the library
|
|
11
11
|
(the diamond thesis, how-it-works diagrams, essay, 2-day dev course, reference, paper).
|
|
12
12
|
|
|
13
|
+
**New here? Start with [First use: from install to one measured result](docs/FIRST-USE-EN.md)**
|
|
14
|
+
— one machine, one small repo, one criterion closed by a real test, and the merge left to you
|
|
15
|
+
([en español](docs/FIRST-USE.md)).
|
|
16
|
+
|
|
13
17
|
```bash
|
|
14
18
|
npx --yes @andresmassello/uscha@latest install --target claude # Claude Code
|
|
15
19
|
npx --yes @andresmassello/uscha@latest install --target codex # Codex
|
|
@@ -40,7 +44,7 @@ Requires **Python 3.8+** on the machine (the engine is Python stdlib — no pip
|
|
|
40
44
|
runtime dependencies). The npm package is a thin router; the canonical installer is
|
|
41
45
|
`uscha-kit/install-uscha.py`.
|
|
42
46
|
|
|
43
|
-
**Kit v2.
|
|
47
|
+
**Kit v2.2.0** <!-- uscha:version --> · [uscha.dev](https://uscha.dev) ·
|
|
44
48
|
[changelog](https://github.com/andresmassello/uscha/blob/main/uscha-kit/CHANGELOG.md)
|
|
45
49
|
(the per-release changelogs live in the repo, not in the npm tarball)
|
|
46
50
|
|
|
@@ -76,7 +80,7 @@ and see which file, which test, and when.
|
|
|
76
80
|
| `/uscha-mirador` | Bird's-eye HTML dashboard: readiness, trail, acceptance, loops |
|
|
77
81
|
| `/uscha-status` | One-line progress readout, in chat |
|
|
78
82
|
|
|
79
|
-
**A measurement engine** (`qa_ledger.py`,
|
|
83
|
+
**A measurement engine** (`qa_ledger.py`, 56 subcommands, Python stdlib) that ingests
|
|
80
84
|
evidence from **11 language stacks** — maven, gradle, ant, python, node, go, rust, dotnet,
|
|
81
85
|
cpp, swift, flutter — and computes a readiness score with hard caps and visible provenance.
|
|
82
86
|
|
|
@@ -132,6 +136,14 @@ else `doctor` reports `advisory` rather than implying a guard it cannot see.
|
|
|
132
136
|
method is enforced by the engine, not by asking the agent to be disciplined.
|
|
133
137
|
- **Model-agnostic.** The engine never reads tokens, model names or vendor telemetry. Any
|
|
134
138
|
model-reported number enters through an adapter, never the engine.
|
|
139
|
+
- **Field truth for greenfield.** In a new system every test payload was invented by the
|
|
140
|
+
agent that wrote the code, so `corpus-run` scores a corpus of REAL inputs against the
|
|
141
|
+
real command and persists the percentage as a fact — advisory until the project
|
|
142
|
+
declares a budget, a blocking gate once it does (ADR-046).
|
|
143
|
+
- **Evidence is executed, not narrated.** The smoke run is a report the project's tool
|
|
144
|
+
writes, not a paragraph an agent types: `smoke-ingest` reads `{"checks": [{"name",
|
|
145
|
+
"ok", ...}]}`, a failed check blocks like any fact gate, and an empty or malformed
|
|
146
|
+
report is refused rather than scored (ADR-047).
|
|
135
147
|
|
|
136
148
|
## The diamond — specs are the source code, end to end
|
|
137
149
|
|
|
@@ -172,7 +184,7 @@ automatic tool can perform: a human verdict.
|
|
|
172
184
|
from the compiled code: 0.815 measured (12 archetypes) — names AND behaviour
|
|
173
185
|
```
|
|
174
186
|
|
|
175
|
-
**What each arrow is, in the engine (kit 1.96.0,
|
|
187
|
+
**What each arrow is, in the engine (kit 1.96.0, 56 subcommands, all measured):**
|
|
176
188
|
|
|
177
189
|
| Leg | Subcommands | What it establishes |
|
|
178
190
|
|---|---|---|
|
|
@@ -188,7 +200,12 @@ under an oracle the compilers never saw — that is the closed loop working. It
|
|
|
188
200
|
1.99.0, when a fourth compiler from a second vendor read one genuinely ambiguous sentence in
|
|
189
201
|
`transformer` the other way and lost a case the three Claude-family models had agreed on
|
|
190
202
|
(ADR-042). The number went down because the bench got harder, and the whole point of the arm was
|
|
191
|
-
to let it. 0
|
|
203
|
+
to let it. Since 2.2.0 that headline is a **derived fact**: `qa_ledger.py facts` counts the verdict
|
|
204
|
+
rows of `DIAMOND-BENCH.md` — the report `bench` generates over the committed fixture — into
|
|
205
|
+
`SYSTEM-FACTS.json`, and `facts --check` compares every published copy of it against that count.
|
|
206
|
+
It was added because the project's own homepage said 9/12 for nine releases after the number
|
|
207
|
+
moved, and every one of those releases was green: the claim sat in one HTML element and its noun
|
|
208
|
+
in the next, where the gate could not see it. 0.815 is the mean *recoverability* of the asset from compiled code counting only
|
|
192
209
|
static and behavioural footing. It read **0.062** until 1.90.0, with the behaviour dimension
|
|
193
210
|
`UNMEASURED`, because no oracle case carried an AC tag — a named absence, not a zero. The 12
|
|
194
211
|
bench oracles are now curated per case (`ORACLE-TAGS-CURATED.json`, human-authored; payloads and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andresmassello/uscha",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Spec-driven development for LLM coding agents: 9 skills + a stdlib evidence engine. Facts block, guesses advise; the human approves.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Andres Massello",
|
|
@@ -19,6 +19,8 @@ phases. **You are NOT a generator. You are an interrogator that distills.** The
|
|
|
19
19
|
is in the questions, not in agreeing.
|
|
20
20
|
|
|
21
21
|
<!-- uscha:orientation-block:begin -->
|
|
22
|
+
<!-- uscha kit: 2.2.0 -- generated region: edit tools/skill-blocks/, then run `python tools/gen-skill-blocks.py` (never this block by hand) -->
|
|
23
|
+
|
|
22
24
|
## First contact (show ONCE, then never again)
|
|
23
25
|
|
|
24
26
|
**Only when this project has no uscha artifacts yet** -- no `QA-LEDGER.json`, no `SPEC.md` or
|
|
@@ -19,6 +19,8 @@ what the code DOES, mechanically, by running it — never what it should do.** Y
|
|
|
19
19
|
the capture harness; you may NOT create, rename, or edit any `.approved` file.
|
|
20
20
|
|
|
21
21
|
<!-- uscha:orientation-block:begin -->
|
|
22
|
+
<!-- uscha kit: 2.2.0 -- generated region: edit tools/skill-blocks/, then run `python tools/gen-skill-blocks.py` (never this block by hand) -->
|
|
23
|
+
|
|
22
24
|
## First contact (show ONCE, then never again)
|
|
23
25
|
|
|
24
26
|
**Only when this project has no uscha artifacts yet** -- no `QA-LEDGER.json`, no `SPEC.md` or
|
|
@@ -27,6 +27,8 @@ artifacts; these can block) and **self-reported** agent counts (log-step — nar
|
|
|
27
27
|
recorded for the retrospective; a measured red always overrides a narrated green).
|
|
28
28
|
|
|
29
29
|
<!-- uscha:orientation-block:begin -->
|
|
30
|
+
<!-- uscha kit: 2.2.0 -- generated region: edit tools/skill-blocks/, then run `python tools/gen-skill-blocks.py` (never this block by hand) -->
|
|
31
|
+
|
|
30
32
|
## First contact (show ONCE, then never again)
|
|
31
33
|
|
|
32
34
|
**Only when this project has no uscha artifacts yet** -- no `QA-LEDGER.json`, no `SPEC.md` or
|
|
@@ -110,10 +112,30 @@ Keep the CONTENT in the conversation's language, but keep the labels (`CLOSED`,
|
|
|
110
112
|
only *run* the existing suite.
|
|
111
113
|
4. **Stop at merge.** You create the PR and confirm CI is green. You do NOT merge.
|
|
112
114
|
The human owns the merge gate.
|
|
113
|
-
5. **Tracked-markdown protocol.** Before modifying any
|
|
114
|
-
(CLAUDE.md, plan/delta docs, docs/adr),
|
|
115
|
-
Those files carry real progress (checkboxes, notes)
|
|
116
|
-
|
|
115
|
+
5. **Tracked-markdown protocol: READ the file, never ask for it.** Before modifying any
|
|
116
|
+
tracked `.md` (CLAUDE.md, plan/delta docs, docs/adr), READ the current version out of
|
|
117
|
+
the tree. Those files carry real progress (checkboxes, notes): preserve it and edit in
|
|
118
|
+
place, never regenerate from scratch. The current version of a tracked file is
|
|
119
|
+
INFORMATION the tree already holds, so asking the human for it is noise — you ask the
|
|
120
|
+
human for DECISIONS, never for information you can read (ADR-044).
|
|
121
|
+
6. **`origin: agent` — a decision the human never made does not enter scope silently.**
|
|
122
|
+
Every acceptance criterion, ADR decision item or HANDOFF rule that YOU introduce
|
|
123
|
+
(rather than one the human answered) carries the marker on its own line:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
- [ ] AC-12 — when X then Y. (origin: agent)
|
|
127
|
+
- [ ] AC-12 — when X then Y. (origin: agent, confirmed: 2026-09-07)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Unmarked items are human-origin by default; nothing existing is retro-tagged. An
|
|
131
|
+
unconfirmed item is **not in scope**: do not implement it, do not gate on it, do not
|
|
132
|
+
quote it as agreed. Ask the human to confirm it ONE BY ONE — a package-level "ok" over
|
|
133
|
+
a twenty-item summary confirms nothing — and only then write
|
|
134
|
+
`confirmed: YYYY-MM-DD` on the same line. List every still-unconfirmed item in the PR
|
|
135
|
+
body under **Agent-origin, unconfirmed**. `spec-check` names them with file:line and
|
|
136
|
+
`readiness` counts them on their own line; both are ADVISORY and neither changes an
|
|
137
|
+
exit code or the score.
|
|
138
|
+
7. **The golden is the one artifact you cannot author.** For migration/legacy work,
|
|
117
139
|
`.approved` fixtures are field truth captured from the ORIGINAL code and approved by
|
|
118
140
|
a HUMAN. You emit `.received` and stop; you never write, edit or rename `.approved`
|
|
119
141
|
(a `PreToolUse` hook denies the write — INV-GOLDEN-01).
|
|
@@ -126,6 +148,12 @@ QL="./.claude/skills/uscha-devloop/qa_ledger.py" # instalac
|
|
|
126
148
|
[ -f "$QL" ] || QL="$HOME/plugins/uscha/skills/uscha-devloop/qa_ledger.py" # Codex plugin install
|
|
127
149
|
[ -f "$QL" ] || QL="$HOME/.claude/skills/uscha-devloop/qa_ledger.py" # Claude global install
|
|
128
150
|
python3 $QL init --config uscha.config.json
|
|
151
|
+
|
|
152
|
+
# adding a service LATER never re-runs init: that would build a NEW ledger and drop every
|
|
153
|
+
# step. --add-repo appends and re-seals the checksum, leaving every existing repo untouched
|
|
154
|
+
# (kit 2.2.0). The new repo has no evidence yet, so it reads UNMEASURED and the repo
|
|
155
|
+
# averages read lower until its first snapshot — that is absence, not a regression:
|
|
156
|
+
python3 $QL init --add-repo <NAME> --path <path> --type <type> --test-command "<cmd>"
|
|
129
157
|
```
|
|
130
158
|
|
|
131
159
|
## Execution policy line (before every phase)
|
|
@@ -368,7 +396,9 @@ python3 $QL log-step --repo <REPO> --tool <code-review|judgment-day|improve> \
|
|
|
368
396
|
weaken the measuring apparatus? did it ADD a secret? — private keys, cloud tokens and
|
|
369
397
|
key-container files block as facts, kit 1.12.0; generic password/token literals advise;
|
|
370
398
|
new dependencies advise too — the change-budget rule "no new deps without approval" made
|
|
371
|
-
visible, kit 1.30.0)
|
|
399
|
+
visible, kit 1.30.0). A rename is reported as a MOVE, never as a deleted test, and `--repo`
|
|
400
|
+
SCOPES the diff to that repo's path, so a monorepo sibling's hunks are neither this repo's
|
|
401
|
+
findings nor this repo's exit code (kit 2.2.0)
|
|
372
402
|
and, for migration work, golden-diff (does behavior still match the human-approved
|
|
373
403
|
baseline?). Then PERSIST each verdict with `log-gate` — a failing fact gate blocks
|
|
374
404
|
convergence and caps readiness ≤65 through the ledger:
|
|
@@ -397,6 +427,10 @@ have caught this and what was done>"` — reflection is part of closing, not opt
|
|
|
397
427
|
python3 $QL gate-check --from-git --base <base> --repo <REPO> # exit 1 = BLOCKER
|
|
398
428
|
python3 $QL log-gate --repo <REPO> --iteration <N> --kind gate-check --verdict <pass|fail>
|
|
399
429
|
|
|
430
|
+
# CI, when the pipeline has run for this commit (kit 2.2.0). A green run is a FACT and
|
|
431
|
+
# belongs in the ledger; --ref is the receipt that outlives the conversation:
|
|
432
|
+
python3 $QL log-gate --repo <REPO> --iteration <N> --kind ci --verdict <pass|fail|not-run> --ref <run URL or id>
|
|
433
|
+
|
|
400
434
|
python3 $QL golden-diff [--dir <fixtures-root>] # exit 0 CLEAN · 1 DIVERGE · 2 NOT-RUN
|
|
401
435
|
python3 $QL log-gate --repo <REPO> --iteration <N> --kind golden-diff \
|
|
402
436
|
--verdict <pass|fail|not-run> # not-run records the absence — it is never green
|
|
@@ -406,6 +440,33 @@ python3 $QL log-gate --repo <REPO> --iteration <N> --kind golden-diff \
|
|
|
406
440
|
but when a PIT report EXISTS and fails the gate, persist it the same way:
|
|
407
441
|
`log-gate --kind pit-check --verdict fail`.)
|
|
408
442
|
|
|
443
|
+
**Field truth — the greenfield evidence class (kit 2.2.0, ADR-046).** `characterize` and
|
|
444
|
+
`golden-diff` answer "does it still do what the OLD code did?", which greenfield has no way to
|
|
445
|
+
ask: there is no old code, and every test payload was invented by the agent that wrote the
|
|
446
|
+
subject. `corpus-run` is the evidence class for that gap — REAL inputs with their real expected
|
|
447
|
+
outputs, one JSON object per line (`input`, `expected`, optional `id`), each fed to the command
|
|
448
|
+
on stdin and compared against its trimmed stdout. Run it when the repo declares a corpus
|
|
449
|
+
(`repos[R].corpus`) or when the human hands you one; it is NOT part of the inner loop, since a
|
|
450
|
+
real corpus can be large (schedule it like pit-check). It is **advisory** until the project
|
|
451
|
+
declares a budget — with no `--threshold`, `repos[R].corpus_threshold` or
|
|
452
|
+
`defaults.corpus_threshold`, the percentage is measured and gates nothing, and it never counts as
|
|
453
|
+
an `ok` gate. With one declared, a run under it caps readiness ≤65 and blocks convergence like
|
|
454
|
+
any fact gate. A missing, empty or malformed corpus is exit 2 naming the line — never a scored
|
|
455
|
+
0 %. Never author a corpus yourself: an invented corpus is the invented input this instrument
|
|
456
|
+
exists to expose.
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
python3 $QL corpus-run --repo <REPO> --corpus <path.jsonl> --command "<cmd>" [--threshold <P>] [--ac AC-FIELD-01] [--timeout 30] # exit 1 = under the declared budget
|
|
460
|
+
# a corpus measured elsewhere (CI, a nightly) goes in through the parity door:
|
|
461
|
+
python3 $QL log-gate --repo <REPO> --iteration <N> --kind corpus --verdict <pass|fail|advisory|not-run>
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
`--ac` stamps criterion ids on the record: a criterion whose only evidence is a corpus record
|
|
465
|
+
closes MEASURED iff that record PASSED, and a ticked criterion without one reports
|
|
466
|
+
`narrated_only` — the same rule a green JUnit testcase has always obeyed. `readiness` prints one
|
|
467
|
+
conditional `--- field <repo>: ...` line per repo that declares a corpus or ran one; it carries
|
|
468
|
+
no weight in the score (that dimension is deferred to its own ADR).
|
|
469
|
+
|
|
409
470
|
4. The **static analysis gate** (`java-qa-gate`: Checkstyle/PMD/SpotBugs/FindSecBugs)
|
|
410
471
|
is NOT counted by hand. Run the gate so its XML reports are written, then ingest
|
|
411
472
|
them — the ledger parses the reports, normalizes severities to the common gate
|
|
@@ -508,13 +569,53 @@ python3 $QL check-terminado # 0 = sealed · 1 = broken · 2 = UNMEASURE
|
|
|
508
569
|
seal broke. **Exit 2** — the seal is UNMEASURED (no git, or no snapshot recorded): say so
|
|
509
570
|
plainly; an answer nobody could measure is not a TERMINADO either.
|
|
510
571
|
- Ensure conventional-commit history is clean.
|
|
572
|
+
- **List the agent-origin items that are still unconfirmed** in the PR body, under
|
|
573
|
+
**Agent-origin, unconfirmed** (principle 6). `spec-check` names them with file:line;
|
|
574
|
+
copy that list. An empty list is written as "none" — silence and "none" are not the
|
|
575
|
+
same fact.
|
|
576
|
+
- **Cite the operability line** in the PR body, under **Operability** (kit 2.2.0, ADR-048):
|
|
577
|
+
copy `readiness`'s `--- operability: ...` line verbatim, whichever four verdicts it
|
|
578
|
+
carries. A reviewer should not have to ask whether this change ships with a release
|
|
579
|
+
path, a reset and a RUNBOOK — and a red line in the PR body is a conversation, which is
|
|
580
|
+
exactly the conversation two projects never had until their last week.
|
|
511
581
|
- Open the PR(s). Confirm CI is green.
|
|
512
582
|
- **STOP.** Present the PR link(s) and wait for the human to merge.
|
|
513
583
|
|
|
514
|
-
## Phase 7 — Smoke
|
|
584
|
+
## Phase 7 — Smoke run (measured, kit 2.2.0, ADR-047)
|
|
585
|
+
|
|
586
|
+
**Evidence is executed, not narrated.** This phase used to end in a prose checklist, and
|
|
587
|
+
prose is exactly where a smoke run goes to die: "the jar served /admin", "the simulator
|
|
588
|
+
answered 200 in 6 ms" are an agent's sentences, believed because they are written
|
|
589
|
+
confidently. A real one cost a field team a release — every simulator run returned an
|
|
590
|
+
empty list because the database had no rows, and a smoke narrated as "verified" would
|
|
591
|
+
have hidden it behind a paragraph. So RUN the smoke paths (real endpoints, real user
|
|
592
|
+
flows, real device paths for this change) with the project's own tool, have that tool
|
|
593
|
+
write `reports/smoke.json`, and INGEST it:
|
|
594
|
+
|
|
595
|
+
```json
|
|
596
|
+
{"checks": [{"name": "AC-28 the jar serves /admin", "ok": true, "status": 200,
|
|
597
|
+
"latency_ms": 6, "evidence": "curl -sS localhost:8080/admin | head -1"}]}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
```bash
|
|
601
|
+
python3 $QL smoke-ingest --repo <REPO> --report reports/smoke.json # exit 1 = a check failed
|
|
602
|
+
```
|
|
515
603
|
|
|
516
|
-
|
|
517
|
-
|
|
604
|
+
`name` and a boolean `ok` are the whole mandatory contract; `status`, `latency_ms` and
|
|
605
|
+
`evidence` are optional and never invented. A missing `checks`, an EMPTY list, or a check
|
|
606
|
+
without a name or a boolean `ok` is exit 2 naming it — a run that verified nothing is not
|
|
607
|
+
a clean gate. A failed check is a BLOCKER like any other fact gate: readiness ≤65,
|
|
608
|
+
convergence blocked, cleared by a later clean report. `smoke` is a FACT kind and never
|
|
609
|
+
runs advisory — a check either answered or it did not. Name a check `AC-nn ...` (the same
|
|
610
|
+
tag grammar a JUnit testcase name uses) and a green check inside a passing report closes
|
|
611
|
+
that criterion MEASURED; a failed one vetoes it, exactly like a red test.
|
|
612
|
+
`log-gate --kind smoke --verdict pass|fail|not-run` is the parity door for a smoke
|
|
613
|
+
measured elsewhere. `uscha-kit/templates/scripts/smoke-report-example.json` is the
|
|
614
|
+
reference report. If the human runs the smoke by hand, they still write the report: a
|
|
615
|
+
checklist a human ticked is evidence, a checklist an agent narrated is not.
|
|
616
|
+
|
|
617
|
+
**The PR body cites the gate** (phase 6): paste the `smoke-ingest` verdict line —
|
|
618
|
+
`<repo>/gate:smoke: PASS — 8/8 checks ok` — not a prose claim that the smoke passed.
|
|
518
619
|
|
|
519
620
|
## Phase 8 — Hand off to docs + retrospective
|
|
520
621
|
|
|
@@ -523,6 +624,33 @@ python3 $QL summary # human-readable
|
|
|
523
624
|
python3 $QL summary --json # machine-readable, includes post_merge_calibration
|
|
524
625
|
```
|
|
525
626
|
|
|
627
|
+
**Operability is MEASURED here, not promised (kit 2.2.0, ADR-048).** Release by CI, the
|
|
628
|
+
reset/seed script and the RUNBOOK used to live in this phase as PROSE, and prose is what
|
|
629
|
+
lets "we do that at the end" survive every gate the kit has. Run it, per repo, BEFORE
|
|
630
|
+
readiness so the record is in the ledger when the KPI renders:
|
|
631
|
+
|
|
632
|
+
```bash
|
|
633
|
+
python3 $QL operability --repo <REPO> # exit 0 always; the gate is the profile's
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
It reads four FACTS out of the tree and never runs anything: a `.github/workflows` step
|
|
637
|
+
that runs the repo's **configured** test command, a workflow that publishes or attaches a
|
|
638
|
+
release asset, a `RUNBOOK.md` (`docs/` first) naming start/config/rollback/smoke in EN or
|
|
639
|
+
ES, and a `seed_command` the config declares whose script is on disk. Repo path first,
|
|
640
|
+
CONFIG ROOT second, and it NAMES which it read. A CI system it does not read (GitLab,
|
|
641
|
+
Jenkins, Azure) is reported `unknown` — never green, never red.
|
|
642
|
+
|
|
643
|
+
The POSTURE is the risk profile's, not the kit's: on **A/B** the record is `advisory`
|
|
644
|
+
(caps nothing, blocks nothing, never counted as `ok`); on **C/D/E** — where
|
|
645
|
+
`defaults.operability.gate` is `true` — a missing check is a BLOCKER that caps readiness
|
|
646
|
+
≤65, blocks convergence, and makes `phase --require pr-ready` refuse NAMING the missing
|
|
647
|
+
one. Do NOT hand-wave a missing check: write the workflow, write the RUNBOOK section,
|
|
648
|
+
declare the seed command. If the project genuinely does not want the gate, that is a
|
|
649
|
+
declaration (`defaults.operability.gate: false`), not a silence.
|
|
650
|
+
|
|
651
|
+
`readiness` then prints one conditional line — `--- operability: ci ok · release missing ·
|
|
652
|
+
runbook ok · seed missing (advisory)` — and that is the line you cite in the PR body.
|
|
653
|
+
|
|
526
654
|
**Readiness KPI — show this after finishing ANY task, not only full runs.** It measures
|
|
527
655
|
the STATE of the result (not effort spent), as a weighted score 0..100 with hard caps:
|
|
528
656
|
|
|
@@ -542,6 +670,11 @@ Right after readiness, run the spec-maintenance advisory (kit 1.66.0):
|
|
|
542
670
|
python3 $QL spec-drift --repo <REPO>
|
|
543
671
|
```
|
|
544
672
|
|
|
673
|
+
It searches the repo's own path first and the CONFIG ROOT (where `uscha.config.json` and
|
|
674
|
+
the ledger live) second, and NAMES which of the two it read — in a monorepo the single
|
|
675
|
+
`SPEC.md` at the root is the spec of every repo, and "no spec documents" used to be
|
|
676
|
+
indistinguishable from "no drift" (kit 2.2.0).
|
|
677
|
+
|
|
545
678
|
Milliseconds, deterministic, exit 0 always — it never gates, so running it every pass
|
|
546
679
|
adds zero ceremony. What it adds is VISIBILITY: the run lands in the ledger, so the
|
|
547
680
|
mirador card and `/uscha-status` show drift without anyone remembering the command —
|