@lifeaitools/rdc-skills 0.34.0 → 0.35.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/.claude-plugin/plugin.json +284 -1
- package/VALIDATOR-ARCHITECTURE.md +534 -0
- package/commands/analyze-tests.md +11 -0
- package/commands/check-clean-code.md +11 -0
- package/commands/check-packages.md +10 -0
- package/commands/compare-compliance.md +14 -0
- package/commands/full-analysis.md +50 -0
- package/commands/get-refactoring-plan.md +13 -0
- package/commands/quick-check.md +13 -0
- package/commands/recover.md +149 -0
- package/commands/review-arch.md +12 -0
- package/commands/review.md +12 -113
- package/commands/suggest-patterns.md +11 -0
- package/commands/validate-solid.md +11 -0
- package/package.json +14 -2
- package/scripts/architecture-score.mjs +157 -0
- package/scripts/clean-code-score.mjs +177 -0
- package/scripts/duplication-score.mjs +66 -0
- package/scripts/lib/architecture-scoring.mjs +695 -0
- package/scripts/lib/clean-code-scoring.mjs +258 -0
- package/scripts/lib/duplication-scoring.mjs +238 -0
- package/scripts/lib/language-plugin.mjs +82 -0
- package/scripts/lib/package-metrics.mjs +439 -0
- package/scripts/lib/pattern-scoring.mjs +351 -0
- package/scripts/lib/plugins/treesitter.mjs +1182 -0
- package/scripts/lib/plugins/typescript.mjs +672 -0
- package/scripts/lib/refactoring-scoring.mjs +307 -0
- package/scripts/lib/solid-scoring.mjs +101 -0
- package/scripts/lib/test-smell-scoring.mjs +581 -0
- package/scripts/lib/vendor/codeflow-parser/.source-commit +1 -0
- package/scripts/lib/vendor/codeflow-parser/grammars.d.ts +23 -0
- package/scripts/lib/vendor/codeflow-parser/grammars.js +57 -0
- package/scripts/lib/vendor/codeflow-parser/memberFacts.d.ts +274 -0
- package/scripts/lib/vendor/codeflow-parser/memberFacts.js +1117 -0
- package/scripts/lib/vendor/codeflow-parser/nativeParser.d.ts +115 -0
- package/scripts/lib/vendor/codeflow-parser/nativeParser.js +759 -0
- package/scripts/lib/vendor/codeflow-parser/package.json +3 -0
- package/scripts/lib/vendor/codeflow-parser/xmlParser.d.ts +77 -0
- package/scripts/lib/vendor/codeflow-parser/xmlParser.js +400 -0
- package/scripts/package-metrics-cli.mjs +112 -0
- package/scripts/pattern-score.mjs +143 -0
- package/scripts/refactoring-score.mjs +253 -0
- package/scripts/solid-score.mjs +337 -0
- package/skills/architecture-reviewer/SKILL.md +287 -0
- package/skills/clean-code-analyzer/SKILL.md +147 -0
- package/skills/package-design/SKILL.md +118 -0
- package/skills/pattern-advisor/SKILL.md +237 -0
- package/skills/pattern-refactoring-guide/SKILL.md +262 -0
- package/skills/review/SKILL.md +29 -0
- package/skills/solid-validator/SKILL.md +92 -0
- package/skills/testing-strategy/SKILL.md +132 -0
- package/tests/lib/architecture-scoring.test.mjs +335 -0
- package/tests/lib/clean-code-scoring.test.mjs +241 -0
- package/tests/lib/duplication-scoring.test.mjs +144 -0
- package/tests/lib/fixtures.mjs +58 -0
- package/tests/lib/package-metrics.test.mjs +241 -0
- package/tests/lib/pattern-scoring.test.mjs +251 -0
- package/tests/lib/refactoring-scoring.test.mjs +264 -0
- package/tests/lib/solid-scoring.test.mjs +291 -0
- package/tests/lib/test-smell-scoring.test.mjs +281 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pattern-refactoring-guide
|
|
3
|
+
description: >-
|
|
4
|
+
Usage `rdc:pattern-refactoring-guide <path>` — turns a pattern-advisor
|
|
5
|
+
recommendation (or a solid-validator/architecture-reviewer finding) into a
|
|
6
|
+
concrete before/after refactor plan. Produces a plan, does not apply it.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
10
|
+
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
11
|
+
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
12
|
+
|
|
13
|
+
# pattern-refactoring-guide — Concrete Refactor Plans
|
|
14
|
+
|
|
15
|
+
## Input contract
|
|
16
|
+
|
|
17
|
+
Takes a finding from one of two sources — an upstream surface (a
|
|
18
|
+
`pattern-advisor` recommendation, a `solid-validator` low-score criterion, an
|
|
19
|
+
`architecture-reviewer` boundary violation), OR this skill's own mechanical
|
|
20
|
+
detection layer, `rdc-refactoring-score` (`scripts/refactoring-score.mjs`) —
|
|
21
|
+
and produces a step-ordered plan a build agent can execute. It does not
|
|
22
|
+
invent new findings beyond what a cited detector reported; it does not apply
|
|
23
|
+
the refactor itself.
|
|
24
|
+
|
|
25
|
+
## Procedure
|
|
26
|
+
|
|
27
|
+
1. **Find or receive the finding.** Two paths, either is a valid start:
|
|
28
|
+
- **Upstream finding already exists** — an emitted `pattern-advisor` /
|
|
29
|
+
`solid-validator` / `architecture-reviewer` result. Skip to step 2.
|
|
30
|
+
- **No finding yet — detect candidates directly.** Run
|
|
31
|
+
`node scripts/refactoring-score.mjs <path> --format json` (installed
|
|
32
|
+
bin: `rdc-refactoring-score`). It is a real, deterministic, AST-based
|
|
33
|
+
scanner — NOT this skill reasoning about the code — covering nine
|
|
34
|
+
refactoring types: `extract-method`, `extract-class`,
|
|
35
|
+
`introduce-parameter-object`, `replace-magic-number`,
|
|
36
|
+
`consolidate-duplicate-code`, `decompose-conditional`,
|
|
37
|
+
`strategy-transform`, `factory-transform`, `null-object-transform`.
|
|
38
|
+
Detection logic: `scripts/lib/refactoring-scoring.mjs`. Every finding
|
|
39
|
+
carries a real file:line and a mechanical effort signal (see step 5).
|
|
40
|
+
A "zero findings" result on a real target is only trustworthy once the
|
|
41
|
+
tool's own positive control passed (it runs one automatically and
|
|
42
|
+
reports `effortScope.positiveControlOk` in JSON output / the "Effort
|
|
43
|
+
scan" line in text output — per
|
|
44
|
+
`.claude/rules/prove-absence-positive-control.md`, treat a failed
|
|
45
|
+
control as "unmeasured", never as "clean").
|
|
46
|
+
Two thresholds here are DELIBERATELY DIFFERENT from
|
|
47
|
+
`clean-code-analyzer`'s mechanical rules even though they read the same
|
|
48
|
+
underlying fact: `extract-method` fires at >25 statements (this tool)
|
|
49
|
+
vs. clean-code's F1 at >20 statements; `introduce-parameter-object`
|
|
50
|
+
fires at >4 params (this tool) vs. clean-code's F2 at >3 params. Both
|
|
51
|
+
numbers are architecture-toolkit's own real thresholds for their
|
|
52
|
+
respective domains (`refactoring-analyzer.ts:49,171`) — cite the
|
|
53
|
+
difference, do not silently merge the two tools' output.
|
|
54
|
+
|
|
55
|
+
2. **Restate the finding** exactly as reported by the upstream skill or by
|
|
56
|
+
`rdc-refactoring-score` — file:line, the score/violation, the evidence.
|
|
57
|
+
|
|
58
|
+
3. **Write the BEFORE** — the actual current code (a real excerpt, not a
|
|
59
|
+
paraphrase).
|
|
60
|
+
|
|
61
|
+
4. **Write the AFTER** — the concrete target shape, real code, not a
|
|
62
|
+
description of code.
|
|
63
|
+
|
|
64
|
+
5. **Order the steps** so each one leaves the codebase in a working state:
|
|
65
|
+
- Extract the new shape ALONGSIDE the old one first (new class/module,
|
|
66
|
+
unused by anything yet).
|
|
67
|
+
- Migrate ONE call site, verify.
|
|
68
|
+
- Migrate remaining call sites incrementally, verifying after each.
|
|
69
|
+
- Delete the old shape only after zero call sites remain — verify with
|
|
70
|
+
the same call-graph-with-positive-control method `clean-code-analyzer`
|
|
71
|
+
uses for dead code, don't assume.
|
|
72
|
+
|
|
73
|
+
6. **Estimate effort — low/medium/high — using named criteria, not a gut
|
|
74
|
+
call.** Adapted from the `estimatedEffort` field actually shipped in
|
|
75
|
+
[OnSightTeam/architecture-toolkit](https://github.com/OnSightTeam/architecture-toolkit)'s
|
|
76
|
+
`src/agents/pattern-refactoring-guide/tools/*.ts`, extended with the
|
|
77
|
+
criterion its own examples expose a gap in (see below). When the finding
|
|
78
|
+
came from `rdc-refactoring-score` (step 1), don't re-derive this table by
|
|
79
|
+
hand — the tool already computed the mechanical half of it: it re-walks
|
|
80
|
+
the SAME cross-file reference-graph mechanism `clean-code-scoring.mjs`'s
|
|
81
|
+
G9 dead-export check uses (`plugin.referenceSitesOf`, built on
|
|
82
|
+
`findReferencesAsNodes`, gated behind its own positive control) and
|
|
83
|
+
reports each unit's real call-site count and whether any reference
|
|
84
|
+
crosses a `packages/*`/`apps/*` boundary. The one criterion below it
|
|
85
|
+
CANNOT compute — a cross-cutting invariant that isn't locally checkable —
|
|
86
|
+
is always a human judgment call; the tool flags `invariantCheckRequired:
|
|
87
|
+
true` as a reminder wherever boundary-crossing or >15 call sites already
|
|
88
|
+
pushed it to High, but never claims to have evaluated the invariant
|
|
89
|
+
itself.
|
|
90
|
+
|
|
91
|
+
| Effort | Criteria (any ONE qualifies) | toolkit precedent |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| **Low** | Single file, ≤3 call sites, no package-boundary crossing, mechanical (rename, extract-constant, extract-condition-to-named-method). | `introduce_parameter_object` (`refactoring-analyzer.ts:179`), `replace_magic_number` (`:234`), `decompose_conditional` (`code-smell-refactoring-guide.ts:121`) — all single-function, in-place edits. |
|
|
94
|
+
| **Medium** | 4-15 call sites within the SAME package, OR a new abstraction introduced but consumed only inside the current package/module. | `extract_method` (`refactoring-analyzer.ts:57`) — "may need to pass many parameters" but stays in one file. `consolidate_duplicate_code` (`code-smell-refactoring-guide.ts:59`), Strategy/Factory/Null-Object pattern transforms (`pattern-transformation-guide.ts:52,110,168`) — new types, but all call sites are local. |
|
|
95
|
+
| **High** | **Crosses a package boundary** (the extraction moves logic into or out of a different `packages/*`/`@regen/*` workspace), OR >15 call sites, OR — the criterion the toolkit's own field values don't cover — **the target carries a cross-cutting invariant that isn't locally checkable** (event ordering, transactional/append-only integrity, freeze-after-mutation semantics): call-site count alone UNDER-counts effort here, because migrating the invariant correctly matters more than how many call sites exist. | `extract_class` (`refactoring-analyzer.ts:116`) is the toolkit's own high-effort case — ">15 methods... requires careful dependency management" is the package-boundary risk stated in words even though the field only tracks method count. |
|
|
96
|
+
|
|
97
|
+
**Call-site count is not sufficient on its own** — see the worked example
|
|
98
|
+
below, where the real count (8) sits in the Medium range by call-site
|
|
99
|
+
count alone, yet the target is still HIGH effort because of the
|
|
100
|
+
package-boundary and invariant clauses.
|
|
101
|
+
|
|
102
|
+
7. **Name the test that must go from red to green** (or the golden-capture
|
|
103
|
+
delta that must appear) at each step — route through `testing-strategy`
|
|
104
|
+
for the right level/shape if the finding doesn't already specify one.
|
|
105
|
+
|
|
106
|
+
8. **Every step names what proves it succeeded — no exceptions, including
|
|
107
|
+
intermediate steps.** This is stricter than the toolkit's own shipped
|
|
108
|
+
examples: `refactoring-analyzer.ts`'s own `extract_method` plan has a
|
|
109
|
+
`validation` field on steps 1, 3, 4 (`:69`, `:79`, `:84`) but step 2 — "extract
|
|
110
|
+
each section into a separate method" (`:72-75`) — carries only a `code`
|
|
111
|
+
field and NO validation, i.e. the toolkit's own reference plan has a step
|
|
112
|
+
whose success is unstated. That gap is exactly what this rule closes: a
|
|
113
|
+
step that only shows the code to write, with no stated proof it worked,
|
|
114
|
+
is incomplete here even if the toolkit's own precedent shipped that way.
|
|
115
|
+
Every step gets one of: a specific test name, a `tsc`/lint exit code, a
|
|
116
|
+
call-site grep returning zero, or an explicit "no assertion possible,
|
|
117
|
+
inspect manually" — never a bare code sample standing in for proof.
|
|
118
|
+
|
|
119
|
+
9. **Report:**
|
|
120
|
+
```
|
|
121
|
+
## Refactor Plan — <finding source>: <file:line>
|
|
122
|
+
### Before
|
|
123
|
+
### After
|
|
124
|
+
### Effort: low/medium/high — <which criterion triggered it>
|
|
125
|
+
### Steps (each leaves the tree working; each names its own proof)
|
|
126
|
+
### Verification per step
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Rules
|
|
130
|
+
|
|
131
|
+
- Never skip straight from BEFORE to AFTER in one step for anything touching
|
|
132
|
+
more than one call site — the incremental-migration order is the point;
|
|
133
|
+
a plan that says "rewrite it" is not a plan.
|
|
134
|
+
- A plan with no verification step per stage is incomplete — and "per
|
|
135
|
+
stage" means literally every step, not just the final one; a step with a
|
|
136
|
+
code sample and no proof is a rewrite instruction wearing a plan's
|
|
137
|
+
clothing.
|
|
138
|
+
- Never report effort from call-site count alone — always check the
|
|
139
|
+
package-boundary and cross-cutting-invariant criteria too; a low
|
|
140
|
+
call-site count on an invariant-bearing target is still HIGH.
|
|
141
|
+
- This skill does not execute the plan — hand off to `rdc:build`/`rdc:fixit`.
|
|
142
|
+
|
|
143
|
+
## Worked Example — extracting `rdc-harness`'s `Harness` god-object
|
|
144
|
+
|
|
145
|
+
Chained input: the `architecture-reviewer` worked example (see that skill's
|
|
146
|
+
own SKILL.md) found `Harness` in
|
|
147
|
+
[`C:/Dev/rdc-harness/packages/core/src/index.mjs`](file:///C:/Dev/rdc-harness/packages/core/src/index.mjs)
|
|
148
|
+
reimplementing transaction/delivery/deploy/orchestration logic inline instead
|
|
149
|
+
of delegating to the sibling packages that already exist for each. `rdc-
|
|
150
|
+
refactoring-score` independently detects the SAME target via step 1's own
|
|
151
|
+
mechanical path — a real run (`node scripts/refactoring-score.mjs
|
|
152
|
+
C:/Dev/rdc-harness/packages/core/src/index.mjs`) reports:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
[extract-class] [high] Harness — 24 methods (over 15) — violates Single
|
|
156
|
+
Responsibility, candidate for Extract Class
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
24 methods, not the loose "+ 9 more methods" this example previously said —
|
|
160
|
+
the real count once `memberEntries()` visits constructors, getters/setters,
|
|
161
|
+
and arrow-property methods too, not just `cls.getMethods()`. This is that
|
|
162
|
+
finding turned into a plan.
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
## Refactor Plan — architecture-reviewer: packages/core/src/index.mjs:49-394 (Harness)
|
|
166
|
+
|
|
167
|
+
### Before
|
|
168
|
+
class Harness {
|
|
169
|
+
deploy({ handleId }) {
|
|
170
|
+
const { handle, target, snapshot } = this.#bind(handleId, 'deploy');
|
|
171
|
+
const approved = Object.values(snapshot.decisions).some(...);
|
|
172
|
+
if (!approved) throw new RefusedError(...);
|
|
173
|
+
const to = join(this.#root, 'artifacts', 'production', ...);
|
|
174
|
+
this.#adapters.writeArtifact({ from: src.path, to }); // inline delivery
|
|
175
|
+
this.#emit({ type: 'deployed', ... });
|
|
176
|
+
}
|
|
177
|
+
// + 23 more methods, same shape: inline logic that belongs to a sibling package
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
### After
|
|
181
|
+
class Harness {
|
|
182
|
+
constructor({ root, clock, adapters, deployPort } = {}) {
|
|
183
|
+
...
|
|
184
|
+
this.#deployPort = deployPort ?? new DefaultDeployPort({ root, adapters });
|
|
185
|
+
}
|
|
186
|
+
deploy({ handleId }) {
|
|
187
|
+
const { handle, target, snapshot } = this.#bind(handleId, 'deploy');
|
|
188
|
+
const receipt = this.#deployPort.deploy({ handle, target, snapshot });
|
|
189
|
+
this.#emit({ type: 'deployed', ...receipt });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// packages/deploy/src/default-deploy-port.mjs — owns the artifact-path/approval logic
|
|
193
|
+
|
|
194
|
+
### Effort: HIGH — package-boundary crossing and the invariant clause, NOT
|
|
195
|
+
call-site count. `rdc-refactoring-score`'s real reference-graph scan
|
|
196
|
+
(`plugin.referenceSitesOf('packages/core/src/index.mjs', 'Harness', ...)`,
|
|
197
|
+
positive control passed) reports **8 cross-file references**, all within
|
|
198
|
+
`packages/core` itself: `index.mjs:75`'s own internal factory call
|
|
199
|
+
(`new Harness(...)`), plus 7 in `packages/core/test/site-html-lifecycle.test.mjs`
|
|
200
|
+
— the `import { Harness, ... }` statement (line 20), 4 `new Harness(...)`
|
|
201
|
+
construction sites (lines 40/55/70/94), and 2 static-method calls,
|
|
202
|
+
`Harness.resume(...)` (line 142) and `Harness.replay(...)` (line 296). This
|
|
203
|
+
is a REAL demonstration of why the real reference-graph walk
|
|
204
|
+
(`findReferencesAsNodes`) is used here instead of a text grep: a naive
|
|
205
|
+
`grep -rn "new Harness(" --include="*.mjs"` finds only 5 of these 8 —
|
|
206
|
+
the import statement and both static-method calls are invisible to that
|
|
207
|
+
grep, and a `new Harness(...)` regex would silently undercount call sites on
|
|
208
|
+
any class whose API includes static factory/replay methods. By call-site
|
|
209
|
+
count alone (8, same package) criterion 2 says **Medium** — this example
|
|
210
|
+
previously speculated a call-site count of zero and a resulting "criterion 2
|
|
211
|
+
would say LOW", which the tool's real output does not bear out; correcting
|
|
212
|
+
that here matters because it is exactly the trap this rule exists to prevent
|
|
213
|
+
(see "Call-site count is not sufficient on its own" above). What actually drives
|
|
214
|
+
this to HIGH is criterion 3: the refactor's real target is package-boundary
|
|
215
|
+
crossing (the extraction moves deploy/delivery/transaction/orchestration
|
|
216
|
+
logic OUT of `packages/core` and INTO `packages/deploy`, `packages/delivery`,
|
|
217
|
+
etc. — those packages are not yet Harness's callers, but they become its
|
|
218
|
+
collaborators after the extract), plus the invariant clause — every method
|
|
219
|
+
must keep emitting to the same append-only event log with the same monotonic
|
|
220
|
+
`#seq`, and the file's own comments (`index.mjs:103-110`) document a prior
|
|
221
|
+
real bug where a spread copy silently defeated a freeze invariant. That
|
|
222
|
+
invariant is exactly the case a mechanical call-site count cannot see, which
|
|
223
|
+
is why `rdc-refactoring-score` reports `invariantCheckRequired: true`
|
|
224
|
+
whenever boundary-crossing or a >15-call-site result already pushes it to
|
|
225
|
+
High, but leaves the actual invariant judgment to this step.
|
|
226
|
+
|
|
227
|
+
### Steps (each leaves the tree working; each names its own proof)
|
|
228
|
+
1. Define `DeployPort`/`DeliveryPort`/`TransactionPort`/`OrchestrationPort`
|
|
229
|
+
interfaces (method signatures only, in `packages/deploy`, `packages/delivery`,
|
|
230
|
+
`packages/transaction`, `packages/orchestration` respectively) — unused by
|
|
231
|
+
`Harness` yet.
|
|
232
|
+
Validation: `npx tsc --noEmit` (or the repo's JS-equivalent lint) passes
|
|
233
|
+
with the new files added; zero import changes in `index.mjs` yet, so the
|
|
234
|
+
existing test suite is still 100% green with zero deltas.
|
|
235
|
+
2. Implement `DefaultDeployPort` in `packages/deploy`, moving `deploy()`'s
|
|
236
|
+
artifact-path + approval-check logic verbatim out of `Harness` into it.
|
|
237
|
+
Validation: a new unit test in `packages/deploy/test/` exercises
|
|
238
|
+
`DefaultDeployPort.deploy()` directly with a fixture snapshot/handle and
|
|
239
|
+
asserts the same receipt shape `Harness.deploy()` used to return.
|
|
240
|
+
3. Wire `Harness`'s constructor to accept an injected `deployPort` (default:
|
|
241
|
+
`new DefaultDeployPort(...)`), and change `deploy()` to call
|
|
242
|
+
`this.#deployPort.deploy(...)` instead of inline logic.
|
|
243
|
+
Validation: `packages/core/test/site-html-lifecycle.test.mjs` (the
|
|
244
|
+
existing suite) passes unmodified — same assertions, same call signature,
|
|
245
|
+
only the internal implementation moved.
|
|
246
|
+
4. Repeat steps 2-3 for `shipDev`→`DeliveryPort`, `requestProduction`/
|
|
247
|
+
`recordDecision`→`TransactionPort`, `createRun`/`createRepository`/
|
|
248
|
+
`createTarget`→`OrchestrationPort`, one method-group at a time.
|
|
249
|
+
Validation per group: same pattern as step 3 — the existing lifecycle
|
|
250
|
+
test suite passes unmodified after each group's swap, never after all
|
|
251
|
+
four at once.
|
|
252
|
+
5. Once all four ports are wired, grep `index.mjs` for `node:fs` and
|
|
253
|
+
`node:crypto` imports used OUTSIDE the four new ports.
|
|
254
|
+
Validation: the grep returns zero matches outside `#emit`/`#load` (the
|
|
255
|
+
event-log read/write, which is intentionally NOT extracted — it's the
|
|
256
|
+
spine, not a layering violation per this skill's own header) — this is
|
|
257
|
+
the positive-control check that no inline I/O was missed.
|
|
258
|
+
## Verdict: HIGH effort, 5 steps, each with a named proof; 8 call sites, all
|
|
259
|
+
WITHIN `packages/core` (zero from any OTHER package today) means the
|
|
260
|
+
migration is low-RISK to sequence but not low-EFFORT — the two are different
|
|
261
|
+
axes and this plan's Effort line says so explicitly.
|
|
262
|
+
```
|
package/skills/review/SKILL.md
CHANGED
|
@@ -106,6 +106,35 @@ description: "Usage `rdc:review [--unattended]` — Post-build quality gate: tsc
|
|
|
106
106
|
|
|
107
107
|
Under `RDC_TEST=1`: echo `[RDC_TEST] skipping code-review dispatch` and continue.
|
|
108
108
|
|
|
109
|
+
8c. **Form/fit/function gate — solid-validator + architecture-reviewer:**
|
|
110
|
+
|
|
111
|
+
⛔ **No CLEAN verdict without this pass either.** Step 8b catches logic,
|
|
112
|
+
security, and convention drift. This step catches a different failure
|
|
113
|
+
shape entirely: code that is logically correct and convention-clean but
|
|
114
|
+
architecturally wrong — the `Harness` case (a use-case orchestrator that
|
|
115
|
+
reimplements what sibling packages exist to do, scoring fine on every
|
|
116
|
+
generic metric while failing the one check that names the actual rule).
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
rdc-solid-score <modified-package-path> --diff origin/main --config <repo>/.solid-score.yml --format json
|
|
120
|
+
```
|
|
121
|
+
Read `regressions` (SOLID score dropped vs `origin/main`) and
|
|
122
|
+
`boundaryViolations` (the `satisfied: false` subset of `boundaryFindings`
|
|
123
|
+
— named Clean Architecture rule failures) from the
|
|
124
|
+
output. Either non-empty is a FORM/FIT failure — dispatch
|
|
125
|
+
`architecture-reviewer` (skill: "architecture-reviewer") for the judgment
|
|
126
|
+
half if a boundary finding needs a suggested fix, not just a named
|
|
127
|
+
violation.
|
|
128
|
+
|
|
129
|
+
**Severity gate:**
|
|
130
|
+
- Any regression, or any boundary violation → verdict cannot be CLEAN.
|
|
131
|
+
- No `.solid-score.yml` present in the target repo → note it and skip,
|
|
132
|
+
do not fabricate a config; a repo with no boundary rules configured has
|
|
133
|
+
nothing wrong to report, but say so explicitly rather than silently
|
|
134
|
+
passing.
|
|
135
|
+
|
|
136
|
+
Under `RDC_TEST=1`: echo `[RDC_TEST] skipping form/fit/function gate` and continue.
|
|
137
|
+
|
|
109
138
|
9. **Verification gate — dispatch the verify agent:**
|
|
110
139
|
After any fixes land, run the verify gate on every touched package. See `guides/agents/verify.md`.
|
|
111
140
|
Apply `guides/engineering-behavior.md` while reviewing: flag unnecessary abstraction, drive-by refactors, missing assumptions, hidden uncertainty, out-of-scope edits, and prose-only verification.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: solid-validator
|
|
3
|
+
description: >-
|
|
4
|
+
Usage `rdc:solid-validator <path> [--diff <ref>] [--config <file>]` — the
|
|
5
|
+
FORM corner of the form/fit/function model. Deterministic AST scoring of
|
|
6
|
+
all five SOLID letters (weighted sum, confidence-annotated) plus a separate
|
|
7
|
+
Clean Architecture boundary check. Real numbers, not LLM judgment — see
|
|
8
|
+
`scripts/solid-score.mjs` for the mechanism.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
12
|
+
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
13
|
+
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
14
|
+
|
|
15
|
+
# solid-validator — Deterministic SOLID + Clean Architecture Scoring
|
|
16
|
+
|
|
17
|
+
## Why this one is mechanical, not a dispatched agent
|
|
18
|
+
|
|
19
|
+
Three other skills in this suite (`architecture-reviewer`,
|
|
20
|
+
`clean-code-analyzer`, `package-design`) dispatch `pr-review-toolkit:code-reviewer`
|
|
21
|
+
for judgment; `pattern-advisor` and `pattern-refactoring-guide` produce
|
|
22
|
+
recommendations/plans without a dispatch, and `testing-strategy` recommends
|
|
23
|
+
a level/shape rather than scoring anything. This one does not dispatch a
|
|
24
|
+
judgment agent at all, on purpose: a ratchet gate that blocks a merge
|
|
25
|
+
on regression needs a NUMBER two runs can be diffed against, and an LLM
|
|
26
|
+
judgment call is not deterministic enough for that job. `git` is the baseline
|
|
27
|
+
— `--diff <ref>` scores each unit twice (at `<ref>` and in the working tree)
|
|
28
|
+
and gates on the delta, so nothing here persists a baseline file that can
|
|
29
|
+
drift out of sync with reality.
|
|
30
|
+
|
|
31
|
+
Dogfooded: `rdc-harness`'s `Harness` god-object scores 68.5/100 on the
|
|
32
|
+
weighted SOLID sum (SRP=40, three disconnected components across 21 members
|
|
33
|
+
— real cohesion signal) while the boundary check independently fails it on
|
|
34
|
+
all 6 of its declared ports. Two checks, proven necessary together — DIP's
|
|
35
|
+
generic concrete-instantiation-ratio metric alone did NOT catch the
|
|
36
|
+
violation (a class with almost no dependencies of any kind scores fine on
|
|
37
|
+
it), which is why the boundary rule exists as a separate, named check rather
|
|
38
|
+
than folded into DIP's score.
|
|
39
|
+
|
|
40
|
+
## Arguments
|
|
41
|
+
|
|
42
|
+
- `rdc:solid-validator <path>` — full score, current working tree
|
|
43
|
+
- `rdc:solid-validator <path> --diff <ref>` — regression gate against `<ref>`
|
|
44
|
+
- `rdc:solid-validator <path> --config <file>` — weights/thresholds/boundaries
|
|
45
|
+
|
|
46
|
+
## Procedure
|
|
47
|
+
|
|
48
|
+
1. **Run the scorer:**
|
|
49
|
+
```bash
|
|
50
|
+
rdc-solid-score <path> --diff <ref> --config <repo>/.solid-score.yml --format json
|
|
51
|
+
```
|
|
52
|
+
(installed globally via `npm link`/publish from this package — see
|
|
53
|
+
`package.json`'s `bin.rdc-solid-score`; falls back to
|
|
54
|
+
`node <rdc-skills-install-path>/scripts/solid-score.mjs` if the bin isn't
|
|
55
|
+
on PATH.)
|
|
56
|
+
|
|
57
|
+
2. **Read `results`** — per-unit SRP/OCP/LSP/ISP/DIP scores with confidence
|
|
58
|
+
(`high`/`low-medium`/`low` — OCP and LSP are heuristic by nature; report
|
|
59
|
+
the confidence, never hide it).
|
|
60
|
+
|
|
61
|
+
3. **Read `regressions`** (only present with `--diff`) — any unit whose
|
|
62
|
+
score dropped more than `diff.maxDecrease` (default 0, i.e. no regression
|
|
63
|
+
tolerated) versus the base ref, or any NEW unit below `diff.newUnitMin`.
|
|
64
|
+
|
|
65
|
+
4. **Read `boundaryFindings`** — named Clean Architecture dependency-rule
|
|
66
|
+
violations from the repo's configured `boundaries` list. Treat these as
|
|
67
|
+
equally load-bearing as a regression — they catch a different failure
|
|
68
|
+
shape (see the `Harness` case above).
|
|
69
|
+
|
|
70
|
+
5. **Read `unresolvedLanguages`** — files no registered plugin could parse.
|
|
71
|
+
Report them explicitly; do not silently treat them as passing. (Day-1
|
|
72
|
+
plugin: TypeScript/JavaScript via `ts-morph`. A Python plugin implementing
|
|
73
|
+
the same `lib/language-plugin.mjs` contract extends coverage without
|
|
74
|
+
touching this skill or the scoring core.)
|
|
75
|
+
|
|
76
|
+
6. **Report:**
|
|
77
|
+
```
|
|
78
|
+
## SOLID + Clean Architecture Score
|
|
79
|
+
| Unit | SRP | OCP | LSP | ISP | DIP | Total |
|
|
80
|
+
### Regressions (vs <ref>)
|
|
81
|
+
### Boundary violations
|
|
82
|
+
### Unresolved languages (no plugin — not silently passed)
|
|
83
|
+
## Verdict: CLEAN / HAS ISSUES
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Rules
|
|
87
|
+
|
|
88
|
+
- Never treat an `unresolvedLanguages` entry as a pass — it is unmeasured,
|
|
89
|
+
not clean.
|
|
90
|
+
- A boundary violation is a hard block regardless of the weighted score —
|
|
91
|
+
do not let a high SOLID total offset a missing required port import.
|
|
92
|
+
- Confidence is reported data, never a reason to omit OCP/LSP scores.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: testing-strategy
|
|
3
|
+
description: >-
|
|
4
|
+
Usage `rdc:testing-strategy <path>` — recommends the right TEST LEVEL and
|
|
5
|
+
SHAPE for a surface (unit/integration/live tier, assertion vs golden-capture),
|
|
6
|
+
not a specific test to write. The FUNCTION corner of the form/fit/function
|
|
7
|
+
model — solid-validator covers FORM, architecture-reviewer covers FIT, this
|
|
8
|
+
covers whether a surface's behavior is actually provable, and how.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
12
|
+
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
13
|
+
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
14
|
+
|
|
15
|
+
# testing-strategy — Test Level & Shape Recommendation
|
|
16
|
+
|
|
17
|
+
## The distinction this exists to enforce
|
|
18
|
+
|
|
19
|
+
A test can be green and prove nothing — this session's own history: a
|
|
20
|
+
proof-ledger claimed 144/144 while grading each row against evidence weaker
|
|
21
|
+
than the row demanded, because the tests were written to make a NAMED CLAIM
|
|
22
|
+
go green rather than to capture real behavior. This skill's job is to say,
|
|
23
|
+
BEFORE a test is written, what level and shape it needs to be honest —
|
|
24
|
+
catching that failure at design time instead of at a review months later.
|
|
25
|
+
|
|
26
|
+
## Level — pick the lowest that can still prove the claim
|
|
27
|
+
|
|
28
|
+
| Level | Proves | Fixture allowed? |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| **Unit** | pure function, one input/output contract | yes — the function IS the boundary |
|
|
31
|
+
| **Integration** | real adapters composed across a package boundary | disposable repo/worktree/registry fixture, never a stub standing in for the boundary itself |
|
|
32
|
+
| **Live** | a real external outcome (real git, real process, real HTTP) through the authoritative source | no — a fixture at this level is a downgrade, not a shortcut |
|
|
33
|
+
|
|
34
|
+
A claim that says "delivers," "deploys," "persists," or "ships" needs live
|
|
35
|
+
tier. A claim about a pure calculation needs unit tier and nothing more —
|
|
36
|
+
forcing live tier on a pure function is not rigor, it's noise that hides the
|
|
37
|
+
real live-tier gaps under a pile of slow, brittle tests.
|
|
38
|
+
|
|
39
|
+
## Shape — assertion vs golden-capture
|
|
40
|
+
|
|
41
|
+
Ask: **can the author state, in one sentence, exactly what "correct" means
|
|
42
|
+
before running the code?**
|
|
43
|
+
|
|
44
|
+
- Yes → assertion-based test. Write the expectation, then the test.
|
|
45
|
+
- No (the correct shape is "whatever the real system currently does, until a
|
|
46
|
+
human reviews a change") → golden-capture. Run the seam, record the FULL
|
|
47
|
+
observable result (return value, thrown error, side-effect journal), diff
|
|
48
|
+
future runs against it. A delta is a question for a human, never an
|
|
49
|
+
auto-pass or auto-fail.
|
|
50
|
+
|
|
51
|
+
Golden-capture is not a lesser form of testing — it exists specifically
|
|
52
|
+
because assertion-writing failed on complex orchestration surfaces this same
|
|
53
|
+
session: the author cannot accidentally assert something weaker than the
|
|
54
|
+
truth when they are not choosing what to assert.
|
|
55
|
+
|
|
56
|
+
## Procedure
|
|
57
|
+
|
|
58
|
+
1. **Identify the surface's SEAM** — its real, production-called entry point.
|
|
59
|
+
If nothing in production calls it, that is not a testing question, it's an
|
|
60
|
+
`architecture-reviewer` finding (dead code / no production caller) —
|
|
61
|
+
route it there first. A test cannot prove a surface has no reason to exist.
|
|
62
|
+
|
|
63
|
+
2. **Classify the claim** the surface makes (calculation / composition /
|
|
64
|
+
external effect) and pick the LOWEST level from the table above that can
|
|
65
|
+
prove it. State the level explicitly in the checklist row — not "tested",
|
|
66
|
+
but "unit: pure function" / "integration: composed across guard+delivery
|
|
67
|
+
against a disposable worktree" / "live: real git commit against a
|
|
68
|
+
disposable repo".
|
|
69
|
+
|
|
70
|
+
3. **Pick assertion vs golden-capture** using the one-sentence test above.
|
|
71
|
+
|
|
72
|
+
4. **For a claim naming an outcome word** (delivers/deploys/persists/ships/
|
|
73
|
+
writes) — require a positive control: the SAME test run against a target
|
|
74
|
+
KNOWN to succeed, before trusting a negative result from it. A test that
|
|
75
|
+
never proves it CAN detect failure proves nothing when it passes.
|
|
76
|
+
|
|
77
|
+
5. **Report:**
|
|
78
|
+
```
|
|
79
|
+
## Testing Strategy
|
|
80
|
+
| Surface | Claim | Level | Shape | Positive control needed? |
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Rules
|
|
84
|
+
|
|
85
|
+
- Never recommend live tier for a pure function, or unit tier for an
|
|
86
|
+
external-effect claim — matching level to claim is the whole point.
|
|
87
|
+
- A recommendation with no seam identified is incomplete — say so, don't
|
|
88
|
+
guess a seam that doesn't exist in production.
|
|
89
|
+
- This skill recommends; it does not write the test. Pair with the actual
|
|
90
|
+
build task.
|
|
91
|
+
|
|
92
|
+
## Mechanical test-smell scoring — a separate, lower layer
|
|
93
|
+
|
|
94
|
+
Everything above answers "what SHAPE should this test be" before it's
|
|
95
|
+
written. `scripts/lib/test-smell-scoring.mjs` answers a different question
|
|
96
|
+
once a test exists: does its own construction show a known smell? Same
|
|
97
|
+
distinction as `solid-validator`/`architecture-reviewer` — this is FORM
|
|
98
|
+
applied to test code, not FIT or FUNCTION, and it runs mechanically over
|
|
99
|
+
existing `.test.mjs`/`.test.ts`/`*.spec.*` files, not as a pre-write
|
|
100
|
+
recommendation.
|
|
101
|
+
|
|
102
|
+
Language-independent in shape (pure functions over text/AST facts, same
|
|
103
|
+
discipline as `language-plugin.mjs` — no ts-morph import in the scoring file
|
|
104
|
+
itself). Rule IDs and several thresholds are reused from
|
|
105
|
+
[OnSightTeam/architecture-toolkit](https://github.com/OnSightTeam/architecture-toolkit)
|
|
106
|
+
(MIT) — see the file header for exact file:line citations and what was
|
|
107
|
+
adapted vs. reused verbatim.
|
|
108
|
+
|
|
109
|
+
| Rule | Detects | Threshold |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| T1 Insufficient Tests | fewer test() blocks than exported functions/methods in the paired source file | `testCount < exportedUnitCount` (source count via the `NormalizedUnit` plugin contract, not a regex) |
|
|
112
|
+
| T2 Ignored Tests | `test.skip`/`it.skip`/`xit`/`xdescribe` | any occurrence |
|
|
113
|
+
| T5 Exhaustive Testing | too many assertions in one test() block | >10 per block |
|
|
114
|
+
| T6 Long Tests | oversized test() block body | >30 lines |
|
|
115
|
+
| T7 Slow Tests | literal `setTimeout`/`setInterval`/`sleep`/`delay` calls INSIDE a test's own body | any occurrence in-block |
|
|
116
|
+
| T8 Fragile Tests | `Date.now()`, bare `new Date()`, `Math.random()`, `process.env.*` referenced directly inside a test body | any occurrence in-block |
|
|
117
|
+
| T9 Duplicated Setup | near-identical `beforeEach`/`beforeAll` bodies ACROSS test files | ≥0.75 Jaccard similarity over normalized 3-token shingles (literals collapsed, identifiers not — see file header) |
|
|
118
|
+
| FIRST-Independent | a `let` declared outside test() and mutated inside 2+ separate test() blocks | requires both the declaration site AND ≥2 distinct mutation sites, not presence alone |
|
|
119
|
+
|
|
120
|
+
T3 (Test Per Class) and T4 (Untested Method) are deliberately not
|
|
121
|
+
implemented — see the file header's "Skipped" section for why faking them
|
|
122
|
+
would have been a weak check invented to fill a slot. Fast/Repeatable/
|
|
123
|
+
SelfValidating/Timely are likewise skipped as either duplicating T7/T8 or
|
|
124
|
+
not being smell checks at all.
|
|
125
|
+
|
|
126
|
+
Dogfooded against `rdc-harness`'s 47 real `*.test.mjs` files (2026-08-20):
|
|
127
|
+
71 findings (T5/T6/T8), plus T1 flagged `packages/core` (28 exported
|
|
128
|
+
members, 16 tests — the same `Harness` god-object `solid-score.mjs` already
|
|
129
|
+
flags on SOLID grounds) and three other packages. T2/T7(in-block)/T9/
|
|
130
|
+
Independent had zero real hits in that corpus — confirmed as true negatives
|
|
131
|
+
via grep positive-control before trusting the absence, and each rule was
|
|
132
|
+
separately proven to fire against a synthetic fixture exercising it.
|