@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.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +104 -0
  3. package/bin/README.md +6 -0
  4. package/bin/uscha.js +28 -0
  5. package/package.json +38 -0
  6. package/uscha-kit/.claude/skills/uscha-adr-refine/SKILL.md +161 -0
  7. package/uscha-kit/.claude/skills/uscha-characterize/SKILL.md +124 -0
  8. package/uscha-kit/.claude/skills/uscha-devloop/SKILL.md +490 -0
  9. package/uscha-kit/.claude/skills/uscha-devloop/qa_ledger.py +5602 -0
  10. package/uscha-kit/.claude/skills/uscha-discovery/SKILL.md +161 -0
  11. package/uscha-kit/.claude/skills/uscha-mirador/SKILL.md +145 -0
  12. package/uscha-kit/.claude/skills/uscha-mirador/mirador-render.py +112 -0
  13. package/uscha-kit/.claude/skills/uscha-mirador/mirador-watch.ps1 +22 -0
  14. package/uscha-kit/.claude/skills/uscha-mirador/mirador-watch.sh +26 -0
  15. package/uscha-kit/.claude/skills/uscha-mirador/mirador.template.html +586 -0
  16. package/uscha-kit/.claude/skills/uscha-mirador/telemetry-extract.py +130 -0
  17. package/uscha-kit/.claude/skills/uscha-reverse-discovery/SKILL.md +116 -0
  18. package/uscha-kit/.claude/skills/uscha-rubric/SKILL.md +79 -0
  19. package/uscha-kit/.claude/skills/uscha-sysdoc/SKILL.md +88 -0
  20. package/uscha-kit/.claude-plugin/plugin.json +24 -0
  21. package/uscha-kit/.codex-plugin/plugin.json +37 -0
  22. package/uscha-kit/CHANGELOG-1.10.0.md +84 -0
  23. package/uscha-kit/CHANGELOG-1.11.0.md +67 -0
  24. package/uscha-kit/CHANGELOG-1.12.0.md +46 -0
  25. package/uscha-kit/CHANGELOG-1.13.0.md +33 -0
  26. package/uscha-kit/CHANGELOG-1.14.0.md +42 -0
  27. package/uscha-kit/CHANGELOG-1.15.0.md +58 -0
  28. package/uscha-kit/CHANGELOG-1.16.0.md +55 -0
  29. package/uscha-kit/CHANGELOG-1.17.0.md +44 -0
  30. package/uscha-kit/CHANGELOG-1.18.0.md +42 -0
  31. package/uscha-kit/CHANGELOG-1.19.0.md +41 -0
  32. package/uscha-kit/CHANGELOG-1.2.2.md +16 -0
  33. package/uscha-kit/CHANGELOG-1.2.3.md +20 -0
  34. package/uscha-kit/CHANGELOG-1.2.4.md +10 -0
  35. package/uscha-kit/CHANGELOG-1.2.5.md +23 -0
  36. package/uscha-kit/CHANGELOG-1.2.6.md +11 -0
  37. package/uscha-kit/CHANGELOG-1.2.7.md +15 -0
  38. package/uscha-kit/CHANGELOG-1.2.8.md +24 -0
  39. package/uscha-kit/CHANGELOG-1.2.9.md +4 -0
  40. package/uscha-kit/CHANGELOG-1.20.0.md +29 -0
  41. package/uscha-kit/CHANGELOG-1.21.0.md +33 -0
  42. package/uscha-kit/CHANGELOG-1.22.0.md +60 -0
  43. package/uscha-kit/CHANGELOG-1.23.0.md +75 -0
  44. package/uscha-kit/CHANGELOG-1.24.0.md +50 -0
  45. package/uscha-kit/CHANGELOG-1.25.0.md +55 -0
  46. package/uscha-kit/CHANGELOG-1.26.0.md +70 -0
  47. package/uscha-kit/CHANGELOG-1.27.0.md +45 -0
  48. package/uscha-kit/CHANGELOG-1.28.0.md +35 -0
  49. package/uscha-kit/CHANGELOG-1.29.0.md +20 -0
  50. package/uscha-kit/CHANGELOG-1.3.0.md +74 -0
  51. package/uscha-kit/CHANGELOG-1.30.0.md +46 -0
  52. package/uscha-kit/CHANGELOG-1.31.0.md +59 -0
  53. package/uscha-kit/CHANGELOG-1.32.0.md +50 -0
  54. package/uscha-kit/CHANGELOG-1.33.0.md +46 -0
  55. package/uscha-kit/CHANGELOG-1.34.0.md +55 -0
  56. package/uscha-kit/CHANGELOG-1.35.0.md +30 -0
  57. package/uscha-kit/CHANGELOG-1.36.0.md +33 -0
  58. package/uscha-kit/CHANGELOG-1.37.0.md +41 -0
  59. package/uscha-kit/CHANGELOG-1.38.0.md +11 -0
  60. package/uscha-kit/CHANGELOG-1.39.0.md +14 -0
  61. package/uscha-kit/CHANGELOG-1.4.0.md +68 -0
  62. package/uscha-kit/CHANGELOG-1.40.0.md +16 -0
  63. package/uscha-kit/CHANGELOG-1.40.1.md +11 -0
  64. package/uscha-kit/CHANGELOG-1.5.0.md +64 -0
  65. package/uscha-kit/CHANGELOG-1.6.0.md +57 -0
  66. package/uscha-kit/CHANGELOG-1.7.0.md +74 -0
  67. package/uscha-kit/CHANGELOG-1.8.0.md +46 -0
  68. package/uscha-kit/CHANGELOG-1.9.0.md +112 -0
  69. package/uscha-kit/LICENSE +21 -0
  70. package/uscha-kit/README.md +497 -0
  71. package/uscha-kit/VERSION +1 -0
  72. package/uscha-kit/WORKBENCH.md +178 -0
  73. package/uscha-kit/hooks/block-approved-writes.ps1 +46 -0
  74. package/uscha-kit/hooks/hooks.json +15 -0
  75. package/uscha-kit/install-uscha.py +344 -0
  76. package/uscha-kit/skills/uscha-adr-refine/SKILL.md +161 -0
  77. package/uscha-kit/skills/uscha-characterize/SKILL.md +124 -0
  78. package/uscha-kit/skills/uscha-devloop/SKILL.md +490 -0
  79. package/uscha-kit/skills/uscha-devloop/qa_ledger.py +5602 -0
  80. package/uscha-kit/skills/uscha-discovery/SKILL.md +161 -0
  81. package/uscha-kit/skills/uscha-mirador/SKILL.md +145 -0
  82. package/uscha-kit/skills/uscha-mirador/mirador-render.py +112 -0
  83. package/uscha-kit/skills/uscha-mirador/mirador-watch.ps1 +22 -0
  84. package/uscha-kit/skills/uscha-mirador/mirador-watch.sh +26 -0
  85. package/uscha-kit/skills/uscha-mirador/mirador.template.html +586 -0
  86. package/uscha-kit/skills/uscha-mirador/telemetry-extract.py +130 -0
  87. package/uscha-kit/skills/uscha-reverse-discovery/SKILL.md +116 -0
  88. package/uscha-kit/skills/uscha-rubric/SKILL.md +79 -0
  89. package/uscha-kit/skills/uscha-sysdoc/SKILL.md +88 -0
  90. package/uscha-kit/templates/.gitattributes +6 -0
  91. package/uscha-kit/templates/CLAUDE.md +56 -0
  92. package/uscha-kit/templates/CONSTITUTION.md +149 -0
  93. package/uscha-kit/templates/RUBRIC.md +38 -0
  94. package/uscha-kit/templates/docs/adr/README.md +19 -0
  95. package/uscha-kit/templates/rubric-grader-prompt.md +63 -0
  96. package/uscha-kit/tests/smoke-engine.sh +1739 -0
  97. package/uscha-kit/uscha.config.json +181 -0
  98. 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,6 @@
1
+ # Golden / approval fixtures: treat as binary so line-ending normalization can NEVER
2
+ # create false diffs (critical on Windows / SQL Server). See INV-GOLDEN-01.
3
+ *.approved.* binary
4
+ *.received.* binary
5
+ *.approved binary
6
+ *.received binary
@@ -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.