@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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andres Massello
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,497 @@
1
+ # uscha-kit
2
+
3
+ **Kit version:** v1.40.1 <!-- uscha:version -->
4
+
5
+ Spec-driven orchestrator + multi-repo QA for Claude Code, with a deterministic ledger.
6
+ **Eight skills** (`uscha-discovery`, `uscha-adr-refine`, `uscha-devloop`, `uscha-sysdoc`, `uscha-reverse-discovery`,
7
+ `uscha-characterize`, `uscha-rubric`, `uscha-mirador`) and a measurement engine (`qa_ledger.py`).
8
+
9
+ **Who it's for:** a solo operator carrying ONE non-trivial or risky change, kept
10
+ honest by a deterministic ledger and a human gate at the merge. It is NOT for trivial
11
+ changes (a one-liner runs build+test and that's it).
12
+
13
+ ## What's inside
14
+
15
+ ```
16
+ uscha-kit/
17
+ ├─ uscha.config.json # config: repos, thresholds, commands
18
+ ├─ hooks/
19
+ │ └─ block-approved-writes.ps1 # PreToolUse: the agent CANNOT write .approved (INV-GOLDEN-01)
20
+ ├─ templates/
21
+ │ ├─ CLAUDE.md # permanent repo protocol
22
+ │ ├─ CONSTITUTION.md # inviolable invariants (fill in the domain)
23
+ │ ├─ .gitattributes # *.approved.* binary — so line endings don't lie
24
+ │ └─ docs/adr/ # ADR scaffold
25
+ ?? .codex-plugin/plugin.json # Codex plugin manifest
26
+ ?? skills/ # Codex plugin mirror of .claude/skills (smoke checks sync)
27
+ └─ .claude/skills/
28
+ ├─ uscha-discovery/ # vague idea → 1x1 grilling → CONSTITUTION/SPEC/ADR/ACCEPTANCE…
29
+ ├─ uscha-adr-refine/ # known feature: precision interview → ADR + ACCEPTANCE
30
+ ├─ uscha-reverse-discovery/ # brownfield: FACTS map of the existing system (does not propose shape)
31
+ ├─ uscha-characterize/ # captures the golden by running the ORIGINAL code (stops at human approval)
32
+ ├─ uscha-devloop/
33
+ │ ├─ SKILL.md # orchestrator: plan → build → QA loop → PR
34
+ │ └─ qa_ledger.py # measurement + ledger + gates (ingest/log-gate/golden-diff/gate-check/pit/simplicity/rebuild)
35
+ ├─ uscha-sysdoc/ # (optional) two-view HTML deck from the ledger
36
+ └─ uscha-rubric/ # (optional) rubric grading — thin adapter; the core is agnostic
37
+ ```
38
+
39
+ ## End-to-end flow
40
+
41
+ `uscha-discovery` is the front for something new (you only have the idea); `uscha-adr-refine` is the front
42
+ for a known feature; `uscha-devloop` builds and verifies. They meet at the `ACCEPTANCE.md`.
43
+
44
+ ```
45
+ /uscha-discovery # idea only + reference material → 1x1 grilling (proposes, you decide)
46
+ ↓ # writes CONTEXT.md, CONSTITUTION.md, SPEC.md, docs/adr/*.md, ACCEPTANCE.md, RISKS.md, HANDOFF.md
47
+ /uscha-devloop # plan → build → QA loop (fact gates to the ledger) → PR (stops at the merge)
48
+
49
+ /uscha-sysdoc # (optional, on request) documents the system from the ledger
50
+ ```
51
+
52
+ (For an already known feature, instead of `/uscha-discovery` you use `/uscha-adr-refine`.)
53
+
54
+ **Migration/legacy on-ramp (profile E):** the golden is the field truth and is captured
55
+ BEFORE touching anything.
56
+
57
+ ```
58
+ /uscha-reverse-discovery # FACTS map of the old system (endpoints, contracts, dependencies)
59
+
60
+ /uscha-characterize # runs the ORIGINAL code with a real corpus → .received → STOPS:
61
+ ↓ # a HUMAN approves the .approved (the agent never writes them — hook)
62
+ /uscha-devloop # migrates; golden-diff byte-compares against the .approved on each pass
63
+ ```
64
+
65
+ ## Requirements
66
+
67
+ - **Python 3.8+** (stdlib only — no `pip install`). `cloc` is NOT needed; the LOC is
68
+ counted in Python.
69
+ - For `ingest-gate` and coverage to work, your Maven build must emit the
70
+ reports (your `java-qa-gate` already has the plugins; these are the paths the ledger
71
+ expects):
72
+
73
+ | data | plugin / goal | file |
74
+ |-------------|--------------------------------------------------|---------|
75
+ | coverage | `jacoco-maven-plugin` (`report`) | `target/site/jacoco/jacoco.xml` (or `jacoco-aggregate/`) |
76
+ | test count | `maven-surefire-plugin` / `failsafe` | `target/surefire-reports/TEST-*.xml` |
77
+ | checkstyle | `maven-checkstyle-plugin` (`checkstyle`) | `target/checkstyle-result.xml` |
78
+ | pmd | `maven-pmd-plugin` (`pmd`) | `target/pmd.xml` |
79
+ | spotbugs+fsb| `spotbugs-maven-plugin` (+ findsecbugs) | `target/spotbugsXml.xml` |
80
+
81
+ Flutter: coverage from `coverage/lcov.info` (`flutter test --coverage`);
82
+ the test count is approximate (it counts `test(`/`testWidgets(`).
83
+
84
+ Python (`type: python`, kit 1.4.0):
85
+
86
+ | data | command | file |
87
+ |-------------|---------------------------------------------------|---------|
88
+ | coverage | `pytest --cov --cov-report=xml:reports/coverage.xml` | `coverage.xml` or `reports/coverage.xml` (Cobertura) |
89
+ | test count | `pytest --junitxml=reports/junit.xml` | `reports/junit.xml` (wrapped root `<testsuites>` supported) |
90
+ | ruff | `ruff check --output-format=json > reports/ruff.json` | `reports/ruff.json` (S*/E9*/F82*→HIGH · B*→MEDIUM · rest→LOW) |
91
+ | mypy | `mypy src > reports/mypy.txt` | `reports/mypy.txt` (error→HIGH · warning→MEDIUM · note→INFO) |
92
+
93
+ `ingest-gate` finds them automatically by the repo's type, or with explicit `--ruff/--mypy`.
94
+ Contract identical to Java: a missing report = the gate didn't run (it never credits fixes).
95
+
96
+ TypeScript/JS (`type: node`, kit 1.5.0):
97
+
98
+ | data | command | file |
99
+ |-------------|---------------------------------------------------|---------|
100
+ | coverage | `jest --coverage` (or vitest with lcov reporter) | `coverage/lcov.info` (same parser as Flutter) |
101
+ | test count | `jest-junit` / `vitest --reporter=junit` | `reports/junit.xml` or `junit.xml` (wrapped root supported) |
102
+ | eslint | `eslint . --format json > reports/eslint.json` | `reports/eslint.json` (error→HIGH · warn→MEDIUM · `security/*` floor HIGH · ruleId null→HIGH) |
103
+ | tsc | `tsc --noEmit > reports/tsc.txt` | `reports/tsc.txt` (error TS → HIGH) |
104
+
105
+ `ingest-gate` finds them by the repo's type, or with explicit `--eslint/--tsc`.
106
+ Same absence contract.
107
+
108
+ Go (`type: go`, kit 1.6.0):
109
+
110
+ | data | command | file |
111
+ |-------------|---------------------------------------------------|---------|
112
+ | coverage | `go test -coverprofile=coverage.out ./...` | `coverage.out` (native cover profile — % by STATEMENTS, the Go convention) |
113
+ | test count | `gotestsum --junitfile reports/junit.xml -- ./...`| `reports/junit.xml` (wrapped JUnit supported) |
114
+ | golangci | `golangci-lint run --output.checkstyle.path=reports/golangci.xml` (v2; in v1: `--out-format checkstyle > ...`) | `reports/golangci.xml` (checkstyle format: error→HIGH · warning→MEDIUM; includes gosec if enabled) |
115
+
116
+ `ingest-gate` finds it by the repo's type, or with explicit `--golangci`. The
117
+ `_test.go` tests live alongside the code (Go convention) — the LOC classifies them by suffix.
118
+ `vendor/` and `testdata/` are excluded from the LOC. Same absence contract.
119
+ **Watch out for severities**: golangci-lint emits `severity=error` for EVERYTHING unless you
120
+ configure `severity:` rules — without that, even style nits gate as HIGH;
121
+ configure severities (or a lean linter set) so that MEDIUM really exists.
122
+
123
+ Rust (`type: rust`, kit 1.7.0):
124
+
125
+ | data | command | file |
126
+ |-------------|---------------------------------------------------|---------|
127
+ | coverage | `cargo llvm-cov --cobertura --output-path reports/coverage.xml` | `reports/coverage.xml` (Cobertura — same parser as Python) |
128
+ | test count | `cargo nextest run` + copy (see note) | `reports/junit.xml` (wrapped JUnit supported) |
129
+ | clippy | `cargo clippy --message-format=json > reports/clippy.json` | `reports/clippy.json` (JSONL: error→HIGH · warning→MEDIUM · `code:null` compile-error→HIGH; summaries without span ignored) |
130
+
131
+ `ingest-gate` with explicit `--clippy` or by type. The inline `#[cfg(test)]` tests
132
+ count as prod LOC (documented limitation); `tests/` = integration.
133
+ **Watch out for junit**: nextest does NOT emit JUnit by default — you have to enable it in
134
+ `.config/nextest.toml` (`[profile.default.junit] path = "junit.xml"`) and the file
135
+ lands in `target/nextest/default/junit.xml`; copy it to `reports/junit.xml`
136
+ (`cp target/nextest/default/junit.xml reports/junit.xml`, already included in the
137
+ example `test_command_rust`).
138
+
139
+ C#/.NET (`type: dotnet`, kit 1.7.0):
140
+
141
+ | data | command | file |
142
+ |-------------|---------------------------------------------------|---------|
143
+ | coverage | coverlet.msbuild: `/p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput=$PWD/reports/coverage.xml` | `reports/coverage.xml` (Cobertura) |
144
+ | test count | `dotnet test --logger "junit;LogFilePath=$PWD/reports/junit.xml"` (JUnitXml.TestLogger package) | `reports/junit.xml` |
145
+ | roslyn | `dotnet build /p:ErrorLog="reports/analysis.sarif,version=2"` | `reports/analysis.sarif` (SARIF: error→HIGH · warning→MEDIUM · note→INFO; suppressed ignored) |
146
+
147
+ `ingest-gate` with explicit `--sarif` or by type. SARIF is the universal static-analysis
148
+ format — the parser works for any tool that emits it.
149
+ **Watch out for paths**: relative `LogFilePath` and `CoverletOutput` resolve against the
150
+ TEST PROJECT directory, not the repo root — hence the `$PWD`
151
+ (absolute anchoring). With multiple test projects, merge (`/p:MergeWith`) or use one
152
+ report per project. `ErrorLog` without `,version=2` emits SARIF **v1**; the parser
153
+ has a v1 fallback, but ask for v2 (the comma requires the quotes).
154
+
155
+ C++ (`type: cpp`, kit 1.8.0):
156
+
157
+ | data | command | file |
158
+ |-------------|---------------------------------------------------|---------|
159
+ | coverage | `gcovr --cobertura reports/coverage.xml` (over gcov) | `reports/coverage.xml` (Cobertura — same parser) |
160
+ | test count | `ctest --test-dir build --output-junit ../reports/junit.xml` (CMake ≥3.21) or `--gtest_output=xml:...` | `reports/junit.xml` (flat AND wrapped root supported) |
161
+ | clang-tidy | `clang-tidy <files> > reports/clang-tidy.txt` | `reports/clang-tidy.txt` (error→HIGH · warning→MEDIUM · `cert-*`/security floor HIGH) |
162
+
163
+ `ingest-gate` with explicit `--clang-tidy` or by type. The build system (CMake/
164
+ Bazel/make) belongs to the per-repo adapter — the kit only requires that the reports exist.
165
+ `cmake-build-*` (any CLion profile) and `_deps` are excluded from the LOC.
166
+
167
+ Kotlin/JVM with Gradle (`type: gradle`, kit 1.9.0) — **Kotlin over Maven already
168
+ works with `type: maven`** (`.kt` has always counted; JaCoCo/Surefire don't
169
+ distinguish JVM language). This type is for the common Gradle case:
170
+
171
+ | data | command | file |
172
+ |-------------|---------------------------------------------------|---------|
173
+ | coverage | `./gradlew test jacocoTestReport` | `build/reports/jacoco/test/jacocoTestReport.xml` (JaCoCo — same parser as maven) |
174
+ | test count | (same `./gradlew test`) | `build/test-results/**/TEST-*.xml` (per-class, like surefire) |
175
+ | detekt | `./gradlew detekt` **separately** (see note) | `build/reports/detekt/detekt.xml` (checkstyle format: error→HIGH · warning→MEDIUM; absolute paths relativized) |
176
+
177
+ `ingest-gate` with explicit `--detekt` or by type. It works the same for Java-over-
178
+ Gradle. LOC: `src/main` = prod; `src/test` AND custom source sets (`src/
179
+ integrationTest`, `src/functionalTest` — any `src/*Test`) = test
180
+ (`.kt`/`.kts`/`.java`). The JaCoCo requirement: `jacoco` plugin +
181
+ `jacocoTestReport { reports { xml.required = true } }`.
182
+ **Watch out for detekt**: do NOT chain it to the test command — its default is `maxIssues: 0`,
183
+ meaning ONE finding breaks the build and the test run would read red even though
184
+ the tests pass. Run `./gradlew detekt` separately, before the `ingest-gate`
185
+ (the lint gate has its own channel).
186
+
187
+ Swift (`type: swift`, kit 1.9.0):
188
+
189
+ | data | command | file |
190
+ |-------------|---------------------------------------------------|---------|
191
+ | coverage | `swift test --enable-code-coverage` + `llvm-cov export -format=lcov ... > coverage/lcov.info` | `coverage/lcov.info` (lcov — same parser as Flutter/node) |
192
+ | test count | `swift test --xunit-output reports/junit.xml` | `reports/junit.xml` **+ `reports/junit-swift-testing.xml`** (they are SUMMED — see note) |
193
+ | swiftlint | `swiftlint lint --reporter checkstyle > reports/swiftlint.xml` | `reports/swiftlint.xml` (checkstyle format: error→HIGH · warning→MEDIUM; absolute paths relativized) |
194
+
195
+ `ingest-gate` with explicit `--swiftlint` or by type. LOC: SwiftPM convention
196
+ (`Sources/` = prod, `Tests/` + `*Tests.swift` = test). The lcov export needs
197
+ the `llvm-cov export` step against the tests binary (the `.profdata` alone is
198
+ not enough) — leave it in your repo's `test_command_swift`; on macOS it is
199
+ `xcrun llvm-cov`, on Linux plain `llvm-cov` (and the binary lives in
200
+ `.build/debug/<Pkg>PackageTests.xctest`).
201
+ **Watch out for Swift Testing**: `--xunit-output` writes the XCTest results to
202
+ `junit.xml` and the **Swift Testing** ones (the default in Swift 6) to a SECOND
203
+ file `junit-swift-testing.xml` — the engine sums BOTH; if it only read the
204
+ first, a Swift 6 package would measure `tests=0` and a real failure would be
205
+ invisible (fail-open).
206
+
207
+ ## Installation
208
+
209
+ **Preferred public install (kit 1.40.1): use npm/npx.** The npm package is a
210
+ thin router over the canonical Python installer, so adoption is easy without
211
+ duplicating installer logic. You need Node/npm for this path and Python 3.8+ for
212
+ the underlying installer.
213
+
214
+ Codex-only machine:
215
+
216
+ ```bash
217
+ npx @andresmassello/uscha@latest version
218
+ npx @andresmassello/uscha@latest install --target codex --dry-run
219
+ npx @andresmassello/uscha@latest install --target codex
220
+ npx @andresmassello/uscha@latest doctor --target codex
221
+ ```
222
+
223
+ Machine used by both Codex and Claude:
224
+
225
+ ```bash
226
+ npx @andresmassello/uscha@latest install --target both --dry-run
227
+ npx @andresmassello/uscha@latest install --target both
228
+ npx @andresmassello/uscha@latest doctor --target both
229
+ ```
230
+
231
+ **Repo checkout install still works and is preferred when developing the kit
232
+ itself.** One interface, two adapters: Codex gets a personal local plugin
233
+ (`~/plugins/uscha` + `~/.agents/plugins/marketplace.json`), Claude gets global
234
+ skills/hooks under `~/.claude`. Use `--dry-run` first on every new machine.
235
+
236
+ ```bash
237
+ python uscha-kit/install-uscha.py version
238
+ python uscha-kit/install-uscha.py install --target codex --dry-run
239
+ python uscha-kit/install-uscha.py install --target codex
240
+ python uscha-kit/install-uscha.py doctor --target codex
241
+ ```
242
+
243
+ For the machine where you DEVELOP the kit, use links instead of copies so the
244
+ installed skills follow the canonical repo after `git pull`:
245
+
246
+ ```bash
247
+ python uscha-kit/install-uscha.py install --target both --mode link --dry-run
248
+ python uscha-kit/install-uscha.py install --target both --mode link
249
+ ```
250
+
251
+ Prepare a repo after the machine install:
252
+
253
+ ```bash
254
+ npx @andresmassello/uscha@latest init --repo <repo> --dry-run
255
+ npx @andresmassello/uscha@latest init --repo <repo>
256
+ # or from a checkout:
257
+ python uscha-kit/install-uscha.py init --repo <repo>
258
+ ```
259
+
260
+ What remains PER PROJECT (state, not installable): `uscha.config.json` in the
261
+ repo root where you run the run (the `path` values are relative to there ? and your declared
262
+ quality bar lives there), the `QA-LEDGER.json`, the `ACCEPTANCE.md`, and for migration
263
+ work the `.gitattributes` from `templates/` (`*.approved.* binary`).
264
+
265
+ **Legacy/manual install** still works: copy `.claude/` and `uscha.config.json`
266
+ per project, or copy the `uscha-*` skills to `~/.claude/skills/`. Prefer the
267
+ installer unless you are debugging the installer itself.
268
+
269
+ **Claude Code plugin** remains available for Claude Code users:
270
+
271
+ ```
272
+ /plugin marketplace add andresmassello/uscha
273
+ /plugin install uscha@uscha
274
+ ```
275
+
276
+ Updates: `/plugin update uscha@uscha` for Claude Code plugin installs,
277
+ `npx @andresmassello/uscha@latest install ...` for npm installs, or rerun
278
+ `install-uscha.py install ...` from a repo checkout.
279
+
280
+
281
+
282
+ **Verify the installation with `doctor`** (kit 1.22.0, in the spirit of flutter doctor —
283
+ Windows and Linux, ASCII output, exit 1 only on errors):
284
+
285
+ ```bash
286
+ python uscha-kit/install-uscha.py doctor --target both
287
+ # or engine-only: python3 ~/.claude/skills/uscha-devloop/qa_ledger.py doctor
288
+ ```
289
+
290
+ It checks: Python >=3.8 · git · the 8 skills alongside the engine (frontmatter
291
+ verified) · the INV-GOLDEN-01 hook (present + registered in settings.json +
292
+ powershell/pwsh interpreter) · and if there is a `uscha.config.json` in the cwd:
293
+ config parseable, ACCEPTANCE with AC-IDs, ledger integrity, the QA skills
294
+ from `qa_tools_order` (the loop orchestrates them without bringing them in) and the
295
+ primary toolchain of each repo by type (its absence is a WARNING — it may live only in CI).
296
+
297
+ ## Configure
298
+
299
+ Edit `uscha.config.json`:
300
+
301
+ - `repos[]`: name, `path` (relative to the primary repo), `type` (`maven`|`flutter`|`python`|`node`|`go`|`rust`|`dotnet`|`cpp`|`gradle`|`swift`).
302
+ - `defaults.coverage_threshold`: triggers the characterization phase if below it.
303
+ - `defaults.severity_gate`: which severities block (default BLOCKER/CRITICAL/HIGH).
304
+ - `defaults.id_granularity`: `line` or `file` (default `file`: more stable if you refactor a lot).
305
+ - `defaults.acceptance_file`: path of the **acceptance task list** (markdown checkboxes) that feeds the ADR completion of readiness. Default `ACCEPTANCE.md`.
306
+ - `defaults.constitution_file`: path of the **CONSTITUTION** (inviolable invariants). Default `CONSTITUTION.md`.
307
+ - `defaults.rebuild.coverage_tolerance`: coverage points the rebuild can drop without penalty (default 5).
308
+ - `defaults.readiness_weights` / `readiness_caps` / `static_gate_zero_at`: weights and caps of the KPI.
309
+ - `defaults.execution_policy`: phase-level routing metadata (`method`, `tier`, `model`, `effort`) shown by `execution-policy` and Mirador. It guides the operator; it does **not** affect readiness.
310
+ - Discovery intake commands (`production-finding`, `spec-doubt`, `spec-change-request`) persist post-merge production facts, SPEC doubts, and human contract-change bridges so the next cycle reopens discovery/SPEC instead of hiding reality in narration.
311
+ - ADRs may use `Status: Experiment` when a decision is an explicit, measured hypothesis. `dashboard --json`/Mirador expose `adr_status`, feedback/review metadata, malformed/expired counts; this is advisory visibility, not readiness scoring.
312
+ - `defaults.max_iterations`, `tools_per_cycle`, test commands.
313
+
314
+ ## Multi-repo: mounting the other repos
315
+
316
+ The skill runs from the primary repo; the others are mounted in the session:
317
+
318
+ ```bash
319
+ cd <repo-primario>
320
+ claude --add-dir ../backend-api --add-dir ../mobile-app
321
+ ```
322
+
323
+ (or `additionalDirectories` in `.claude/settings.json`).
324
+
325
+ ## Use
326
+
327
+ Inside Claude Code, invoke the orchestrator (with the ADR/PLAN ready or ask it for one):
328
+
329
+ ```
330
+ /dev-loop
331
+ ```
332
+
333
+ The skill handles the phases on its own: plan → coverage gate → (characterization if needed)
334
+ → build → per-repo QA loop → integration → verify → PR (stops at the merge, you approve
335
+ it) → smoke list. It logs every step in `QA-LEDGER.json`. At the end:
336
+
337
+ ```
338
+ /uscha-sysdoc # generates docs/system-deck.html (CEO + technical, navigable)
339
+ ```
340
+
341
+ ## Quick check (engine dry run, without the skill)
342
+
343
+ To confirm the engine parses your reports correctly BEFORE trusting it with the loop:
344
+
345
+ ```bash
346
+ cd <repo-primario>
347
+ QL=".claude/skills/uscha-devloop/qa_ledger.py"
348
+
349
+ python3 $QL --help # see subcommands
350
+ python3 $QL init --config uscha.config.json # creates QA-LEDGER.json
351
+
352
+ # run your build with the reports, then:
353
+ python3 $QL snapshot --repo backend-api --phase pre
354
+ python3 $QL check-coverage --repo backend-api # exit 0 = OK, 1 = below threshold
355
+ python3 $QL ingest-gate --repo backend-api --iteration 1
356
+ python3 $QL summary # human summary
357
+ python3 $QL summary --json # includes post_merge_calibration
358
+ ```
359
+
360
+ If `snapshot`/`ingest-gate` say "no report found", it's because the build hasn't generated
361
+ the XML yet — run `mvn test` with the plugins active first.
362
+
363
+ ## Readiness KPI (when finishing any task)
364
+
365
+ Shows the "ready for release" status as a 0..100 score, **based on the state of the
366
+ result, not on effort spent**:
367
+
368
+ ```bash
369
+ python3 $QL readiness --acceptance ACCEPTANCE.md
370
+ python3 $QL readiness --json # consumed by sys-doc (traffic-light widget)
371
+ python3 $QL execution-policy --phase qa # one-line methodology/model/effort routing
372
+ python3 $QL production-finding --repo backend-api --severity HIGH --title "..." --evidence "..."
373
+ python3 $QL spec-doubt --repo backend-api --kind spec-wrong --note "..." --evidence "..."
374
+ python3 $QL spec-change-request --repo backend-api --source SD-001 --requested-change "..." --evidence "..."
375
+ ```
376
+
377
+ - Dimensions/weights: **acceptance traced (MEASURED) 30**, ADR/checkboxes 15, coverage 15,
378
+ static gate 20, convergence 10, integration 10. The **ADR completion** comes from your
379
+ acceptance task list (checkboxes `- [x]`/`- [ ]`, read-only) — count the whole file
380
+ (CLI default); `--section` only if you verified that the heading matches
381
+ exactly (a mismatch silently zeroes the dimension).
382
+ - **Traceability (kit 1.10.0, the dominant dimension)**: each criterion carries a stable ID
383
+ — `- [ ] AC-01 — when X then Y`. A criterion closes MEASURED only when there exists
384
+ ≥1 GREEN testcase with its tag in the name (`test_ac1_x` / `testAC01X` / `"AC-01: ..."`
385
+ — normalized by number: `AC-01 == AC_1 == ac1`) in the already-ingested JUnit reports,
386
+ and no tagged testcase in red. The checkbox is NARRATIVE; the testcase is FACT: an
387
+ `[x]` without a green test appears as `narrated_only` and does NOT close. Anti-Goodhart: the agent
388
+ can no longer raise the KPI by polishing coverage — only by closing criteria with named
389
+ tests. `spec-check --acceptance ACCEPTANCE.md` validates the structure as a FACT (zero
390
+ traceable criteria or duplicate IDs = BLOCKED). Without IDs: it falls back to the checkbox ratio
391
+ with a warning (legacy, incremental adoption). Flutter doesn't emit JUnit: its criteria don't
392
+ close measured (documented limitation).
393
+ - A lintable repo whose static gate **never ran** scores that dimension UNMEASURED (0.0)
394
+ — silence is not success.
395
+ - **Hard caps** (they override the ceiling): tests in red → ≤35, open BLOCKER/CRITICAL → ≤65,
396
+ unresolved escalation → ≤75 (holds until `resolve-escalation`, a recorded event).
397
+ - Bands: `<50 NOT READY` · `50–79 IN PROGRESS` · `80–94 RELEASE CANDIDATE` · `95–100 READY`.
398
+ - Multi-repo: per-repo and aggregate (min() for blockers, LOC-weighted for quality).
399
+ - Cycles/regressions are **churn** (process health), reported separately and never
400
+ raise readiness.
401
+
402
+ ## Rebuild test (SPEC completeness)
403
+
404
+ A different question for the ledger: not "did this build pass?" (correctness) but "is the SPEC
405
+ enough to regenerate the system?" (completeness). For profiles C+/E or periodic in CI.
406
+
407
+ ```bash
408
+ # 1) in the ORIGINAL tree: capture the signature the rebuild must match
409
+ python3 $QL rebuild --mode baseline --config uscha.config.json # → REBUILD-BASELINE.json
410
+ # 2) in a CLEAN tree / new session: regenerate ONLY the production code from
411
+ # SPEC/ADR/ACCEPTANCE, PRESERVING the tests, and run the suite.
412
+ # 3) score the regenerated tree against the baseline
413
+ python3 $QL rebuild --mode compare --baseline REBUILD-BASELINE.json # exit 0 = COVERS
414
+ python3 $QL rebuild --mode compare --baseline REBUILD-BASELINE.json --json # consumed by sys-doc
415
+ ```
416
+
417
+ - Dimensions/weights: tests 60, acceptance 20, coverage 15, surface 5. The dominant signal
418
+ is the **preserved suite**: a test that passed and fails in the regenerated code = behavior
419
+ the SPEC left implicit.
420
+ - Verdicts: `COVERS ≥90` · `PARTIAL ≥70` · `DIVERGE <70`. The score lists the concrete
421
+ **gaps** — feed them back into the SPEC and re-run. Divergence is a spec hole, not a code bug.
422
+
423
+ ## Simplicity gate — "Reduce" (minimality of the change)
424
+
425
+ The **Simplicity** invariant of the CONSTITUTION made a deterministic gate: it scores the *diff*
426
+ (not CC by AST — they are measurable proxies: minimality, nesting, new abstractions).
427
+
428
+ ```bash
429
+ git diff --unified=0 <base> | python3 $QL simplicity-check --config uscha.config.json
430
+ python3 $QL simplicity-check --from-git --base main # uses git for you
431
+ python3 $QL simplicity-check --diff changes.diff --json # consumed by sys-doc / CI
432
+ ```
433
+
434
+ - Dimensions/weights: diff_size 35, nesting 30, net_growth 20, fan_out 8, blob 7
435
+ (abstraction does NOT weigh in the score — it's a guessy proxy, kept as a metric + advisory flag).
436
+ - Verdicts: `SIMPLE ≥85` · `ACCEPTABLE ≥65` · `OVERBUILT <65` (exit 1 = BLOCKER: trim and re-run).
437
+ A gross excess (2× budget, or very deep nesting) caps the score at 60 no matter what.
438
+ - **Tests OUT of the budget** (kit 1.11.0): the test files (conventions of the
439
+ 9 stacks) are counted and reported separately (`test_lines_added`) but do not gate — writing
440
+ tests never pushes the diff to OVERBUILT (deleting them is already blocked by gate-check).
441
+ - The flags tell you what to trim (guard clauses, speculative types/layers, giant hunks).
442
+ - Budgets in `defaults.simplicity`; adjustable per risk profile. 2-space → `--indent-width 2`.
443
+
444
+ ## Ledger subcommands
445
+
446
+ `doctor · init · snapshot · check-coverage · log-step · ingest-gate · log-gate · flag-blocker ·
447
+ converged · oscillation · escalate · resolve-escalation · summary · readiness · execution-policy · production-finding · spec-doubt · rebuild ·
448
+ simplicity-check · pit-check · gate-check · spec-check · golden-diff · regression-check ·
449
+ phase · rubric-ingest · doctor` — each with `--help`.
450
+
451
+ The **fact gates** (golden-diff, gate-check, pit-check, simplicity) are PERSISTED with
452
+ `log-gate`: a fail blocks convergence and caps readiness ≤65 via the ledger. A CONSTITUTION
453
+ violation is recorded with `flag-blocker` (same effect, until `--resolve`).
454
+
455
+ ## Notes
456
+
457
+ - **It doesn't merge on its own.** It creates the PR and stops; the merge is yours.
458
+ - **ADR experiments are visible hypotheses.** `Status: Experiment` requires Hypothesis, Feedback Signal, Review By/Trigger, Promote Criteria and Rollback/Supersede Criteria. Missing or expired metadata is shown by `dashboard --json`/Mirador as advisory, not as a hard PR gate.
459
+ - **Tracked `.md` protocol.** Before touching CLAUDE.md / plan/delta docs / docs/adr,
460
+ the skill asks for the current version of the file (it doesn't regenerate and overwrite real progress).
461
+ - `ingest-gate` credits a fix only if the report EXISTS and came back clean; a missing
462
+ report = the gate didn't run (it doesn't invent zeros).
463
+
464
+ ## Setting up the workbench (generic setup)
465
+
466
+ Before using the skills you need the base toolchain (Claude Code + Python + git/gh +
467
+ the skills installed). It's all in **`WORKBENCH.md`**: installation, verification and
468
+ update, without the specifics of each stack (Java/MSSQL/linters = per-repo adapter).
469
+
470
+ - What I have installed: `bash workbench-doctor.sh`
471
+ - Kit version: `python uscha-kit/install-uscha.py version` or `cat VERSION`
472
+
473
+ ## Templates for the repo (so the repo becomes "methodology-ready")
474
+
475
+ The kit installs the skills; these templates leave the **repo** ready. Copy them to the root
476
+ of the repo where you're going to work:
477
+
478
+ ```
479
+ cp uscha-kit/templates/CLAUDE.md <repo>/CLAUDE.md # permanent repo protocol
480
+ cp uscha-kit/templates/CONSTITUTION.md <repo>/CONSTITUTION.md # inviolable invariants (fill in the domain)
481
+ cp -r uscha-kit/templates/docs <repo>/docs # docs/adr scaffold
482
+ # if you use other agents besides Claude Code: cp <repo>/CLAUDE.md <repo>/AGENTS.md
483
+ ```
484
+
485
+ Then, complete the "Project adapter" block of the `CLAUDE.md` with the build/test/gate
486
+ commands of that stack (it's the only project-specific thing).
487
+
488
+ ## Verify the agent reads the kit
489
+
490
+ Inside Claude Code, ask it:
491
+
492
+ ```
493
+ List the active rules from CLAUDE.md and the available skills.
494
+ ```
495
+
496
+ The protocol rules and the commands /uscha-discovery /uscha-adr-refine
497
+ /uscha-devloop /uscha-sysdoc should appear. (For the machine's toolchain: `bash uscha-kit/workbench-doctor.sh`.)
@@ -0,0 +1 @@
1
+ uscha-kit 1.40.1