@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,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
|
+
"name": "uscha",
|
|
4
|
+
"version": "1.40.1",
|
|
5
|
+
"displayName": "Uscha",
|
|
6
|
+
"description": "Spec-driven development for LLM coding agents: 8 skills (discovery, adr-refine, reverse-discovery, characterize, devloop, sysdoc, rubric, mirador) + a stdlib measurement engine (qa_ledger.py, 29 subcommands + universal installer + npm/npx router). Facts block, guesses advise; the human approves.",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Andres Massello",
|
|
9
|
+
"url": "https://github.com/andresmassello"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/andresmassello/uscha",
|
|
12
|
+
"repository": "https://github.com/andresmassello/uscha",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"spec-driven",
|
|
16
|
+
"qa",
|
|
17
|
+
"gates",
|
|
18
|
+
"golden-testing",
|
|
19
|
+
"readiness",
|
|
20
|
+
"methodology"
|
|
21
|
+
],
|
|
22
|
+
"skills": "./.claude/skills/",
|
|
23
|
+
"hooks": "./hooks/hooks.json"
|
|
24
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "uscha",
|
|
3
|
+
"version": "1.40.1",
|
|
4
|
+
"description": "Uscha spec-driven development methodology for coding agents. Includes npm/npx router.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Andres Massello",
|
|
7
|
+
"url": "https://github.com/andresmassello"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/andresmassello/uscha",
|
|
10
|
+
"repository": "https://github.com/andresmassello/uscha",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"spec-driven",
|
|
14
|
+
"qa",
|
|
15
|
+
"gates",
|
|
16
|
+
"golden-testing",
|
|
17
|
+
"readiness"
|
|
18
|
+
],
|
|
19
|
+
"skills": "./skills/",
|
|
20
|
+
"interface": {
|
|
21
|
+
"displayName": "Uscha",
|
|
22
|
+
"shortDescription": "Spec-driven development with fact gates and readiness.",
|
|
23
|
+
"longDescription": "Uscha installs discovery, ADR, characterization, devloop, rubric, sysdoc and Mirador skills plus the qa_ledger.py evidence engine.",
|
|
24
|
+
"developerName": "Andres Massello",
|
|
25
|
+
"category": "Productivity",
|
|
26
|
+
"capabilities": [
|
|
27
|
+
"Write",
|
|
28
|
+
"Interactive"
|
|
29
|
+
],
|
|
30
|
+
"defaultPrompt": [
|
|
31
|
+
"Run Uscha discovery for this feature.",
|
|
32
|
+
"Use Uscha devloop to verify this change.",
|
|
33
|
+
"Show the Uscha readiness for this repo."
|
|
34
|
+
],
|
|
35
|
+
"brandColor": "#7C3AED"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# dev-loop-kit 1.10.0 — acceptance trazable: AC-n cierra por testcase MEDIDO (2026-07-02)
|
|
2
|
+
|
|
3
|
+
Primera mejora del backlog PragProg (M2 de `docs/analisis-pragmatic-programmer.md`;
|
|
4
|
+
Topic 50 "Do What Works" + la anécdota Jeffries/Sudoku + Tip 94 "Find Bugs Once").
|
|
5
|
+
Ataca el modo de falla típico del agente: **pulir la métrica sin acercarse a la
|
|
6
|
+
solución**. El readiness deja de estar dominado por coverage/tests-verdes y pasa a
|
|
7
|
+
estar dominado por **criterios de aceptación cerrados con evidencia medida**.
|
|
8
|
+
Smoke suite: 68/68.
|
|
9
|
+
|
|
10
|
+
## La idea (measured beats narrated, ahora a nivel CRITERIO)
|
|
11
|
+
|
|
12
|
+
- Cada criterio de `ACCEPTANCE.md` lleva un ID estable: `- [ ] AC-01 — cuando X
|
|
13
|
+
entonces Y`.
|
|
14
|
+
- Un criterio cierra **MEDIDO** solo cuando existe ≥1 testcase VERDE cuyo nombre
|
|
15
|
+
lleva el tag (`test_ac1_x`, `testAC01X`, `"AC-01: ..."`) en los reportes JUnit
|
|
16
|
+
que el engine ya ingiere — y **ningún** testcase taggeado en rojo (evidencia
|
|
17
|
+
roja veta: fail-closed).
|
|
18
|
+
- El checkbox es RELATO; el testcase es HECHO. Un `[x]` sin test verde se
|
|
19
|
+
reporta como `narrated_only` y NO cierra.
|
|
20
|
+
|
|
21
|
+
## Engine (qa_ledger.py)
|
|
22
|
+
|
|
23
|
+
- `_parse_acceptance_items()`: parser de checkboxes con ID opcional; IDs
|
|
24
|
+
normalizados por número (`AC-01 == AC_1 == ac1` — los nombres de test de
|
|
25
|
+
python/go no admiten `-`).
|
|
26
|
+
- `_ac_tags()`: scan de NOMBRES de testcase en los reportes JUnit por type
|
|
27
|
+
(reusa el selector de ubicaciones vía `_junit_report_files()`, extraído para
|
|
28
|
+
no duplicar la lista — surefire/gradle per-clase, junit-family, dual-file
|
|
29
|
+
swift; flutter no emite JUnit → sus criterios no cierran medido, documentado).
|
|
30
|
+
Boundaries explícitos en el regex del tag: `\b` NO sirve (`_` es word char y
|
|
31
|
+
`test_ac1` quedaría invisible); soporta separador no-alfanumérico y camelCase.
|
|
32
|
+
- `readiness`: nueva dimensión **acceptance** (dominante, peso 30) = criterios
|
|
33
|
+
cerrados medidos / criterios totales (un criterio sin ID no puede cerrar →
|
|
34
|
+
cuenta como abierto). Pesos default rebalanceados:
|
|
35
|
+
acceptance 30 · adr 15 · coverage 15 · static 20 · convergencia 10 ·
|
|
36
|
+
integración 10 — el techo a coverage/verde es el anti-Goodhart. JSON expone
|
|
37
|
+
`traceable/ids/measured_closed/narrated_only/measured_unchecked/untagged`;
|
|
38
|
+
warnings en texto para narrated-only y sin-IDs.
|
|
39
|
+
- **Fallback legacy**: ACCEPTANCE sin ningún AC-ID → la dimensión cae al ratio
|
|
40
|
+
de checkboxes con warning (adopción incremental, no rotura retroactiva).
|
|
41
|
+
- `spec-check --acceptance ACCEPTANCE.md`: la trazabilidad es estructura =
|
|
42
|
+
FACT → bloquea: archivo ausente, cero criterios, CERO criterios trazables,
|
|
43
|
+
IDs duplicados (normalizados). Criterios sueltos sin ID = advisory. Puede
|
|
44
|
+
correr solo (sin `--spec`).
|
|
45
|
+
|
|
46
|
+
## Skills / docs
|
|
47
|
+
|
|
48
|
+
- `discovery`: ACCEPTANCE se genera con AC-NN secuenciales, nunca reusados;
|
|
49
|
+
cada criterio pensado para ser cubrible por un test con nombre.
|
|
50
|
+
- `dev-loop`: al escribir los tests de un criterio, el tag AC-n va en el nombre
|
|
51
|
+
del test; `spec-check --acceptance` al arrancar.
|
|
52
|
+
|
|
53
|
+
## Hardening (review fresco pre-commit, 10 hallazgos aplicados)
|
|
54
|
+
|
|
55
|
+
- `_ac_tags`: el tag ahora lee SOLO el nombre del testcase (nunca classname) —
|
|
56
|
+
un módulo/clase que matchea "ACn" por coincidencia (`test_ac3_flow.py`) ya no
|
|
57
|
+
contamina los OTROS tests del mismo archivo.
|
|
58
|
+
- `_AC_ID`: tolera IDs markdown-formateados (`**AC-01**`, `` `AC-01` ``) — antes
|
|
59
|
+
degradaban en silencio a `id=None` y toda la trazabilidad caía a legacy.
|
|
60
|
+
- `readiness`: IDs duplicados en ACCEPTANCE cuentan **una sola vez** (antes un
|
|
61
|
+
test verde podía cerrar "medido" tantos criterios como copias del ID).
|
|
62
|
+
- `readiness`: config pre-1.10.0 con `readiness_weights` explícitos que no
|
|
63
|
+
conocían `acceptance` ya no la reciben inyectada por default — se excluye
|
|
64
|
+
(peso 0) con warning hasta que el usuario la agregue o taggee AC-IDs (si no,
|
|
65
|
+
duplicaba el peso de `adr` en silencio).
|
|
66
|
+
- `readiness`: `--section` sin match ahora avisa (`0 criterios en scope`) en
|
|
67
|
+
vez de zonear en silencio adr+acceptance.
|
|
68
|
+
- `readiness`: ledger sin `config.repos` ya no crashea (KeyError) — usa
|
|
69
|
+
`.get("repos", [])` como el resto del comando.
|
|
70
|
+
- `spec-check --acceptance`: pipear un SPEC por stdin junto con `--acceptance`
|
|
71
|
+
ya no se descarta en silencio — se lee stdin salvo modo interactivo puro
|
|
72
|
+
acceptance-only.
|
|
73
|
+
- `spec-check --acceptance --strict`: los criterios sin AC-ID ahora gatean
|
|
74
|
+
`--strict` (antes el verdict imprimía "OK" con advisories pendientes).
|
|
75
|
+
- Documentado (no resuelto): reportes JUnit stale de maven/gradle pueden
|
|
76
|
+
vetear/cerrar un AC sin evidencia vigente — mismo límite que
|
|
77
|
+
`junit_test_count`, ahora con blast radius mayor. Mitigación real (mtime +
|
|
78
|
+
correlación con el árbol de fuentes) diferida.
|
|
79
|
+
|
|
80
|
+
## Diferido consciente
|
|
81
|
+
|
|
82
|
+
- El resto del backlog PragProg (M1 regression-capture, M3 ledger atómico,
|
|
83
|
+
M8 secret-scan, M9 tests fuera del presupuesto de simplicity, etc.) sigue en
|
|
84
|
+
`docs/analisis-pragmatic-programmer.md` — una mejora por release.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# dev-loop-kit 1.11.0 — tests fuera del presupuesto de simplicity (2026-07-03)
|
|
2
|
+
|
|
3
|
+
Segunda mejora del backlog PragProg (M9 de `docs/analisis-pragmatic-programmer.md`;
|
|
4
|
+
Topic 51: *"un buen proyecto puede tener MÁS código de test que de producción, y
|
|
5
|
+
vale la pena"*). Elimina un **incentivo perverso activo**: el simplicity-check
|
|
6
|
+
contaba las líneas de test junto a las de producción contra un único presupuesto —
|
|
7
|
+
el gate castigaba escribir tests y empujaba al agente a testear menos para pasar.
|
|
8
|
+
Smoke suite: 73/73.
|
|
9
|
+
|
|
10
|
+
## La idea
|
|
11
|
+
|
|
12
|
+
- Escribir tests **nunca** acerca un diff a OVERBUILT. Los archivos de test se
|
|
13
|
+
detectan, se cuentan y se **reportan aparte** (`test_lines_added`,
|
|
14
|
+
`test_files_changed`) — pero no gatean ninguna dimensión del score.
|
|
15
|
+
- La otra dirección ya estaba protegida: **borrar** tests lo bloquea gate-check.
|
|
16
|
+
Con esto el incentivo queda alineado en ambas direcciones.
|
|
17
|
+
|
|
18
|
+
## Engine (qa_ledger.py)
|
|
19
|
+
|
|
20
|
+
- `_is_simplicity_test_file()`: clasificador type-agnóstico (el diff no trae
|
|
21
|
+
`repo_type`) — unión de las convenciones de los 9 stacks: dirs
|
|
22
|
+
`test/tests/__tests__/Tests/*.Tests`, source sets Gradle (`src/*Test/`),
|
|
23
|
+
`test_*.py`, `*_test.go`, `*.test.ts`/`*.spec.js` (multi-dot incluido),
|
|
24
|
+
`*Test.java`/`*Tests.cs` CamelCase **case-sensitive** — `backtest.cpp` /
|
|
25
|
+
`protest.cc` siguen contando como producción (misma trampa que ya evitan
|
|
26
|
+
dotnet/cpp en `_is_test_path`).
|
|
27
|
+
- Dirección de fallo benigna y documentada: un falso positivo solo EXIME del
|
|
28
|
+
presupuesto — nunca bloquea ni borra nada.
|
|
29
|
+
- `_simplicity_metrics()`: tercer estado de conteo (`prod`/`test`/fuera);
|
|
30
|
+
las líneas de test no alimentan `lines_added`, `net_lines`, `files_changed`,
|
|
31
|
+
`max_nesting`, `max_hunk_added` ni abstracciones. Output humano: línea
|
|
32
|
+
informativa "tests FUERA del presupuesto: +N líneas en M archivo(s)".
|
|
33
|
+
|
|
34
|
+
## Smoke
|
|
35
|
+
|
|
36
|
+
- **T32**: diff sintético 6 líneas prod + 302 de test → el presupuesto ve 6/1;
|
|
37
|
+
batería del clasificador (9 convenciones positivas + backtest/protest/Engine
|
|
38
|
+
negativas).
|
|
39
|
+
- **T31** (edges 1.10.0, deuda del release anterior): batería de falsos
|
|
40
|
+
positivos del tag regex (`HVAC2`, `mac1`, `track12` no taggean), classname
|
|
41
|
+
jamás taggea, y semántica flaky de surefire (`<flakyFailure>` que pasó tras
|
|
42
|
+
retry = verde; `<failure>`+`<rerunFailure>` = rojo, veta).
|
|
43
|
+
|
|
44
|
+
## Hardening (review fresco pre-commit)
|
|
45
|
+
|
|
46
|
+
- El review detectó que gate-check tenía SU PROPIO clasificador de tests
|
|
47
|
+
(`_gc_is_test_file`) más débil: no reconocía `foo_test.go` (Go),
|
|
48
|
+
`*.Tests/*.cs` (dotnet), `*.spec.tsx` ni `__tests__/` — así que "borrar
|
|
49
|
+
tests lo bloquea gate-check" era overclaim para 4+ stacks. Fix: unión
|
|
50
|
+
fail-closed — gate-check reusa el clasificador compartido de los 9 stacks
|
|
51
|
+
MÁS sus sufijos legacy; solo se AMPLÍA qué cuenta como test, ningún path
|
|
52
|
+
antes protegido se desprotege.
|
|
53
|
+
- `_GC_TESTDEF` ampliado con las definiciones de test que faltaban:
|
|
54
|
+
`func TestX` (Go), `[Fact]`/`[Theory]` (xunit), `#[test]` (rust),
|
|
55
|
+
`it(`/`test(`/`describe(` (js) — seguro porque TESTDEF solo se evalúa
|
|
56
|
+
dentro de archivos ya clasificados como test.
|
|
57
|
+
- Smoke **T33**: borrado de tests Go/dotnet/JS → BLOCKER (antes invisible).
|
|
58
|
+
- Corrección truth-pass en `dev-loop-kit/README.md`: los pesos documentados
|
|
59
|
+
de simplicity (`diff_size 30, nesting 25, abstraction 20...`) no coincidían
|
|
60
|
+
con el engine (`35/30/20/8/7`, abstraction advisory sin peso) — drift
|
|
61
|
+
pre-existente, alineado acá.
|
|
62
|
+
|
|
63
|
+
## Diferido consciente
|
|
64
|
+
|
|
65
|
+
- El resto del backlog PragProg (M1 regression-capture, M3 ledger atómico,
|
|
66
|
+
M8 secret-scan, etc.) sigue en `docs/analisis-pragmatic-programmer.md` —
|
|
67
|
+
una mejora por release.
|