@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,130 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""telemetry-extract.py -- vendor telemetry for the mirador (uscha-kit 1.33.0).
|
|
3
|
+
|
|
4
|
+
Parses a Claude Code session transcript (*.jsonl) and appends ONE line to the
|
|
5
|
+
telemetry sidecar (.uscha/telemetry.jsonl) summarizing that session's token and
|
|
6
|
+
wall-time cost, broken down by model.
|
|
7
|
+
|
|
8
|
+
This is VENDOR telemetry (Claude Code), NOT engine measurement. It lives in the
|
|
9
|
+
mirador skill (the vendor adapter), NEVER in qa_ledger.py -- the engine stays
|
|
10
|
+
model-agnostic and never sees a token. The mirador shows this in a segregated
|
|
11
|
+
strip; it is never gated and never feeds readiness (measured beats narrated).
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
python3 telemetry-extract.py <transcript.jsonl> [--sidecar .uscha/telemetry.jsonl] [--note "..."]
|
|
15
|
+
|
|
16
|
+
Best-effort: unknown/older transcript schemas degrade (missing fields -> 0/None),
|
|
17
|
+
never crash. If no usage data is found, nothing is appended.
|
|
18
|
+
"""
|
|
19
|
+
import argparse
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _ts(v):
|
|
27
|
+
if not v:
|
|
28
|
+
return None
|
|
29
|
+
try:
|
|
30
|
+
return datetime.fromisoformat(str(v).replace("Z", "+00:00"))
|
|
31
|
+
except (ValueError, TypeError):
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def extract(transcript_path):
|
|
36
|
+
"""Returns (by_model: {model: [tin, tout]}, t_min, t_max, seen: bool)."""
|
|
37
|
+
by_model = {}
|
|
38
|
+
t_min = t_max = None
|
|
39
|
+
seen = False
|
|
40
|
+
with open(transcript_path, encoding="utf-8") as f:
|
|
41
|
+
for line in f:
|
|
42
|
+
line = line.strip()
|
|
43
|
+
if not line:
|
|
44
|
+
continue
|
|
45
|
+
try:
|
|
46
|
+
rec = json.loads(line)
|
|
47
|
+
except ValueError:
|
|
48
|
+
continue
|
|
49
|
+
ts = _ts(rec.get("timestamp"))
|
|
50
|
+
if ts:
|
|
51
|
+
t_min = ts if (t_min is None or ts < t_min) else t_min
|
|
52
|
+
t_max = ts if (t_max is None or ts > t_max) else t_max
|
|
53
|
+
msg = rec.get("message") or {}
|
|
54
|
+
usage = msg.get("usage")
|
|
55
|
+
if not isinstance(usage, dict):
|
|
56
|
+
continue
|
|
57
|
+
model = msg.get("model") or rec.get("model") or "unknown"
|
|
58
|
+
tin = ((usage.get("input_tokens") or 0)
|
|
59
|
+
+ (usage.get("cache_read_input_tokens") or 0)
|
|
60
|
+
+ (usage.get("cache_creation_input_tokens") or 0))
|
|
61
|
+
tout = usage.get("output_tokens") or 0
|
|
62
|
+
if tin == 0 and tout == 0:
|
|
63
|
+
continue
|
|
64
|
+
agg = by_model.setdefault(model, [0, 0])
|
|
65
|
+
agg[0] += tin
|
|
66
|
+
agg[1] += tout
|
|
67
|
+
seen = True
|
|
68
|
+
return by_model, t_min, t_max, seen
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def main():
|
|
72
|
+
ap = argparse.ArgumentParser(description="append a Claude Code session's token/time cost to the mirador telemetry sidecar")
|
|
73
|
+
ap.add_argument("transcript", help="path to a Claude Code session transcript (*.jsonl)")
|
|
74
|
+
ap.add_argument("--sidecar", default=os.path.join(".uscha", "telemetry.jsonl"),
|
|
75
|
+
help="append-only telemetry file (default: .uscha/telemetry.jsonl)")
|
|
76
|
+
ap.add_argument("--session", default=None,
|
|
77
|
+
help="session key for idempotent upsert (default: transcript basename); "
|
|
78
|
+
"re-running replaces this session's line instead of appending -- "
|
|
79
|
+
"so a watch/refresh loop does not inflate the totals")
|
|
80
|
+
ap.add_argument("--note", default=None, help="optional label for this session line")
|
|
81
|
+
args = ap.parse_args()
|
|
82
|
+
|
|
83
|
+
by_model, t_min, t_max, seen = extract(args.transcript)
|
|
84
|
+
if not seen:
|
|
85
|
+
print("[telemetry-extract] no usage data found -- nothing appended", file=sys.stderr)
|
|
86
|
+
return 0
|
|
87
|
+
|
|
88
|
+
bm = [{"model": m, "tokens_in": v[0], "tokens_out": v[1]}
|
|
89
|
+
for m, v in sorted(by_model.items())]
|
|
90
|
+
total_in = sum(v["tokens_in"] for v in bm)
|
|
91
|
+
total_out = sum(v["tokens_out"] for v in bm)
|
|
92
|
+
ms = int((t_max - t_min).total_seconds() * 1000) if (t_min and t_max) else None
|
|
93
|
+
at = t_max.isoformat() if t_max else None
|
|
94
|
+
rec = {
|
|
95
|
+
"at": at,
|
|
96
|
+
"model": bm[0]["model"] if len(bm) == 1 else "+".join(v["model"] for v in bm),
|
|
97
|
+
"tokens_in": total_in, "tokens_out": total_out, "ms": ms,
|
|
98
|
+
"by_model": bm,
|
|
99
|
+
}
|
|
100
|
+
session = args.session or os.path.basename(args.transcript)
|
|
101
|
+
rec["session"] = session
|
|
102
|
+
if args.note:
|
|
103
|
+
rec["note"] = args.note
|
|
104
|
+
|
|
105
|
+
os.makedirs(os.path.dirname(args.sidecar) or ".", exist_ok=True)
|
|
106
|
+
# upsert by session: re-running (watch mode) REPLACES this session's line instead of
|
|
107
|
+
# appending a duplicate -> a refresh loop never inflates the aggregated totals.
|
|
108
|
+
kept = []
|
|
109
|
+
if os.path.isfile(args.sidecar):
|
|
110
|
+
for line in open(args.sidecar, encoding="utf-8"):
|
|
111
|
+
line = line.strip()
|
|
112
|
+
if not line:
|
|
113
|
+
continue
|
|
114
|
+
try:
|
|
115
|
+
prev = json.loads(line)
|
|
116
|
+
except ValueError:
|
|
117
|
+
continue
|
|
118
|
+
if prev.get("session") != session:
|
|
119
|
+
kept.append(prev)
|
|
120
|
+
kept.append(rec)
|
|
121
|
+
with open(args.sidecar, "w", encoding="utf-8") as f:
|
|
122
|
+
for r in kept:
|
|
123
|
+
f.write(json.dumps(r, ensure_ascii=False) + "\n")
|
|
124
|
+
print(f"[telemetry-extract] {session}: {total_in} in / {total_out} out "
|
|
125
|
+
f"across {len(bm)} model(s) -> {args.sidecar} (upsert)")
|
|
126
|
+
return 0
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
if __name__ == "__main__":
|
|
130
|
+
sys.exit(main())
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: uscha-reverse-discovery
|
|
3
|
+
description: >
|
|
4
|
+
Brownfield front of the methodology, for migrating/modernizing an EXISTING system. The
|
|
5
|
+
inverse of discovery: the system already exists and its behavior IS the truth, so you
|
|
6
|
+
EXTRACT facts instead of proposing shape. Produce ONLY facts — a system map (endpoints,
|
|
7
|
+
contracts, dependency graph, module candidates via static analysis) and a golden suite
|
|
8
|
+
captured mechanically at the boundaries. NEVER author an inferred SPEC or ADR of the old
|
|
9
|
+
system; the human writes those reading your facts. Invoke for "reverse-discovery",
|
|
10
|
+
"migrar/modernizar este sistema", "caracterizar el sistema viejo antes de tocarlo".
|
|
11
|
+
allowed-tools: Read, Write, Glob, Grep, Bash
|
|
12
|
+
disable-model-invocation: false
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# reverse-discovery — extract the facts of an existing system before migrating it
|
|
16
|
+
|
|
17
|
+
`uscha-discovery` is greenfield: you only have an idea, so you PROPOSE the shape. This is the
|
|
18
|
+
opposite. The system already runs; its observable behavior is the ground truth. **You do
|
|
19
|
+
not invent anything — you characterize what is already there, as facts.**
|
|
20
|
+
|
|
21
|
+
## The one non-negotiable: produce ONLY facts
|
|
22
|
+
|
|
23
|
+
A system map (from static analysis) and a golden suite (byte-captured) are FACTS —
|
|
24
|
+
verifiable, not opinions. **You do NOT author a SPEC of "what it does" or ADRs of "why it
|
|
25
|
+
is built this way."** Those are inference, and if the agent writes them it encodes its own
|
|
26
|
+
(mis)reading of the code — the exact blind spot the golden exists to counter. The golden
|
|
27
|
+
is field truth; a SPEC the agent writes about legacy code is a claim. So this skill emits
|
|
28
|
+
facts, and the human infers meaning from them.
|
|
29
|
+
|
|
30
|
+
If you catch yourself writing a requirement or a rationale, stop: that belongs to the human
|
|
31
|
+
(and to `/uscha-adr-refine` for the FORWARD decisions), not here.
|
|
32
|
+
|
|
33
|
+
## Phase 1 — Map (fact)
|
|
34
|
+
|
|
35
|
+
Static analysis only. Extract, and write to `SYSTEM-MAP.md`:
|
|
36
|
+
- **Boundaries**: every public endpoint / operation / message topic, with its contract
|
|
37
|
+
(request/response schema, status codes, idempotency where observable).
|
|
38
|
+
- **Dependency graph**: who calls whom (service→service, module→module, DB, external APIs).
|
|
39
|
+
Flag cycles and hubs.
|
|
40
|
+
- **Module candidates**: current package/service structure and natural seams (this is
|
|
41
|
+
observation of the CURRENT layout, NOT a proposal for the new one).
|
|
42
|
+
Everything here must be traceable to code. No "this seems to…", no guessed intent.
|
|
43
|
+
|
|
44
|
+
## Phase 2 — Characterize (fact)
|
|
45
|
+
|
|
46
|
+
Capture the golden at the system boundaries by running the ORIGINAL code with REAL inputs.
|
|
47
|
+
Delegate to the `uscha-characterize` skill; if it is not installed, follow its contract inline:
|
|
48
|
+
- Write a deterministic capture harness (you MAY author the harness).
|
|
49
|
+
- Normalize every non-determinism source before serializing: timestamps, seeds, map/set
|
|
50
|
+
iteration order, GUIDs/auto-increment, concurrency, **the target locale**, and use
|
|
51
|
+
deterministic serialization (sorted keys, fixed float precision, explicit encoding).
|
|
52
|
+
- Run the capture → `.received`. **STOP.** Return control to the human to review and
|
|
53
|
+
approve the `.approved`.
|
|
54
|
+
- **You NEVER create, rename, or edit a `.approved` file** (INV-GOLDEN-01). It is field
|
|
55
|
+
truth; only a human approves it.
|
|
56
|
+
- Corpus in order of value: real/recorded production inputs → hand-built edge cases →
|
|
57
|
+
inputs of past bugs. A boundary whose corpus does not exercise its known branches is
|
|
58
|
+
marked **PARTIAL**, never covered.
|
|
59
|
+
|
|
60
|
+
## Phase 3 — Summary (facts, no opinion)
|
|
61
|
+
|
|
62
|
+
Write `DISCOVERY-SUMMARY.md`: the system map + the golden coverage report (which boundaries
|
|
63
|
+
are captured and approved, which are PARTIAL and why). This is the fact base the human reads
|
|
64
|
+
to write the migration SPEC. Do not editorialize.
|
|
65
|
+
|
|
66
|
+
## What you do NOT do (the human's job)
|
|
67
|
+
|
|
68
|
+
- Do NOT write a SPEC of the old system's behavior — the golden IS the executable spec.
|
|
69
|
+
- Do NOT write ADRs of the old system's implicit decisions.
|
|
70
|
+
- Do NOT decide the NEW structure (module boundaries, shared kernel, sync vs events). Those
|
|
71
|
+
are forward decisions → `/uscha-adr-refine`.
|
|
72
|
+
|
|
73
|
+
## Guardrails
|
|
74
|
+
|
|
75
|
+
- `.approved` files are sacred and human-approved; the agent is mechanically forbidden from
|
|
76
|
+
writing them (a `PreToolUse` hook on `**/*.approved.*` should enforce it).
|
|
77
|
+
- `.gitattributes`: `*.approved.* binary` (line endings must not create false diffs).
|
|
78
|
+
- Corpus insufficient → PARTIAL. Never claim coverage you did not exercise.
|
|
79
|
+
|
|
80
|
+
## Convergence — finish when
|
|
81
|
+
|
|
82
|
+
The map is complete (every boundary and dependency accounted for, or explicitly marked
|
|
83
|
+
unknown), the golden is captured and **human-approved**, and the coverage report states
|
|
84
|
+
what is covered vs PARTIAL. State plainly that the facts are ready, then hand off.
|
|
85
|
+
|
|
86
|
+
## Handoff
|
|
87
|
+
|
|
88
|
+
> "Read SYSTEM-MAP.md and DISCOVERY-SUMMARY.md, and inspect the approved golden. These are
|
|
89
|
+
> FACTS about the current system. Now write the migration SPEC — behavior == golden,
|
|
90
|
+
> structure == the new module boundaries — and take the partition decisions via /uscha-adr-refine.
|
|
91
|
+
> Do not treat any of my output as a requirement or a rationale; those are yours to decide."
|
|
92
|
+
|
|
93
|
+
Flow (migration): `uscha-reverse-discovery` (facts) → human writes SPEC + `/uscha-adr-refine` (forward
|
|
94
|
+
module decisions) → `/uscha-devloop` (restructure; `golden-diff` + `ApplicationModules.verify()`
|
|
95
|
+
stay green the whole way) → readiness + human gate.
|
|
96
|
+
|
|
97
|
+
## Relationship to the other skills
|
|
98
|
+
|
|
99
|
+
- **discovery** (greenfield): you PROPOSE the shape from an idea. **reverse-discovery**
|
|
100
|
+
(brownfield): you EXTRACT facts from a running system. Opposite direction, opposite trust
|
|
101
|
+
model.
|
|
102
|
+
- **characterize**: the golden-capture sub-step this skill orchestrates in Phase 2.
|
|
103
|
+
- **adr-refine**: where the FORWARD decisions (the new module boundaries) are taken — this
|
|
104
|
+
skill deliberately does not.
|
|
105
|
+
|
|
106
|
+
## Why this skill is safe by construction
|
|
107
|
+
|
|
108
|
+
It lives entirely on the FACTS side of the facts-vs-prose line: static analysis and
|
|
109
|
+
byte-capture, both verifiable. Unlike a prose-heuristic gate, there is no interpretation to
|
|
110
|
+
get wrong — so there is nothing fragile to break. If a step would require guessing, it has
|
|
111
|
+
left this skill's scope.
|
|
112
|
+
|
|
113
|
+
## Tracked-markdown protocol
|
|
114
|
+
|
|
115
|
+
If `SYSTEM-MAP.md` / `DISCOVERY-SUMMARY.md` already exist and are tracked, ask for the
|
|
116
|
+
current version before overwriting — never silently replace real progress.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: uscha-rubric
|
|
3
|
+
description: >
|
|
4
|
+
Grade the change against the versioned RUBRIC.md (the ACCEPTANCE of the
|
|
5
|
+
non-testable: conventions, error-handling sanity, API ergonomics, doc quality)
|
|
6
|
+
and ingest the verdict into the ledger. This skill is a THIN ADAPTER for
|
|
7
|
+
Claude Code: the portable core is templates/rubric-grader-prompt.md (works on
|
|
8
|
+
Codex, Gemini CLI, Cursor, raw API, or a human) + the vendor-neutral JSON
|
|
9
|
+
contract that `qa_ledger.py rubric-ingest` validates. Advisory by default;
|
|
10
|
+
gates only when the human declares it. Invoke for "grade the rubric",
|
|
11
|
+
"evaluá la rúbrica", "rubric pass".
|
|
12
|
+
allowed-tools: Read, Write, Glob, Grep, Bash
|
|
13
|
+
disable-model-invocation: false
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# uscha-rubric — grade the non-testable against versioned criteria (adapter)
|
|
17
|
+
|
|
18
|
+
**Architecture note (read this first).** You are the Claude Code ADAPTER of a
|
|
19
|
+
vendor-neutral layer. The core is: `RUBRIC.md` (versioned criteria) + the JSON
|
|
20
|
+
contract + `qa_ledger.py rubric-ingest` (stdlib, runs anywhere). ANY runner can be
|
|
21
|
+
the grader — this skill just wraps the neutral prompt so Claude Code users get it
|
|
22
|
+
in one command. Never add Claude-specific behavior to the contract.
|
|
23
|
+
|
|
24
|
+
## Protocol
|
|
25
|
+
|
|
26
|
+
1. **Locate the rubric**: `defaults.rubric.file` in `uscha.config.json`, else
|
|
27
|
+
`./RUBRIC.md`. If absent, offer to create one from `templates/RUBRIC.md` and STOP
|
|
28
|
+
(the criteria are the human's to approve — propose, don't impose).
|
|
29
|
+
2. **Validate structure first** (facts block):
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
QL="./.claude/skills/uscha-devloop/qa_ledger.py"
|
|
33
|
+
[ -f "$QL" ] || QL="$HOME/.codex/skills/uscha-devloop/qa_ledger.py"
|
|
34
|
+
[ -f "$QL" ] || QL="$HOME/plugins/uscha/skills/uscha-devloop/qa_ledger.py"
|
|
35
|
+
[ -f "$QL" ] || QL="$HOME/.claude/skills/uscha-devloop/qa_ledger.py"
|
|
36
|
+
python3 $QL spec-check --rubric RUBRIC.md # exit 1 = fix the rubric before grading
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
3. **Grade with ISOLATED context** — follow `templates/rubric-grader-prompt.md` to
|
|
40
|
+
the letter: read ONLY the diff + RUBRIC.md (not the maker's reasoning, not the PR
|
|
41
|
+
body). For every criterion emit `pass|fail`; **evidence `file:line` is mandatory
|
|
42
|
+
for any verdict that affects the score** (a positive's pass, a negative's fail) —
|
|
43
|
+
without it the engine discards the verdict. Anchors calibrate you; when in doubt,
|
|
44
|
+
fail (the optimist bias is the failure mode this layer exists to counter).
|
|
45
|
+
4. **Write the contract JSON** to `reports/rubric-grade.json`:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{"criteria": [{"id": "RB-01", "verdict": "pass",
|
|
49
|
+
"evidence": "src/x.py:42 — ...", "note": "..."}]}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
5. **Ingest** (the ledger validates IDs, applies evidence-or-nothing, computes the
|
|
53
|
+
weighted score vs threshold, and persists — advisory by default):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python3 $QL rubric-ingest --repo <REPO> --report reports/rubric-grade.json \
|
|
57
|
+
--iteration <N> # add --gate ONLY if the human declared it
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
A below-threshold score with the gate declared (config `defaults.rubric.gate: true`
|
|
61
|
+
or `--gate`) blocks convergence and caps readiness ≤65 through the existing ledger
|
|
62
|
+
plumbing. Without the declaration it advises — never silently escalate it yourself.
|
|
63
|
+
|
|
64
|
+
## Non-negotiables
|
|
65
|
+
|
|
66
|
+
- **Maker ≠ grader**: never grade a change you authored in this same context. Run
|
|
67
|
+
the grade in a fresh/isolated pass (that separation is the entire value).
|
|
68
|
+
- **Evidence-or-nothing**: a verdict without a `file:line` citation does not count —
|
|
69
|
+
the engine enforces it, you comply with it.
|
|
70
|
+
- The rubric file is the HUMAN's criterion: propose edits, never rewrite it silently
|
|
71
|
+
(tracked-markdown protocol applies).
|
|
72
|
+
- This layer never replaces the hard gates (tests, golden, gate-check, simplicity):
|
|
73
|
+
it is the structured-guess layer — facts block, guesses advise.
|
|
74
|
+
|
|
75
|
+
## Relationship to the other skills
|
|
76
|
+
|
|
77
|
+
- `uscha-devloop` runs this in Phase 3b alongside gate-check when a rubric exists.
|
|
78
|
+
- `uscha-discovery` / `uscha-adr-refine` are where the human's quality criteria
|
|
79
|
+
crystallize — a RUBRIC.md can be drafted there (step: quality bar).
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: uscha-sysdoc
|
|
3
|
+
description: >
|
|
4
|
+
Generate a single self-contained, navigable HTML deck (PowerPoint-style, keyboard +
|
|
5
|
+
click navigation) documenting a system in two parallel tracks: a commercial/CEO view
|
|
6
|
+
and a technical view. Pulls real metrics from QA-LEDGER.json, includes inline SVG
|
|
7
|
+
diagrams, dark control-room aesthetic. Invoke for "document this system",
|
|
8
|
+
"make the system deck", "commercial + tech doc". Pairs with the dev-loop skill.
|
|
9
|
+
allowed-tools: Read, Write, Glob, Grep, Bash
|
|
10
|
+
disable-model-invocation: false
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# sys-doc — two-view system deck generator
|
|
14
|
+
|
|
15
|
+
Produce ONE self-contained `.html` file (no external assets, no CDN, no localStorage)
|
|
16
|
+
that reads like a slide deck and documents the system on two tracks the reader can
|
|
17
|
+
switch between at any time:
|
|
18
|
+
|
|
19
|
+
- **Commercial / CEO track** — what the system does, the value, the risk posture, the
|
|
20
|
+
status. No code. Plain business language. Money/time/reliability framing.
|
|
21
|
+
- **Technical track** — architecture, modules, data flow, contracts, QA results,
|
|
22
|
+
coverage, known deferred issues.
|
|
23
|
+
|
|
24
|
+
## Inputs
|
|
25
|
+
|
|
26
|
+
1. **Metrics (authoritative):** run the ledger summary and use its numbers verbatim —
|
|
27
|
+
never invent figures.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
QL="./.claude/skills/uscha-devloop/qa_ledger.py" # instalacion por proyecto
|
|
31
|
+
[ -f "$QL" ] || QL="$HOME/.codex/skills/uscha-devloop/qa_ledger.py" # Codex raw-skills install
|
|
32
|
+
[ -f "$QL" ] || QL="$HOME/plugins/uscha/skills/uscha-devloop/qa_ledger.py" # Codex plugin install
|
|
33
|
+
[ -f "$QL" ] || QL="$HOME/.claude/skills/uscha-devloop/qa_ledger.py" # Claude global install
|
|
34
|
+
python3 $QL summary --json > /tmp/qa-summary.json
|
|
35
|
+
python3 $QL readiness --json > /tmp/qa-readiness.json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
From the summary use: `total_steps`, `by_tool`, `by_repo`, `aggregate`, `escalations`.
|
|
39
|
+
From readiness use: `score`, `status`, `cap_reason`, `dimensions`, `acceptance`,
|
|
40
|
+
`by_repo`. Render readiness as a **semaphore widget** at the top of slide 5 and as a
|
|
41
|
+
per-repo readiness column on the technical QA slide: green ≥80, amber 50–79, red <50,
|
|
42
|
+
and always print the `cap_reason` when a hard cap is active.
|
|
43
|
+
|
|
44
|
+
2. **System understanding:** read the ADR/PLAN, CLAUDE.md, module layout, and key
|
|
45
|
+
contracts to describe architecture and value. If no ledger exists, ask whether to
|
|
46
|
+
proceed without QA metrics (the deck still works, just without the QA section).
|
|
47
|
+
|
|
48
|
+
3. **Tracked-markdown protocol:** the HTML output itself is not tracked markdown, so
|
|
49
|
+
generate freely. But if asked to also update a tracked `.md`, ask for its current
|
|
50
|
+
version first.
|
|
51
|
+
|
|
52
|
+
## Structure (each is one navigable slide)
|
|
53
|
+
|
|
54
|
+
1. **Title** — system name, one-line purpose, date, run id.
|
|
55
|
+
2. **Track switcher** — persistent toggle: Commercial ⇄ Technical (affects which
|
|
56
|
+
slides/sections show; default Commercial).
|
|
57
|
+
3. Commercial: **What it does** (plain language, the job it removes).
|
|
58
|
+
4. Commercial: **Value & status** (what's done, what's in flight, risk posture).
|
|
59
|
+
5. Commercial: **Quality at a glance** — coverage %, tests count, a simple
|
|
60
|
+
"issues found and resolved" readout from `by_tool`. No jargon.
|
|
61
|
+
6. Technical: **Architecture** — inline SVG: modules/repos as boxes, data flow as
|
|
62
|
+
arrows, external systems (DB, external APIs, devices) distinct.
|
|
63
|
+
7. Technical: **Key contracts / interfaces** — the seams between repos/modules.
|
|
64
|
+
8. Technical: **QA results** — per-tool table (reported / fixed / %fixed / deferred /
|
|
65
|
+
suppressed), coverage per repo, tests/kLOC, escalations list.
|
|
66
|
+
9. Technical: **Deferred issues** — summarize `ISSUES-DEFERRED.md` honestly.
|
|
67
|
+
10. **Smoke checklist** — the manual verification steps.
|
|
68
|
+
|
|
69
|
+
## Build constraints
|
|
70
|
+
|
|
71
|
+
- Single `.html`, all CSS/JS inline. Works opened directly from disk and deployable to
|
|
72
|
+
Cloudflare Pages / S3 as-is.
|
|
73
|
+
- **No localStorage / sessionStorage** (won't run in some sandboxes). Hold nav state in
|
|
74
|
+
JS variables only.
|
|
75
|
+
- Navigation: arrow keys (← →), on-screen prev/next, a slide index/dots, and Esc for an
|
|
76
|
+
overview grid. Slide counter visible.
|
|
77
|
+
- Diagrams are hand-authored inline `<svg>` using `currentColor`/CSS variables so they
|
|
78
|
+
theme with the deck. No raster images, no external diagram libs.
|
|
79
|
+
- Aesthetic: dark control-room (deep neutral background, one accent, high-contrast
|
|
80
|
+
mono for technical figures), but keep the Commercial track clean and uncluttered.
|
|
81
|
+
- Accessible: semantic headings, `aria-label`s on nav controls, visible focus, contrast
|
|
82
|
+
AA. Readable when printed (print stylesheet flattens slides to a linear document).
|
|
83
|
+
|
|
84
|
+
## Output
|
|
85
|
+
|
|
86
|
+
Write to `docs/system-deck.html` (or the path the human gives). Then state the file
|
|
87
|
+
path and the two or three things the reader should look at first. Do not paste the HTML
|
|
88
|
+
into chat — present the file.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# CLAUDE.md — repo protocol (Uscha)
|
|
2
|
+
|
|
3
|
+
**Permanent** rules for this repo. Claude Code reads them at every session. What is specific to
|
|
4
|
+
each change lives in `SPEC.md` / `docs/adr/` / `ACCEPTANCE.md`, not here.
|
|
5
|
+
|
|
6
|
+
> If you use other agents besides Claude Code, copy this file as `AGENTS.md`
|
|
7
|
+
> (same content) so they read it too.
|
|
8
|
+
|
|
9
|
+
## Non-negotiable rules
|
|
10
|
+
|
|
11
|
+
1. **Don't code from a vague idea.** If there is no `SPEC.md` + `ACCEPTANCE.md`, model
|
|
12
|
+
first: `/uscha-discovery` (new system) or `/uscha-adr-refine` (known feature). Only once the
|
|
13
|
+
package is written do you build.
|
|
14
|
+
2. **Truth lives in files, not in the chat.** Before touching code, read `SPEC.md`,
|
|
15
|
+
`ACCEPTANCE.md` and `docs/adr/*.md`. Don't rely on the conversation: context
|
|
16
|
+
resets, and sub-agents and CI read the repo.
|
|
17
|
+
3. **Converge, don't chase zero.** Apply only the findings ≥ severity gate; the rest
|
|
18
|
+
goes to `ISSUES-DEFERRED.md`. The loop ends when it converges, not when "there are no issues".
|
|
19
|
+
4. **ADR + CONSTITUTION discipline during the build.** Before touching an area, read
|
|
20
|
+
`CONSTITUTION.md` (inviolable invariants) and the ADRs for the area. Stop and propose an ADR if
|
|
21
|
+
you are going to: introduce a new dependency, create a new pattern, choose between non-obvious
|
|
22
|
+
alternatives, or contradict an accepted ADR. **A CONSTITUTION violation is a BLOCKER: it is
|
|
23
|
+
escalated, never worked around.** Link the code with `// ADR: <slug> — see docs/adr/...`.
|
|
24
|
+
5. **Evidence captured, not narrated.** Evidence is produced by execution (tests,
|
|
25
|
+
gates, coverage) — it is not transcribed by hand. Absent = no evidence, never "OK".
|
|
26
|
+
6. **Legacy baseline.** In old code: 0 new HIGH/CRITICAL findings, 0 regressions,
|
|
27
|
+
no new warnings in touched files. Old debt is frozen, new debt is blocked.
|
|
28
|
+
7. **Change budget.** Max iterations/files per the plan; 0 schema changes without an
|
|
29
|
+
ADR; 0 new dependencies without approval. If the scope is exceeded or a fix reverts another
|
|
30
|
+
→ escalate (don't keep going alone).
|
|
31
|
+
8. **Never edit the SPEC/ADR to make the implementation look correct.** If reality
|
|
32
|
+
forces a SPEC change, version it and go back to Ready.
|
|
33
|
+
9. **Human gate.** Don't merge or release automatically. You stop at the PR; the merge and
|
|
34
|
+
the smoke test in a real environment are decided by a person.
|
|
35
|
+
|
|
36
|
+
## Truth hierarchy
|
|
37
|
+
|
|
38
|
+
`CONSTITUTION.md` (what is never acceptable) ▸ `SPEC.md` (what must happen) ▸ `docs/adr/` (why this shape). The CONSTITUTION sits above the ADRs: no ADR or SPEC may violate it. `/uscha-discovery`, `/uscha-adr-refine` and `/uscha-devloop` read it before proposing or touching anything.
|
|
39
|
+
|
|
40
|
+
## Commands (skills)
|
|
41
|
+
|
|
42
|
+
- `/uscha-discovery` — idea → CONTEXT/DOMAIN-MODEL/CONSTITUTION/SPEC/ADR/ACCEPTANCE/RISKS/HANDOFF
|
|
43
|
+
- `/uscha-adr-refine` — known feature → SPEC + ADR + ACCEPTANCE
|
|
44
|
+
- `/uscha-devloop` — plan → build → QA loop → PR (stops at the merge)
|
|
45
|
+
- `/uscha-sysdoc` — documents the system from the ledger
|
|
46
|
+
|
|
47
|
+
## Project adapter (TO COMPLETE per repo)
|
|
48
|
+
|
|
49
|
+
> This is the only stack-specific part. Complete it and delete this reminder.
|
|
50
|
+
|
|
51
|
+
- **Build:** `<e.g. mvn -q compile>`
|
|
52
|
+
- **Tests:** `<e.g. mvn -q test>`
|
|
53
|
+
- **Static gate:** `<e.g. mvn -q verify -Pqa → checkstyle-result.xml, pmd.xml, spotbugsXml.xml>`
|
|
54
|
+
- **Coverage:** `<e.g. target/site/jacoco/jacoco.xml>`
|
|
55
|
+
- **No-go zones:** `<folders/files that are not touched>`
|
|
56
|
+
- **Secrets / credentials:** `<where they are and what NOT to log>`
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# CONSTITUTION.md — project invariants (Uscha)
|
|
2
|
+
|
|
3
|
+
The layer **above the ADRs**. It records what **no ADR or SPEC may violate**,
|
|
4
|
+
whatever trade-off wins. An ADR *chooses* between alternatives; the CONSTITUTION *forbids*.
|
|
5
|
+
|
|
6
|
+
> Truth hierarchy: **SPEC** = what must happen · **ADR** = why this shape ·
|
|
7
|
+
> **CONSTITUTION** = what is never acceptable.
|
|
8
|
+
|
|
9
|
+
Versioned, one per project. `/discovery`, `/adr-refine` and `/dev-loop` read it **before**
|
|
10
|
+
proposing or touching anything. A violation is a **BLOCKER** finding (non-negotiable): the agent
|
|
11
|
+
MUST record it — `qa_ledger.py flag-blocker --kind constitution` — and once recorded it
|
|
12
|
+
blocks convergence and caps readiness ≤65 until `--resolve` (a human decision). The engine
|
|
13
|
+
does not read this file: the obligation to detect and record the violation belongs to the agent/human;
|
|
14
|
+
enforcing the record is the engine's job. It is never resolved by "working around it" in code.
|
|
15
|
+
|
|
16
|
+
## Security (non-negotiable)
|
|
17
|
+
|
|
18
|
+
- [ ] Secrets never in logs or in the repo <!-- CWE-532 / CWE-798 -->
|
|
19
|
+
- [ ] Every external input validated before use <!-- CWE-20 -->
|
|
20
|
+
- [ ] Only parameterized SQL, never concatenated <!-- CWE-89 -->
|
|
21
|
+
- [ ] Credentials / certificates encrypted at rest
|
|
22
|
+
- [ ] Never cross environments or credentials (dev / prod)
|
|
23
|
+
|
|
24
|
+
## Domain (TO COMPLETE per project)
|
|
25
|
+
|
|
26
|
+
> Business rules that can never be broken. Examples (replace with those of your domain):
|
|
27
|
+
|
|
28
|
+
- [ ] Mandatory idempotency on critical operations
|
|
29
|
+
- [ ] Sequential numbering with no logical gaps
|
|
30
|
+
- [ ] Exactness to the cent; never two effects for the same `requestId`
|
|
31
|
+
|
|
32
|
+
## Operation (non-negotiable)
|
|
33
|
+
|
|
34
|
+
- [ ] No destructive migration without an explicit rollback
|
|
35
|
+
- [ ] No automatic merge or release — human gate always
|
|
36
|
+
- [ ] Evidence captured by execution, never narrated
|
|
37
|
+
|
|
38
|
+
## Simplicity — "Reduce" (non-negotiable)
|
|
39
|
+
|
|
40
|
+
> Maeda's law 1 and Karpathy's "Simplicity First", made a deterministic gate.
|
|
41
|
+
> It is not CC by AST: they are measurable *proxies* over the diff. Measured by
|
|
42
|
+
> `qa_ledger.py simplicity-check`; an **OVERBUILT** verdict is a **BLOCKER** finding.
|
|
43
|
+
|
|
44
|
+
- [ ] Minimal code that solves what was asked — no unrequested features, layers or "flexibility" <!-- YAGNI / speculative generality -->
|
|
45
|
+
- [ ] No speculative abstractions — every new type/layer is justified against the SPEC <!-- YAGNI -->
|
|
46
|
+
- [ ] Bounded nesting — flatten with guard clauses / extract function <!-- CWE-1124 -->
|
|
47
|
+
- [ ] Change bounded to the budget (`defaults.simplicity`) — subtract before adding
|
|
48
|
+
- [ ] "Would a senior say this is overbuilt?" If yes, it is trimmed before converging
|
|
49
|
+
|
|
50
|
+
## Reuse — "REUSE-FIRST" (principle; the proxy ADVISES, gates if declared)
|
|
51
|
+
|
|
52
|
+
> Waste of duplication (Poppendieck ch. 4): AI code reinvents instead of reusing
|
|
53
|
+
> (GitClear *Maintainability Gap*: +81% duplication since 2023). `simplicity-check`
|
|
54
|
+
> does NOT see it — it scores the diff in ISOLATION, never against what already exists. Measured by
|
|
55
|
+
> `qa_ledger.py waste-check` (Type-1/2 clones of the diff vs the repo). The FACT —a block
|
|
56
|
+
> of 5+ lines already exists at `file:line`— is measured; the "wasteful" VERDICT is a
|
|
57
|
+
> heuristic with known false positives (boilerplate, DTOs, embedded SQL/JSON), so
|
|
58
|
+
> it **advises by default** and gates ONLY with `defaults.waste.gate: true` or `--gate`
|
|
59
|
+
> (provenance: the committed config IS the declaration). CWE-1041 / DRY.
|
|
60
|
+
|
|
61
|
+
- [ ] Don't reimplement what already exists in the repo — reuse before cloning
|
|
62
|
+
- [ ] Duplication within the change bounded — extract a helper before cloning
|
|
63
|
+
- [ ] A WASTEFUL, if the human declared the gate, is reused/refactored, not converged
|
|
64
|
+
|
|
65
|
+
## Effective tests — "coverage lies" (non-negotiable)
|
|
66
|
+
|
|
67
|
+
> Coverage says the line *ran*; not that a test *verifies* it. Effectiveness is
|
|
68
|
+
> measured with mutation testing (PIT): if mutating the code doesn't break the test, the test
|
|
69
|
+
> doesn't assert. Measured by `qa_ledger.py pit-check` over `mutations.xml`.
|
|
70
|
+
|
|
71
|
+
- [ ] Tests ASSERT behavior, not just execute code <!-- assertion-gap -->
|
|
72
|
+
- [ ] Test-strength above the gate (`--min-score`) in the touched area
|
|
73
|
+
- [ ] Zero live mutants in domain logic / critical path without explicit justification
|
|
74
|
+
- [ ] High coverage with live mutants = false safety; the gap is closed, not ignored
|
|
75
|
+
|
|
76
|
+
## Gate integrity — "don't game the meter" (non-negotiable)
|
|
77
|
+
|
|
78
|
+
> A maker-optimizer takes the cheapest path to "green", and editing the gate is usually
|
|
79
|
+
> the cheapest. The apparatus that measures correctness is NOT modified by the change that it measures,
|
|
80
|
+
> without explicit human sign-off. (Osmani lists the red-flags; the rule is the kit's synthesis.)
|
|
81
|
+
|
|
82
|
+
- [ ] The change does not weaken the gate: no deleting or skipping tests, no disabling lint, no lowering thresholds
|
|
83
|
+
- [ ] Mass rewrite of existing asserts = flag (the safety net edited to accept what is broken)
|
|
84
|
+
- [ ] No new helper duplicating an existing one <!-- reuse / Reduce -->
|
|
85
|
+
- [ ] High blast-radius: the checker is uncorrelated with the maker (another family/profile); the loop that produced the change is not its only approver
|
|
86
|
+
- [ ] Test diffs are read more strictly than production ones
|
|
87
|
+
|
|
88
|
+
## Golden — pre-change behavior (non-negotiable)
|
|
89
|
+
|
|
90
|
+
> **INV-GOLDEN-01.** In migrations/modernizations, no module enters the change phase without
|
|
91
|
+
> a golden suite captured and committed over its PRE-change behavior. The golden is captured by
|
|
92
|
+
> a script running the ORIGINAL code with real inputs; the agent **NEVER** generates or edits
|
|
93
|
+
> the `.approved` files — it is the one piece the agent cannot author, and that is its reason to exist.
|
|
94
|
+
> **CWE-440** (Expected Behavior Violation).
|
|
95
|
+
|
|
96
|
+
- [ ] Migration: golden suite captured + committed BEFORE touching the module
|
|
97
|
+
- [ ] The `.approved` files are field truth; approved by a HUMAN, never by the agent
|
|
98
|
+
- [ ] `golden-diff` clean (byte for byte) = hard closing condition of the touched module
|
|
99
|
+
- [ ] Corpus documented; a module with insufficient corpus = PARTIAL, never COVERS
|
|
100
|
+
|
|
101
|
+
## Anti-ceremony — Lean over the method itself (meta-invariant)
|
|
102
|
+
|
|
103
|
+
> The risk is not a bad gate: it is the **sum** of good gates turning `/dev-loop` into an
|
|
104
|
+
> audit. That is *over-processing* — the waste of ceremony (Poppendieck ch. 4). It applies to the
|
|
105
|
+
> tool, not the code: if a step does not add value **for the human**, it is waste.
|
|
106
|
+
> It is a **meta-invariant** — the criterion that EVERY future gate must pass before entering.
|
|
107
|
+
> Today only rule 3 is mechanized (the single verdict of `readiness`, kit 1.25.0); the
|
|
108
|
+
> rest is design discipline and review judgment, not something the engine checks.
|
|
109
|
+
|
|
110
|
+
- [ ] **Runs without the human typing anything** — a script/agent autocompletes it; no routine forms
|
|
111
|
+
- [ ] **Speaks only when it matters** — a failure, or a high-risk profile; if it always speaks, it is silenced by default
|
|
112
|
+
- [ ] **Collapses into `readiness`** — one number + one line, not another screen (`--verbose` opens the detail)
|
|
113
|
+
- [ ] **A trivial change skips it** — gated by risk profile <!-- principle: profiles A–E NOT yet mechanized in the engine -->
|
|
114
|
+
|
|
115
|
+
## How it is enforced
|
|
116
|
+
|
|
117
|
+
- `/discovery` and `/adr-refine` read it and derive the **severity gate** from here (the
|
|
118
|
+
"inviolable constraints" step). Each invariant carries, where it maps, a CWE reference.
|
|
119
|
+
- `/dev-loop` consults it before touching a governed area; a violation is recorded with
|
|
120
|
+
`qa_ledger.py flag-blocker --kind constitution --note "<invariant>"` and enters the ledger
|
|
121
|
+
as a **BLOCKER** finding (readiness cap ≤ 65, blocks convergence until `--resolve`).
|
|
122
|
+
Detecting it is the agent/human's obligation; once recorded, enforcement is the engine's.
|
|
123
|
+
- The **Simplicity** invariant is measured without human judgment: `qa_ledger.py simplicity-check`
|
|
124
|
+
scores the diff (minimality, nesting, abstraction) and returns `SIMPLE / ACCEPTABLE /
|
|
125
|
+
OVERBUILT`. **OVERBUILT** = BLOCKER (exit 1): it is trimmed, not converged.
|
|
126
|
+
- The **Reuse (REUSE-FIRST)** invariant is measured by `qa_ledger.py waste-check`: Type-1/2 clones
|
|
127
|
+
of the diff vs the repo (`dup_vs_repo` is the dominant signal). **Advisory by default** (advises
|
|
128
|
+
with `file:line` to reuse, exit 0); with `defaults.waste.gate: true` or `--gate` a
|
|
129
|
+
**WASTEFUL** is exit 1 and is persisted with `log-gate --kind waste --verdict fail` (readiness
|
|
130
|
+
cap ≤ 65, blocks convergence). An honest Type-1/2 proxy, never semantic nor by AST.
|
|
131
|
+
- The **Effective tests** invariant is measured with `qa_ledger.py pit-check`: if the mutation
|
|
132
|
+
score falls below the gate or mutants survive on the critical path, it is a **BLOCKER** finding —
|
|
133
|
+
green coverage is not enough. Expensive → *scheduled / incremental* tier, not in the inner loop.
|
|
134
|
+
- The **Gate integrity** invariant is measured by `qa_ledger.py gate-check`: deleted or
|
|
135
|
+
disabled tests and lowered thresholds = **BLOCKER** (exit 1); lint suppressions, removed
|
|
136
|
+
asserts and **new dependencies** (the "0 deps without approval" rule, made visible —
|
|
137
|
+
kit 1.30.0) = review (or `--strict`). For high blast-radius a checker
|
|
138
|
+
uncorrelated with the maker (different family/profile) is also required — that is process, not code.
|
|
139
|
+
- The **Golden (INV-GOLDEN-01)** invariant is measured by `qa_ledger.py golden-diff`: any `.received`
|
|
140
|
+
that does not match its `.approved` (or is unapproved) = **DIVERGE**, cutting the chain before judgment-day.
|
|
141
|
+
The agent does not touch `.approved` (ideally a `PreToolUse` hook makes it impossible).
|
|
142
|
+
- The **Anti-ceremony** meta-invariant is not measured by any subcommand: it is the admission filter
|
|
143
|
+
for new gates (does it autorun? does it stay quiet except when it matters? does it collapse into `readiness`? does a
|
|
144
|
+
trivial change skip it?). Its only mechanized leg today is the single verdict of `readiness` (kit
|
|
145
|
+
1.25.0): persisted gates are shown collapsed on one line by default and are opened with
|
|
146
|
+
`--verbose`. A gate that does not pass the four questions does not enter the kit — the reason is documented.
|
|
147
|
+
- **An ADR that contradicts the CONSTITUTION is not valid**: it is escalated, not approved. If a
|
|
148
|
+
decision would need to violate an invariant, changing the CONSTITUTION is discussed first
|
|
149
|
+
(an explicit human decision), never "worked around" silently.
|