@andresmassello/uscha 1.61.0 → 1.62.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 +1 -1
- package/package.json +1 -1
- package/uscha-kit/.claude/skills/uscha-devloop/qa_ledger.py +20 -2
- package/uscha-kit/.claude/skills/uscha-mirador/SKILL.md +1 -1
- package/uscha-kit/.claude-plugin/plugin.json +1 -1
- package/uscha-kit/.codex-plugin/plugin.json +1 -1
- package/uscha-kit/README.md +3 -2
- package/uscha-kit/VERSION +1 -1
- package/uscha-kit/reports/junit/.specdrift-cases.json +1 -1
- package/uscha-kit/skills/uscha-devloop/qa_ledger.py +20 -2
- package/uscha-kit/skills/uscha-mirador/SKILL.md +1 -1
- package/uscha-kit/uscha.config.json +1 -1
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.
|
|
43
|
+
**Kit v1.62.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
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andresmassello/uscha",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.62.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",
|
|
@@ -3026,7 +3026,7 @@ def _sd_governs(path):
|
|
|
3026
3026
|
return None
|
|
3027
3027
|
if not lines or lines[0].strip() != "---":
|
|
3028
3028
|
return None
|
|
3029
|
-
globs, in_governs = None, False
|
|
3029
|
+
globs, in_governs, explicit_empty = None, False, False
|
|
3030
3030
|
# scan runs to the CLOSING fence, not an arbitrary window -- a governs: key late in a
|
|
3031
3031
|
# long frontmatter block must not silently read as UNMAPPED (fresh-review finding).
|
|
3032
3032
|
for ln in lines[1:]:
|
|
@@ -3038,6 +3038,8 @@ def _sd_governs(path):
|
|
|
3038
3038
|
if rest.startswith("[") and rest.endswith("]"):
|
|
3039
3039
|
globs = [x.strip().strip("\x27\x22")
|
|
3040
3040
|
for x in rest[1:-1].split(",") if x.strip()]
|
|
3041
|
+
# only an INLINE [] is a declaration of "nothing to govern"
|
|
3042
|
+
explicit_empty = not globs
|
|
3041
3043
|
in_governs = False
|
|
3042
3044
|
elif rest:
|
|
3043
3045
|
# bare scalar (`governs: src/**`) -- a plausible authoring shorthand;
|
|
@@ -3051,6 +3053,11 @@ def _sd_governs(path):
|
|
|
3051
3053
|
globs.append(s[2:].strip().strip("\x27\x22"))
|
|
3052
3054
|
elif s and not ln.startswith((" ", "\t")):
|
|
3053
3055
|
in_governs = False
|
|
3056
|
+
if globs == [] and not explicit_empty:
|
|
3057
|
+
# a `governs:` key with nothing usable under it (a placeholder, a comment, a typo) is
|
|
3058
|
+
# an UNFINISHED declaration, not a statement that this spec governs nothing. Report it
|
|
3059
|
+
# as UNMAPPED, which is what it is (fresh-review finding).
|
|
3060
|
+
return None
|
|
3054
3061
|
return globs
|
|
3055
3062
|
|
|
3056
3063
|
|
|
@@ -3111,6 +3118,16 @@ def cmd_spec_drift(args):
|
|
|
3111
3118
|
row.update({"verdict": "UNMAPPED", "reason": "no governs: frontmatter"})
|
|
3112
3119
|
results.append(row)
|
|
3113
3120
|
continue
|
|
3121
|
+
if not governs:
|
|
3122
|
+
# An EXPLICIT empty list is a declaration, not an omission: this decision governs
|
|
3123
|
+
# no code and never will. Negative ADRs ("we are NOT doing X, and why") are a
|
|
3124
|
+
# documented practice in this kit, and reporting them UNMAPPED forever turns a
|
|
3125
|
+
# correct state into permanent noise -- which is how an advisory gets ignored.
|
|
3126
|
+
# Found by running spec-drift on this repo's own ADR-004.
|
|
3127
|
+
row.update({"verdict": "NO-CODE",
|
|
3128
|
+
"reason": "declares governs: [] -- a decision that governs no code"})
|
|
3129
|
+
results.append(row)
|
|
3130
|
+
continue
|
|
3114
3131
|
matched = []
|
|
3115
3132
|
pats = [_fp_glob_re(g) for g in governs]
|
|
3116
3133
|
for f in tracked:
|
|
@@ -3171,7 +3188,8 @@ def cmd_spec_drift(args):
|
|
|
3171
3188
|
print("SPEC-DRIFT %s (advisory, lag > %dd):" % (args.repo, lag_days))
|
|
3172
3189
|
if not results:
|
|
3173
3190
|
print(" no spec documents found (SPEC.md / docs/adr/*.md)")
|
|
3174
|
-
mark = {"SPEC_STALE": "!!", "CLEAN": "ok", "UNMAPPED": "--", "UNTRACKED": "--"
|
|
3191
|
+
mark = {"SPEC_STALE": "!!", "CLEAN": "ok", "UNMAPPED": "--", "UNTRACKED": "--",
|
|
3192
|
+
"NO-CODE": "ok"}
|
|
3175
3193
|
for r_ in results:
|
|
3176
3194
|
line = " %s %s: %s" % (mark.get(r_["verdict"], "??"), r_["file"],
|
|
3177
3195
|
r_["verdict"])
|
|
@@ -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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "uscha",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.62.0",
|
|
5
5
|
"displayName": "Uscha",
|
|
6
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.",
|
|
7
7
|
"author": {
|
package/uscha-kit/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# uscha-kit
|
|
2
2
|
|
|
3
|
-
**Kit version:** v1.
|
|
3
|
+
**Kit version:** v1.62.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
|
|
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
|
|
package/uscha-kit/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
uscha-kit 1.
|
|
1
|
+
uscha-kit 1.62.0
|
|
@@ -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}
|
|
@@ -3026,7 +3026,7 @@ def _sd_governs(path):
|
|
|
3026
3026
|
return None
|
|
3027
3027
|
if not lines or lines[0].strip() != "---":
|
|
3028
3028
|
return None
|
|
3029
|
-
globs, in_governs = None, False
|
|
3029
|
+
globs, in_governs, explicit_empty = None, False, False
|
|
3030
3030
|
# scan runs to the CLOSING fence, not an arbitrary window -- a governs: key late in a
|
|
3031
3031
|
# long frontmatter block must not silently read as UNMAPPED (fresh-review finding).
|
|
3032
3032
|
for ln in lines[1:]:
|
|
@@ -3038,6 +3038,8 @@ def _sd_governs(path):
|
|
|
3038
3038
|
if rest.startswith("[") and rest.endswith("]"):
|
|
3039
3039
|
globs = [x.strip().strip("\x27\x22")
|
|
3040
3040
|
for x in rest[1:-1].split(",") if x.strip()]
|
|
3041
|
+
# only an INLINE [] is a declaration of "nothing to govern"
|
|
3042
|
+
explicit_empty = not globs
|
|
3041
3043
|
in_governs = False
|
|
3042
3044
|
elif rest:
|
|
3043
3045
|
# bare scalar (`governs: src/**`) -- a plausible authoring shorthand;
|
|
@@ -3051,6 +3053,11 @@ def _sd_governs(path):
|
|
|
3051
3053
|
globs.append(s[2:].strip().strip("\x27\x22"))
|
|
3052
3054
|
elif s and not ln.startswith((" ", "\t")):
|
|
3053
3055
|
in_governs = False
|
|
3056
|
+
if globs == [] and not explicit_empty:
|
|
3057
|
+
# a `governs:` key with nothing usable under it (a placeholder, a comment, a typo) is
|
|
3058
|
+
# an UNFINISHED declaration, not a statement that this spec governs nothing. Report it
|
|
3059
|
+
# as UNMAPPED, which is what it is (fresh-review finding).
|
|
3060
|
+
return None
|
|
3054
3061
|
return globs
|
|
3055
3062
|
|
|
3056
3063
|
|
|
@@ -3111,6 +3118,16 @@ def cmd_spec_drift(args):
|
|
|
3111
3118
|
row.update({"verdict": "UNMAPPED", "reason": "no governs: frontmatter"})
|
|
3112
3119
|
results.append(row)
|
|
3113
3120
|
continue
|
|
3121
|
+
if not governs:
|
|
3122
|
+
# An EXPLICIT empty list is a declaration, not an omission: this decision governs
|
|
3123
|
+
# no code and never will. Negative ADRs ("we are NOT doing X, and why") are a
|
|
3124
|
+
# documented practice in this kit, and reporting them UNMAPPED forever turns a
|
|
3125
|
+
# correct state into permanent noise -- which is how an advisory gets ignored.
|
|
3126
|
+
# Found by running spec-drift on this repo's own ADR-004.
|
|
3127
|
+
row.update({"verdict": "NO-CODE",
|
|
3128
|
+
"reason": "declares governs: [] -- a decision that governs no code"})
|
|
3129
|
+
results.append(row)
|
|
3130
|
+
continue
|
|
3114
3131
|
matched = []
|
|
3115
3132
|
pats = [_fp_glob_re(g) for g in governs]
|
|
3116
3133
|
for f in tracked:
|
|
@@ -3171,7 +3188,8 @@ def cmd_spec_drift(args):
|
|
|
3171
3188
|
print("SPEC-DRIFT %s (advisory, lag > %dd):" % (args.repo, lag_days))
|
|
3172
3189
|
if not results:
|
|
3173
3190
|
print(" no spec documents found (SPEC.md / docs/adr/*.md)")
|
|
3174
|
-
mark = {"SPEC_STALE": "!!", "CLEAN": "ok", "UNMAPPED": "--", "UNTRACKED": "--"
|
|
3191
|
+
mark = {"SPEC_STALE": "!!", "CLEAN": "ok", "UNMAPPED": "--", "UNTRACKED": "--",
|
|
3192
|
+
"NO-CODE": "ok"}
|
|
3175
3193
|
for r_ in results:
|
|
3176
3194
|
line = " %s %s: %s" % (mark.get(r_["verdict"], "??"), r_["file"],
|
|
3177
3195
|
r_["verdict"])
|
|
@@ -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
|