@andresmassello/uscha 1.61.0 → 1.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -40,7 +40,7 @@ Requires **Python 3.8+** on the machine (the engine is Python stdlib — no pip
40
40
  runtime dependencies). The npm package is a thin router; the canonical installer is
41
41
  `uscha-kit/install-uscha.py`.
42
42
 
43
- **Kit v1.61.0** <!-- uscha:version --> · [uscha.dev](https://uscha.dev) ·
43
+ **Kit v1.63.0** <!-- uscha:version --> · [uscha.dev](https://uscha.dev) ·
44
44
  [changelog](https://github.com/andresmassello/uscha/blob/main/uscha-kit/CHANGELOG.md)
45
45
  (the per-release changelogs live in the repo, not in the npm tarball)
46
46
 
@@ -76,7 +76,7 @@ and see which file, which test, and when.
76
76
  | `/uscha-mirador` | Bird's-eye HTML dashboard: readiness, trail, acceptance, loops |
77
77
  | `/uscha-status` | One-line progress readout, in chat |
78
78
 
79
- **A measurement engine** (`qa_ledger.py`, 32 subcommands, Python stdlib) that ingests
79
+ **A measurement engine** (`qa_ledger.py`, 33 subcommands, Python stdlib) that ingests
80
80
  evidence from **11 language stacks** — maven, gradle, ant, python, node, go, rust, dotnet,
81
81
  cpp, swift, flutter — and computes a readiness score with hard caps and visible provenance.
82
82
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andresmassello/uscha",
3
- "version": "1.61.0",
3
+ "version": "1.63.0",
4
4
  "description": "Spec-driven development for LLM coding agents: 9 skills + a stdlib evidence engine. Facts block, guesses advise; the human approves.",
5
5
  "author": {
6
6
  "name": "Andres Massello",
@@ -2629,6 +2629,35 @@ def _derive_phase(ledger, name, node, k, qa_order):
2629
2629
  reasons.append("la evidencia medida no contiene tests ejecutados")
2630
2630
  _go, sev = _gate_open_and_sev(node)
2631
2631
  blk = sev.get("BLOCKER", 0) + sev.get("CRITICAL", 0)
2632
+ # clean-room gate (ADR-008). OPT-IN: no clean_room block, or mode "off", and this does not
2633
+ # exist -- behavior identical to earlier releases. Declared "final": pr-ready additionally
2634
+ # requires clean-room evidence that is GREEN and pinned to the CURRENT HEAD. A new commit
2635
+ # makes the previous run stale for the gate, the same staleness posture the rest of the
2636
+ # engine takes: evidence certifies the thing it was measured against, nothing later.
2637
+ _cr = _cr_cfg(ledger)
2638
+ if _cr and _cr.get("mode") == "final":
2639
+ _head = None
2640
+ _hr = None
2641
+ _crcfg = (_repo_cfg(ledger, name) if name != "integration"
2642
+ else {"path": "."}) # synthetic scope: never in config["repos"]
2643
+ try:
2644
+ _hr = subprocess.run(["git", "rev-parse", "HEAD"],
2645
+ cwd=_crcfg.get("path", "."),
2646
+ capture_output=True, text=True, encoding="utf-8",
2647
+ errors="replace")
2648
+ except OSError:
2649
+ _hr = None
2650
+ if _hr is not None and _hr.returncode == 0:
2651
+ _head = _hr.stdout.strip()
2652
+ _run = _cr_latest(ledger, name, _head) if _head else None
2653
+ if not _head:
2654
+ reasons.append("clean-room declarado pero no se pudo resolver HEAD "
2655
+ "(no se mide, no se aprueba)")
2656
+ conv = False
2657
+ elif not _run or not _run.get("ok"):
2658
+ reasons.append("falta clean-room verde para %s (evidencia del arbol no "
2659
+ "certifica el commit)" % _head[:8])
2660
+ conv = False
2632
2661
  if conv and tests_measured_green and not tests_red and blk == 0:
2633
2662
  evidence = ["ciclo de agente limpio", "tests verdes (medidos)",
2634
2663
  "0 BLOCKER/CRITICAL abiertos"]
@@ -3026,7 +3055,7 @@ def _sd_governs(path):
3026
3055
  return None
3027
3056
  if not lines or lines[0].strip() != "---":
3028
3057
  return None
3029
- globs, in_governs = None, False
3058
+ globs, in_governs, explicit_empty = None, False, False
3030
3059
  # scan runs to the CLOSING fence, not an arbitrary window -- a governs: key late in a
3031
3060
  # long frontmatter block must not silently read as UNMAPPED (fresh-review finding).
3032
3061
  for ln in lines[1:]:
@@ -3038,6 +3067,8 @@ def _sd_governs(path):
3038
3067
  if rest.startswith("[") and rest.endswith("]"):
3039
3068
  globs = [x.strip().strip("\x27\x22")
3040
3069
  for x in rest[1:-1].split(",") if x.strip()]
3070
+ # only an INLINE [] is a declaration of "nothing to govern"
3071
+ explicit_empty = not globs
3041
3072
  in_governs = False
3042
3073
  elif rest:
3043
3074
  # bare scalar (`governs: src/**`) -- a plausible authoring shorthand;
@@ -3051,6 +3082,11 @@ def _sd_governs(path):
3051
3082
  globs.append(s[2:].strip().strip("\x27\x22"))
3052
3083
  elif s and not ln.startswith((" ", "\t")):
3053
3084
  in_governs = False
3085
+ if globs == [] and not explicit_empty:
3086
+ # a `governs:` key with nothing usable under it (a placeholder, a comment, a typo) is
3087
+ # an UNFINISHED declaration, not a statement that this spec governs nothing. Report it
3088
+ # as UNMAPPED, which is what it is (fresh-review finding).
3089
+ return None
3054
3090
  return globs
3055
3091
 
3056
3092
 
@@ -3111,6 +3147,16 @@ def cmd_spec_drift(args):
3111
3147
  row.update({"verdict": "UNMAPPED", "reason": "no governs: frontmatter"})
3112
3148
  results.append(row)
3113
3149
  continue
3150
+ if not governs:
3151
+ # An EXPLICIT empty list is a declaration, not an omission: this decision governs
3152
+ # no code and never will. Negative ADRs ("we are NOT doing X, and why") are a
3153
+ # documented practice in this kit, and reporting them UNMAPPED forever turns a
3154
+ # correct state into permanent noise -- which is how an advisory gets ignored.
3155
+ # Found by running spec-drift on this repo's own ADR-004.
3156
+ row.update({"verdict": "NO-CODE",
3157
+ "reason": "declares governs: [] -- a decision that governs no code"})
3158
+ results.append(row)
3159
+ continue
3114
3160
  matched = []
3115
3161
  pats = [_fp_glob_re(g) for g in governs]
3116
3162
  for f in tracked:
@@ -3171,7 +3217,8 @@ def cmd_spec_drift(args):
3171
3217
  print("SPEC-DRIFT %s (advisory, lag > %dd):" % (args.repo, lag_days))
3172
3218
  if not results:
3173
3219
  print(" no spec documents found (SPEC.md / docs/adr/*.md)")
3174
- mark = {"SPEC_STALE": "!!", "CLEAN": "ok", "UNMAPPED": "--", "UNTRACKED": "--"}
3220
+ mark = {"SPEC_STALE": "!!", "CLEAN": "ok", "UNMAPPED": "--", "UNTRACKED": "--",
3221
+ "NO-CODE": "ok"}
3175
3222
  for r_ in results:
3176
3223
  line = " %s %s: %s" % (mark.get(r_["verdict"], "??"), r_["file"],
3177
3224
  r_["verdict"])
@@ -3337,6 +3384,132 @@ def cmd_golden_coverage(args):
3337
3384
 
3338
3385
 
3339
3386
 
3387
+ # --------------------------------------------------------------------------- #
3388
+ # cleanroom (ADR-008: verify the COMMIT, not the tree -- opt-in, human-supplied command)
3389
+ # --------------------------------------------------------------------------- #
3390
+
3391
+ CLEAN_ROOM_KEY = "clean_room"
3392
+
3393
+
3394
+ def _cr_cfg(ledger):
3395
+ cfg = ledger["config"].get("defaults", {}).get(CLEAN_ROOM_KEY)
3396
+ return cfg if isinstance(cfg, dict) else None
3397
+
3398
+
3399
+ def _cr_latest(ledger, repo, ref=None):
3400
+ """Latest clean-room record for a repo, optionally pinned to one ref."""
3401
+ runs = [e for e in ledger.get(CLEAN_ROOM_KEY, [])
3402
+ if e.get("repo") == repo and (ref is None or e.get("ref") == ref)]
3403
+ return runs[-1] if runs else None
3404
+
3405
+
3406
+ def cmd_cleanroom(args):
3407
+ """Run a command against a CLEAN CHECKOUT of one commit, in a throwaway worktree.
3408
+
3409
+ Evidence produced in the maker's tree is true of the TREE; this produces evidence true
3410
+ of the COMMIT. As a side effect maker != checker becomes physical: the worktree cannot
3411
+ see uncommitted state.
3412
+
3413
+ The engine does NOT decide what to run. The command arrives explicitly via --run, the
3414
+ same contract golden-coverage uses for --harness: the engine owns what it can guarantee
3415
+ (isolation, the SHA binding, cleanup) and never guesses what a project's suite is.
3416
+ Reading test_command_* from config and executing it would make the engine an executor
3417
+ of config-supplied shell, which it is not (ADR-008)."""
3418
+ import time
3419
+ ledger = _load(args.ledger)
3420
+ _repo_node(ledger, args.repo)
3421
+ repo_path = _repo_cfg(ledger, args.repo).get("path", ".")
3422
+
3423
+ def git(*a, **kw):
3424
+ cwd = kw.pop("cwd", repo_path)
3425
+ try:
3426
+ return subprocess.run(["git"] + list(a), cwd=cwd, capture_output=True,
3427
+ text=True, encoding="utf-8", errors="replace")
3428
+ except OSError:
3429
+ return None
3430
+
3431
+ rev = git("rev-parse", "--verify", (args.ref or "HEAD") + "^{commit}")
3432
+ if rev is None or rev.returncode != 0 or not rev.stdout.strip():
3433
+ print("[qa_ledger] cannot resolve ref %r in %s - nothing to verify."
3434
+ % (args.ref or "HEAD", repo_path), file=sys.stderr)
3435
+ sys.exit(2)
3436
+ sha = rev.stdout.strip()
3437
+
3438
+ wt = tempfile.mkdtemp(prefix="uscha-cleanroom-")
3439
+ target = os.path.join(wt, "tree")
3440
+ started = time.time()
3441
+ record = {"repo": args.repo, "ref": sha, "at": _now(), "ok": False,
3442
+ "status": None, "wall_ms": None, "worktree_sha": None}
3443
+ keep = bool((_cr_cfg(ledger) or {}).get("keep_worktree_on_failure"))
3444
+ try:
3445
+ add = git("worktree", "add", "--detach", target, sha)
3446
+ if add is None or add.returncode != 0:
3447
+ record["status"] = "WORKTREE_FAILED"
3448
+ print("[qa_ledger] git worktree add failed:\n%s"
3449
+ % ((add.stderr if add else "git unavailable") or "")[-800:], file=sys.stderr)
3450
+ else:
3451
+ # a worktree of a commit must be clean by construction; if it is not, something
3452
+ # (a smudge filter, a hook, a stale index) intervened and the isolation claim is
3453
+ # already false. Say so rather than measure in it.
3454
+ st = git("status", "--porcelain", cwd=target)
3455
+ if st is None or st.returncode != 0 or st.stdout.strip():
3456
+ record["status"] = "WORKTREE_DIRTY"
3457
+ else:
3458
+ record["worktree_sha"] = sha
3459
+ if args.setup:
3460
+ r = subprocess.run(args.setup, cwd=target, shell=True,
3461
+ capture_output=True, text=True,
3462
+ encoding="utf-8", errors="replace")
3463
+ if r.returncode != 0:
3464
+ record["status"] = "SETUP_FAILED"
3465
+ print((r.stderr or "")[-800:], file=sys.stderr)
3466
+ if record["status"] is None:
3467
+ r = subprocess.run(args.run, cwd=target, shell=True,
3468
+ capture_output=True, text=True,
3469
+ encoding="utf-8", errors="replace")
3470
+ record["exit_code"] = r.returncode
3471
+ record["status"] = "GREEN" if r.returncode == 0 else "RED"
3472
+ record["ok"] = r.returncode == 0
3473
+ if r.returncode != 0:
3474
+ print((r.stdout or "")[-1500:], file=sys.stderr)
3475
+ finally:
3476
+ record["wall_ms"] = int((time.time() - started) * 1000)
3477
+ # Cleanup is unconditional unless the human asked to inspect a FAILURE: a zombie
3478
+ # worktree is a defect, and `git worktree remove` alone leaves the admin entry behind.
3479
+ if record["ok"] or not keep:
3480
+ git("worktree", "remove", "--force", target)
3481
+ git("worktree", "prune")
3482
+ shutil.rmtree(wt, ignore_errors=True)
3483
+ # VERIFY the removal instead of assuming it. rmtree(ignore_errors=True) and a
3484
+ # discarded git return code can both fail in silence -- typically on Windows,
3485
+ # where a handle the caller's command left open blocks removal.
3486
+ if os.path.exists(target):
3487
+ record["cleanup_failed"] = True
3488
+ record["worktree_kept_at"] = target
3489
+ print("[qa_ledger] WARNING: the clean-room worktree could not be removed and "
3490
+ "is still at %s (a process may still hold a handle). Remove it with: "
3491
+ "git worktree remove --force %s && git worktree prune"
3492
+ % (target, target), file=sys.stderr)
3493
+ else:
3494
+ record["worktree_kept_at"] = target
3495
+
3496
+ ledger.setdefault(CLEAN_ROOM_KEY, []).append(record)
3497
+ ledger["step_counter"] += 1
3498
+ ledger["steps"].append({"n": ledger["step_counter"], "at": _now(),
3499
+ "kind": "cleanroom", "repo": args.repo})
3500
+ _save(args.ledger, ledger)
3501
+
3502
+ if args.json:
3503
+ print(json.dumps(record, indent=2, ensure_ascii=False))
3504
+ else:
3505
+ print("CLEANROOM %s @ %s: %s (%.1fs)"
3506
+ % (args.repo, sha[:8], record["status"], (record["wall_ms"] or 0) / 1000.0))
3507
+ if record.get("worktree_kept_at"):
3508
+ print(" worktree kept for inspection: " + record["worktree_kept_at"])
3509
+ sys.exit(0 if record["ok"] else 1)
3510
+
3511
+
3512
+
3340
3513
  def cmd_escalate(args):
3341
3514
  ledger = _load(args.ledger)
3342
3515
  _repo_node(ledger, args.repo)
@@ -4383,6 +4556,9 @@ def cmd_dashboard(args):
4383
4556
  _org[_rn] = _snaps[-1]["origin"]
4384
4557
  if _org:
4385
4558
  out["evidence_origin"] = _org
4559
+ if ledger.get(CLEAN_ROOM_KEY):
4560
+ out["clean_room"] = {r: [e for e in ledger[CLEAN_ROOM_KEY] if e.get("repo") == r][-1]
4561
+ for r in {e.get("repo") for e in ledger[CLEAN_ROOM_KEY]}}
4386
4562
  if getattr(args, "json", False):
4387
4563
  print(json.dumps(out, indent=2, ensure_ascii=False))
4388
4564
  return
@@ -7087,6 +7263,17 @@ def build_parser():
7087
7263
  pfp.add_argument("--json", action="store_true")
7088
7264
  pfp.set_defaults(func=cmd_fastpath_eval)
7089
7265
 
7266
+ pcr = sub.add_parser("cleanroom",
7267
+ help="run a command against a CLEAN checkout of one commit in a throwaway worktree (ADR-008)")
7268
+ pcr.add_argument("--ledger", default="QA-LEDGER.json")
7269
+ pcr.add_argument("--repo", required=True)
7270
+ pcr.add_argument("--ref", default=None, help="commit to verify; default HEAD")
7271
+ pcr.add_argument("--run", required=True,
7272
+ help="the command to run inside the worktree; the engine never guesses it")
7273
+ pcr.add_argument("--setup", default=None, help="optional bootstrap before --run (e.g. npm ci)")
7274
+ pcr.add_argument("--json", action="store_true")
7275
+ pcr.set_defaults(func=cmd_cleanroom)
7276
+
7090
7277
  pgc = sub.add_parser("golden-coverage",
7091
7278
  help="record the MEASURED source files a golden's harness exercises (ADR-006)")
7092
7279
  pgc.add_argument("--harness", required=True, help="script that drives the subject")
@@ -71,7 +71,7 @@ than inventing a step. Keep the CONTENT in the conversation's language and the l
71
71
  repo straight from the ledger, or null when none was requested. The template degrades when
72
72
  absent, like every other field.
73
73
  - **Spec-drift (ADR-005):** `dashboard --json` carries `spec_drift` — the latest advisory
74
- run (per-document verdicts: SPEC_STALE / CLEAN / UNMAPPED / UNTRACKED) — only when a run
74
+ run (per-document verdicts: SPEC_STALE / CLEAN / UNMAPPED / UNTRACKED / NO-CODE) — only when a run
75
75
  exists in the ledger; a virgin ledger keeps the exact prior schema. Advisory visibility of
76
76
  the spec-maintenance tax, never readiness input.
77
77
  - **Modes card:** the template draws one card for both modes — fast-path verdict chips per
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "uscha",
4
- "version": "1.61.0",
4
+ "version": "1.63.0",
5
5
  "displayName": "Uscha",
6
- "description": "Spec-driven development for LLM coding agents: 9 skills (discovery, adr-refine, reverse-discovery, characterize, devloop, sysdoc, rubric, mirador, status) + a stdlib measurement engine (qa_ledger.py, 32 subcommands + universal installer + npm/npx router). Facts block, guesses advise; the human approves.",
6
+ "description": "Spec-driven development for LLM coding agents: 9 skills (discovery, adr-refine, reverse-discovery, characterize, devloop, sysdoc, rubric, mirador, status) + a stdlib measurement engine (qa_ledger.py, 33 subcommands + universal installer + npm/npx router). Facts block, guesses advise; the human approves.",
7
7
  "author": {
8
8
  "name": "Andres Massello",
9
9
  "url": "https://github.com/andresmassello"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uscha",
3
- "version": "1.61.0",
3
+ "version": "1.63.0",
4
4
  "description": "Uscha spec-driven development methodology for coding agents. Includes npm/npx router.",
5
5
  "author": {
6
6
  "name": "Andres Massello",
@@ -1,6 +1,6 @@
1
1
  # uscha-kit
2
2
 
3
- **Kit version:** v1.61.0 <!-- uscha:version --> · **[uscha.dev](https://uscha.dev)**
3
+ **Kit version:** v1.63.0 <!-- uscha:version --> · **[uscha.dev](https://uscha.dev)**
4
4
 
5
5
  Spec-driven orchestrator + multi-repo QA for Claude Code, with a deterministic ledger.
6
6
  **Nine skills** (`uscha-discovery`, `uscha-adr-refine`, `uscha-devloop`, `uscha-sysdoc`, `uscha-reverse-discovery`,
@@ -123,7 +123,8 @@ Per document: **`SPEC_STALE`** when governed code outran the spec by more than
123
123
  `defaults.spec_drift.max_lag_days` (default 30), listing the newer files; **`CLEAN`** when it
124
124
  did not; **`UNMAPPED`** when there is no `governs:` frontmatter *or its globs match nothing*
125
125
  — absence of a mapping is absence of measurement, not "no drift"; **`UNTRACKED`** when the
126
- spec has no commit date to compare. The latest run lands in the ledger (`spec_drift`) so the
126
+ spec has no commit date to compare; **`NO-CODE`** when it declares `governs: []`, i.e. a
127
+ decision that governs no source (negative ADRs) — a declaration, not an omission. The latest run lands in the ledger (`spec_drift`) so the
127
128
  mirador can surface it. No readiness impact, no exit-code gate: a stale spec is a prompt for
128
129
  a human conversation, not a blocked pipeline.
129
130
 
@@ -148,6 +149,35 @@ knowing a tree was dirty does not tell you the evidence is wrong, only that it w
148
149
  produced from a commit alone. The git-worktree clean-room that would answer the stronger
149
150
  question is deliberately deferred; ADR-007 records why.
150
151
 
152
+ ## Clean-room (ADR-008) - verify the commit, not your tree
153
+
154
+ A suite can pass in your working tree *because of uncommitted state* and fail against the
155
+ commit alone. The ledger records that as measured, with provenance true of the TREE and not of
156
+ the COMMIT that will merge.
157
+
158
+ ```bash
159
+ python qa_ledger.py cleanroom --repo <name> --run "<your test command>" [--setup "npm ci"]
160
+ ```
161
+
162
+ It creates a detached `git worktree` of the commit, verifies it is clean by construction, runs
163
+ what you gave it, records `ref` / `worktree_sha` / `status` / wall-clock, and removes the
164
+ worktree unconditionally (a zombie worktree is a defect). Status is specific:
165
+ `GREEN`, `RED`, `SETUP_FAILED` (a bootstrap failure is not a failing suite),
166
+ `WORKTREE_FAILED`, `WORKTREE_DIRTY`.
167
+
168
+ **The engine never decides what to run.** The command arrives via `--run`, the same contract
169
+ `golden-coverage` uses for `--harness`: the engine owns isolation, the SHA binding and
170
+ cleanup, and never turns a config file into a code-execution surface. That is also why it
171
+ works for any stack - it never had to know the stack.
172
+
173
+ **Opt-in gate:** `defaults.clean_room.mode` absent or `"off"` and nothing changes. `"final"`
174
+ makes `pr-ready` require a GREEN run pinned to the current HEAD; a new commit makes the
175
+ previous run stale for the gate.
176
+
177
+ Honest limit: this is **not** a substitute for CI. A local worktree runs on your machine, your
178
+ OS, your shell - environment variance is invisible to it. Different failure class, different
179
+ instrument.
180
+
151
181
  ## End-to-end flow
152
182
 
153
183
  `uscha-discovery` is the front for something new (you only have the idea); `uscha-adr-refine` is the front
package/uscha-kit/VERSION CHANGED
@@ -1 +1 @@
1
- uscha-kit 1.61.0
1
+ uscha-kit 1.63.0
@@ -0,0 +1 @@
1
+ {"AC-CR-06": true, "AC-CR-02": true, "AC-CR-04": true, "AC-CR-05": true, "AC-CR-03": true, "AC-CR-01": true, "AC-CR-07": true, "AC-CR-08": true}
@@ -1 +1 @@
1
- {"AC-SD-01": true, "AC-SD-03": true, "AC-SD-02": true, "AC-SD-04": true}
1
+ {"AC-SD-01": true, "AC-SD-03": true, "AC-SD-05": true, "AC-SD-02": true, "AC-SD-04": true}
@@ -2629,6 +2629,35 @@ def _derive_phase(ledger, name, node, k, qa_order):
2629
2629
  reasons.append("la evidencia medida no contiene tests ejecutados")
2630
2630
  _go, sev = _gate_open_and_sev(node)
2631
2631
  blk = sev.get("BLOCKER", 0) + sev.get("CRITICAL", 0)
2632
+ # clean-room gate (ADR-008). OPT-IN: no clean_room block, or mode "off", and this does not
2633
+ # exist -- behavior identical to earlier releases. Declared "final": pr-ready additionally
2634
+ # requires clean-room evidence that is GREEN and pinned to the CURRENT HEAD. A new commit
2635
+ # makes the previous run stale for the gate, the same staleness posture the rest of the
2636
+ # engine takes: evidence certifies the thing it was measured against, nothing later.
2637
+ _cr = _cr_cfg(ledger)
2638
+ if _cr and _cr.get("mode") == "final":
2639
+ _head = None
2640
+ _hr = None
2641
+ _crcfg = (_repo_cfg(ledger, name) if name != "integration"
2642
+ else {"path": "."}) # synthetic scope: never in config["repos"]
2643
+ try:
2644
+ _hr = subprocess.run(["git", "rev-parse", "HEAD"],
2645
+ cwd=_crcfg.get("path", "."),
2646
+ capture_output=True, text=True, encoding="utf-8",
2647
+ errors="replace")
2648
+ except OSError:
2649
+ _hr = None
2650
+ if _hr is not None and _hr.returncode == 0:
2651
+ _head = _hr.stdout.strip()
2652
+ _run = _cr_latest(ledger, name, _head) if _head else None
2653
+ if not _head:
2654
+ reasons.append("clean-room declarado pero no se pudo resolver HEAD "
2655
+ "(no se mide, no se aprueba)")
2656
+ conv = False
2657
+ elif not _run or not _run.get("ok"):
2658
+ reasons.append("falta clean-room verde para %s (evidencia del arbol no "
2659
+ "certifica el commit)" % _head[:8])
2660
+ conv = False
2632
2661
  if conv and tests_measured_green and not tests_red and blk == 0:
2633
2662
  evidence = ["ciclo de agente limpio", "tests verdes (medidos)",
2634
2663
  "0 BLOCKER/CRITICAL abiertos"]
@@ -3026,7 +3055,7 @@ def _sd_governs(path):
3026
3055
  return None
3027
3056
  if not lines or lines[0].strip() != "---":
3028
3057
  return None
3029
- globs, in_governs = None, False
3058
+ globs, in_governs, explicit_empty = None, False, False
3030
3059
  # scan runs to the CLOSING fence, not an arbitrary window -- a governs: key late in a
3031
3060
  # long frontmatter block must not silently read as UNMAPPED (fresh-review finding).
3032
3061
  for ln in lines[1:]:
@@ -3038,6 +3067,8 @@ def _sd_governs(path):
3038
3067
  if rest.startswith("[") and rest.endswith("]"):
3039
3068
  globs = [x.strip().strip("\x27\x22")
3040
3069
  for x in rest[1:-1].split(",") if x.strip()]
3070
+ # only an INLINE [] is a declaration of "nothing to govern"
3071
+ explicit_empty = not globs
3041
3072
  in_governs = False
3042
3073
  elif rest:
3043
3074
  # bare scalar (`governs: src/**`) -- a plausible authoring shorthand;
@@ -3051,6 +3082,11 @@ def _sd_governs(path):
3051
3082
  globs.append(s[2:].strip().strip("\x27\x22"))
3052
3083
  elif s and not ln.startswith((" ", "\t")):
3053
3084
  in_governs = False
3085
+ if globs == [] and not explicit_empty:
3086
+ # a `governs:` key with nothing usable under it (a placeholder, a comment, a typo) is
3087
+ # an UNFINISHED declaration, not a statement that this spec governs nothing. Report it
3088
+ # as UNMAPPED, which is what it is (fresh-review finding).
3089
+ return None
3054
3090
  return globs
3055
3091
 
3056
3092
 
@@ -3111,6 +3147,16 @@ def cmd_spec_drift(args):
3111
3147
  row.update({"verdict": "UNMAPPED", "reason": "no governs: frontmatter"})
3112
3148
  results.append(row)
3113
3149
  continue
3150
+ if not governs:
3151
+ # An EXPLICIT empty list is a declaration, not an omission: this decision governs
3152
+ # no code and never will. Negative ADRs ("we are NOT doing X, and why") are a
3153
+ # documented practice in this kit, and reporting them UNMAPPED forever turns a
3154
+ # correct state into permanent noise -- which is how an advisory gets ignored.
3155
+ # Found by running spec-drift on this repo's own ADR-004.
3156
+ row.update({"verdict": "NO-CODE",
3157
+ "reason": "declares governs: [] -- a decision that governs no code"})
3158
+ results.append(row)
3159
+ continue
3114
3160
  matched = []
3115
3161
  pats = [_fp_glob_re(g) for g in governs]
3116
3162
  for f in tracked:
@@ -3171,7 +3217,8 @@ def cmd_spec_drift(args):
3171
3217
  print("SPEC-DRIFT %s (advisory, lag > %dd):" % (args.repo, lag_days))
3172
3218
  if not results:
3173
3219
  print(" no spec documents found (SPEC.md / docs/adr/*.md)")
3174
- mark = {"SPEC_STALE": "!!", "CLEAN": "ok", "UNMAPPED": "--", "UNTRACKED": "--"}
3220
+ mark = {"SPEC_STALE": "!!", "CLEAN": "ok", "UNMAPPED": "--", "UNTRACKED": "--",
3221
+ "NO-CODE": "ok"}
3175
3222
  for r_ in results:
3176
3223
  line = " %s %s: %s" % (mark.get(r_["verdict"], "??"), r_["file"],
3177
3224
  r_["verdict"])
@@ -3337,6 +3384,132 @@ def cmd_golden_coverage(args):
3337
3384
 
3338
3385
 
3339
3386
 
3387
+ # --------------------------------------------------------------------------- #
3388
+ # cleanroom (ADR-008: verify the COMMIT, not the tree -- opt-in, human-supplied command)
3389
+ # --------------------------------------------------------------------------- #
3390
+
3391
+ CLEAN_ROOM_KEY = "clean_room"
3392
+
3393
+
3394
+ def _cr_cfg(ledger):
3395
+ cfg = ledger["config"].get("defaults", {}).get(CLEAN_ROOM_KEY)
3396
+ return cfg if isinstance(cfg, dict) else None
3397
+
3398
+
3399
+ def _cr_latest(ledger, repo, ref=None):
3400
+ """Latest clean-room record for a repo, optionally pinned to one ref."""
3401
+ runs = [e for e in ledger.get(CLEAN_ROOM_KEY, [])
3402
+ if e.get("repo") == repo and (ref is None or e.get("ref") == ref)]
3403
+ return runs[-1] if runs else None
3404
+
3405
+
3406
+ def cmd_cleanroom(args):
3407
+ """Run a command against a CLEAN CHECKOUT of one commit, in a throwaway worktree.
3408
+
3409
+ Evidence produced in the maker's tree is true of the TREE; this produces evidence true
3410
+ of the COMMIT. As a side effect maker != checker becomes physical: the worktree cannot
3411
+ see uncommitted state.
3412
+
3413
+ The engine does NOT decide what to run. The command arrives explicitly via --run, the
3414
+ same contract golden-coverage uses for --harness: the engine owns what it can guarantee
3415
+ (isolation, the SHA binding, cleanup) and never guesses what a project's suite is.
3416
+ Reading test_command_* from config and executing it would make the engine an executor
3417
+ of config-supplied shell, which it is not (ADR-008)."""
3418
+ import time
3419
+ ledger = _load(args.ledger)
3420
+ _repo_node(ledger, args.repo)
3421
+ repo_path = _repo_cfg(ledger, args.repo).get("path", ".")
3422
+
3423
+ def git(*a, **kw):
3424
+ cwd = kw.pop("cwd", repo_path)
3425
+ try:
3426
+ return subprocess.run(["git"] + list(a), cwd=cwd, capture_output=True,
3427
+ text=True, encoding="utf-8", errors="replace")
3428
+ except OSError:
3429
+ return None
3430
+
3431
+ rev = git("rev-parse", "--verify", (args.ref or "HEAD") + "^{commit}")
3432
+ if rev is None or rev.returncode != 0 or not rev.stdout.strip():
3433
+ print("[qa_ledger] cannot resolve ref %r in %s - nothing to verify."
3434
+ % (args.ref or "HEAD", repo_path), file=sys.stderr)
3435
+ sys.exit(2)
3436
+ sha = rev.stdout.strip()
3437
+
3438
+ wt = tempfile.mkdtemp(prefix="uscha-cleanroom-")
3439
+ target = os.path.join(wt, "tree")
3440
+ started = time.time()
3441
+ record = {"repo": args.repo, "ref": sha, "at": _now(), "ok": False,
3442
+ "status": None, "wall_ms": None, "worktree_sha": None}
3443
+ keep = bool((_cr_cfg(ledger) or {}).get("keep_worktree_on_failure"))
3444
+ try:
3445
+ add = git("worktree", "add", "--detach", target, sha)
3446
+ if add is None or add.returncode != 0:
3447
+ record["status"] = "WORKTREE_FAILED"
3448
+ print("[qa_ledger] git worktree add failed:\n%s"
3449
+ % ((add.stderr if add else "git unavailable") or "")[-800:], file=sys.stderr)
3450
+ else:
3451
+ # a worktree of a commit must be clean by construction; if it is not, something
3452
+ # (a smudge filter, a hook, a stale index) intervened and the isolation claim is
3453
+ # already false. Say so rather than measure in it.
3454
+ st = git("status", "--porcelain", cwd=target)
3455
+ if st is None or st.returncode != 0 or st.stdout.strip():
3456
+ record["status"] = "WORKTREE_DIRTY"
3457
+ else:
3458
+ record["worktree_sha"] = sha
3459
+ if args.setup:
3460
+ r = subprocess.run(args.setup, cwd=target, shell=True,
3461
+ capture_output=True, text=True,
3462
+ encoding="utf-8", errors="replace")
3463
+ if r.returncode != 0:
3464
+ record["status"] = "SETUP_FAILED"
3465
+ print((r.stderr or "")[-800:], file=sys.stderr)
3466
+ if record["status"] is None:
3467
+ r = subprocess.run(args.run, cwd=target, shell=True,
3468
+ capture_output=True, text=True,
3469
+ encoding="utf-8", errors="replace")
3470
+ record["exit_code"] = r.returncode
3471
+ record["status"] = "GREEN" if r.returncode == 0 else "RED"
3472
+ record["ok"] = r.returncode == 0
3473
+ if r.returncode != 0:
3474
+ print((r.stdout or "")[-1500:], file=sys.stderr)
3475
+ finally:
3476
+ record["wall_ms"] = int((time.time() - started) * 1000)
3477
+ # Cleanup is unconditional unless the human asked to inspect a FAILURE: a zombie
3478
+ # worktree is a defect, and `git worktree remove` alone leaves the admin entry behind.
3479
+ if record["ok"] or not keep:
3480
+ git("worktree", "remove", "--force", target)
3481
+ git("worktree", "prune")
3482
+ shutil.rmtree(wt, ignore_errors=True)
3483
+ # VERIFY the removal instead of assuming it. rmtree(ignore_errors=True) and a
3484
+ # discarded git return code can both fail in silence -- typically on Windows,
3485
+ # where a handle the caller's command left open blocks removal.
3486
+ if os.path.exists(target):
3487
+ record["cleanup_failed"] = True
3488
+ record["worktree_kept_at"] = target
3489
+ print("[qa_ledger] WARNING: the clean-room worktree could not be removed and "
3490
+ "is still at %s (a process may still hold a handle). Remove it with: "
3491
+ "git worktree remove --force %s && git worktree prune"
3492
+ % (target, target), file=sys.stderr)
3493
+ else:
3494
+ record["worktree_kept_at"] = target
3495
+
3496
+ ledger.setdefault(CLEAN_ROOM_KEY, []).append(record)
3497
+ ledger["step_counter"] += 1
3498
+ ledger["steps"].append({"n": ledger["step_counter"], "at": _now(),
3499
+ "kind": "cleanroom", "repo": args.repo})
3500
+ _save(args.ledger, ledger)
3501
+
3502
+ if args.json:
3503
+ print(json.dumps(record, indent=2, ensure_ascii=False))
3504
+ else:
3505
+ print("CLEANROOM %s @ %s: %s (%.1fs)"
3506
+ % (args.repo, sha[:8], record["status"], (record["wall_ms"] or 0) / 1000.0))
3507
+ if record.get("worktree_kept_at"):
3508
+ print(" worktree kept for inspection: " + record["worktree_kept_at"])
3509
+ sys.exit(0 if record["ok"] else 1)
3510
+
3511
+
3512
+
3340
3513
  def cmd_escalate(args):
3341
3514
  ledger = _load(args.ledger)
3342
3515
  _repo_node(ledger, args.repo)
@@ -4383,6 +4556,9 @@ def cmd_dashboard(args):
4383
4556
  _org[_rn] = _snaps[-1]["origin"]
4384
4557
  if _org:
4385
4558
  out["evidence_origin"] = _org
4559
+ if ledger.get(CLEAN_ROOM_KEY):
4560
+ out["clean_room"] = {r: [e for e in ledger[CLEAN_ROOM_KEY] if e.get("repo") == r][-1]
4561
+ for r in {e.get("repo") for e in ledger[CLEAN_ROOM_KEY]}}
4386
4562
  if getattr(args, "json", False):
4387
4563
  print(json.dumps(out, indent=2, ensure_ascii=False))
4388
4564
  return
@@ -7087,6 +7263,17 @@ def build_parser():
7087
7263
  pfp.add_argument("--json", action="store_true")
7088
7264
  pfp.set_defaults(func=cmd_fastpath_eval)
7089
7265
 
7266
+ pcr = sub.add_parser("cleanroom",
7267
+ help="run a command against a CLEAN checkout of one commit in a throwaway worktree (ADR-008)")
7268
+ pcr.add_argument("--ledger", default="QA-LEDGER.json")
7269
+ pcr.add_argument("--repo", required=True)
7270
+ pcr.add_argument("--ref", default=None, help="commit to verify; default HEAD")
7271
+ pcr.add_argument("--run", required=True,
7272
+ help="the command to run inside the worktree; the engine never guesses it")
7273
+ pcr.add_argument("--setup", default=None, help="optional bootstrap before --run (e.g. npm ci)")
7274
+ pcr.add_argument("--json", action="store_true")
7275
+ pcr.set_defaults(func=cmd_cleanroom)
7276
+
7090
7277
  pgc = sub.add_parser("golden-coverage",
7091
7278
  help="record the MEASURED source files a golden's harness exercises (ADR-006)")
7092
7279
  pgc.add_argument("--harness", required=True, help="script that drives the subject")
@@ -71,7 +71,7 @@ than inventing a step. Keep the CONTENT in the conversation's language and the l
71
71
  repo straight from the ledger, or null when none was requested. The template degrades when
72
72
  absent, like every other field.
73
73
  - **Spec-drift (ADR-005):** `dashboard --json` carries `spec_drift` — the latest advisory
74
- run (per-document verdicts: SPEC_STALE / CLEAN / UNMAPPED / UNTRACKED) — only when a run
74
+ run (per-document verdicts: SPEC_STALE / CLEAN / UNMAPPED / UNTRACKED / NO-CODE) — only when a run
75
75
  exists in the ledger; a virgin ledger keeps the exact prior schema. Advisory visibility of
76
76
  the spec-maintenance tax, never readiness input.
77
77
  - **Modes card:** the template draws one card for both modes — fast-path verdict chips per
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.61.0",
2
+ "version": "1.63.0",
3
3
  "project": null,
4
4
  "defaults": {
5
5
  "coverage_threshold": 60,
@@ -55,6 +55,10 @@
55
55
  "spec_drift": {
56
56
  "max_lag_days": 30
57
57
  },
58
+ "clean_room": {
59
+ "mode": "off",
60
+ "keep_worktree_on_failure": false
61
+ },
58
62
  "static_gate_zero_at": 10,
59
63
  "constitution_file": "CONSTITUTION.md",
60
64
  "rebuild": {