@lifeaitools/rdc-skills 0.35.2 → 0.35.4

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rdc",
3
- "version": "0.35.2",
3
+ "version": "0.35.4",
4
4
  "description": "RDC typed-agent dispatch skill suite for Claude Code — plan, build, review, overnight unattended builds with work-item tracking and TDD enforcement.",
5
5
  "author": {
6
6
  "name": "LIFEAI",
package/CHANGELOG.md CHANGED
@@ -7,6 +7,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## v0.35.4 — marketplace sync false-positive: untracked file blocked it forever
11
+
12
+ ### Fixed
13
+ - `syncMarketplaceCheckout()`'s dirty-check used plain `git status --porcelain`,
14
+ which also reports untracked files. A single harmless untracked file in a
15
+ marketplace clone (e.g. a stray local scratch file) made the check treat the
16
+ clone as "has local changes" and permanently refuse to sync it, on every
17
+ single install run, forever — even though `git reset --hard` never touches
18
+ untracked files and there was no real edit to preserve. This is the
19
+ confirmed root cause of the marketplace checkout sitting stale for over a
20
+ week despite repeated installer runs. Fixed to `--untracked-files=no`, which
21
+ checks only tracked-file modifications — genuine local edits still block the
22
+ sync exactly as before (PRESERVE-DIRTY intact); a stray untracked file no
23
+ longer does.
24
+ - Regression test added: a real temp git origin+clone pair reproduces both the
25
+ untracked-cruft case (must sync) and the genuine-local-edit case (must still
26
+ block), so this cannot silently regress.
27
+ - `tests/install-rdc-skills.test.mjs` also had two stale fixtures caught while
28
+ fixing this: `package-lock.json` version drifted from `package.json`
29
+ (0.35.2 vs 0.35.3+), and a hardcoded `skillCount === 36` assertion had rotted
30
+ against the real, current 43 skill directories. The count assertion now
31
+ compares against the actual `skills/*/SKILL.md` directories on disk instead
32
+ of a hardcoded number, so it can't go stale the same way again.
33
+
34
+ ---
35
+
36
+ ## v0.35.3 — coding standards wired into plan/preplan/build/fixit/review
37
+
38
+ ### Fixed
39
+ - `docs/CODING-STANDARDS.md` (regen-root's SOLID/Clean-Architecture standard)
40
+ existed but was never referenced by any skill that actually reads source
41
+ documents before working — `preplan`, `plan`, `build`, `fixit`, and
42
+ `review` all now load it (regen-root; skip if absent). Applied to both the
43
+ `commands/*.md` and `skills/*/SKILL.md` copies where they diverge.
44
+ - `skills/review/SKILL.md` incorrectly attributed `rdc-solid-score` to ATF;
45
+ it is this package's own validator (`scripts/solid-score.mjs`), which ATF's
46
+ Test-Ladder separately invokes as a consumer, not an owner.
47
+
48
+ ### Added
49
+ - WIP, best-effort references to ATF's Test-Ladder and rdc-harness's
50
+ proof-ledger/mutation-check pattern in `build`, `fixit`, and `review` — not
51
+ a hard gate; explicitly a no-op where a target repo doesn't ship either.
52
+
53
+ ---
54
+
10
55
  ## v0.25.11 — private Coolify maintenance path
11
56
 
12
57
  ### Added
package/commands/build.md CHANGED
@@ -108,7 +108,9 @@ Read the task title and description, then:
108
108
 
109
109
  3. **Load the plan** (if exists): check `.rdc/plans/` for matching topic (fallback: `.rdc/plans/`).
110
110
 
111
- 4. **Read CLAUDE.md files** for all affected packages.
111
+ 4. **Read CLAUDE.md files** for all affected packages, plus `docs/CODING-STANDARDS.md`
112
+ (SOLID/Clean-Architecture standard — regen-root; skip if absent) — carry it into every
113
+ dispatched agent prompt.
112
114
 
113
115
  5. **Classify each task** → assign agent type from the table above.
114
116
 
@@ -163,6 +165,12 @@ Read the task title and description, then:
163
165
  - Read the full output — zero failures, zero type errors
164
166
  - If any step fails: fix and re-run the entire gate. Do not skip.
165
167
  - NEVER `pnpm build` / `pnpm test` / `pnpm -r` (crashes machine)
168
+ - **ATF Test-Ladder / rdc-harness (WIP — best-effort, not a hard gate yet):** if a
169
+ touched package/repo ships an ATF `STP-001.md` or an `rdc-harness`-style
170
+ `tools/mutate-check.mjs`/`tools/proof-ledger.mjs` pair, run it and quote the result
171
+ alongside vitest/tsc. A red mutation gate is a real finding — report it, do not
172
+ silently drop it because it isn't wired into this checklist as required yet.
173
+ Absence of either system in the target is not a failure; do not install one ad hoc.
166
174
 
167
175
  11. **After verification passes:**
168
176
  - Push all commits:
package/commands/fixit.md CHANGED
@@ -70,7 +70,8 @@ This signals the Stop hook that fixit is handling its own documentation.
70
70
 
71
71
  ### 4. Make the fix
72
72
 
73
- Do the minimal work. Scope creep rule: if you discover the fix requires more than originally scoped, **stop immediately**:
73
+ Do the minimal work, conforming to `docs/CODING-STANDARDS.md` where present
74
+ (regen-root; skip if absent). Scope creep rule: if you discover the fix requires more than originally scoped, **stop immediately**:
74
75
  1. Close the work item: `update_work_item_status('<id>', 'blocked', '["Escalated — scope exceeded fixit threshold"]')`
75
76
  2. Delete the marker file
76
77
  3. Tell the user to use `/rdc:build` instead
@@ -108,6 +109,11 @@ failure this step exists to prevent. Carry the change to where it is consumed an
108
109
 
109
110
  Under `RDC_TEST=1`: echo `[RDC_TEST] skipping delivery (publish/deploy)` and proceed.
110
111
 
112
+ **ATF Test-Ladder / rdc-harness (WIP — best-effort, not a hard gate yet):** if the touched
113
+ package/repo already ships an ATF `STP-001.md` or an `rdc-harness`-style
114
+ `tools/mutate-check.mjs`/`tools/proof-ledger.mjs` pair, run it and quote the result. Neither
115
+ is fleet-wide yet — its absence is not itself a fixit failure.
116
+
111
117
  ### 6. Report, review, close, and clean up
112
118
 
113
119
  **Precondition: Step 5.5 delivery is verified.** If delivery is blocked, set `blocked` (not
package/commands/plan.md CHANGED
@@ -34,6 +34,10 @@ description: >-
34
34
  .claude/rules/work-items-rpc.md — work item schema, RPC, status enums
35
35
  .claude/rules/system-quick-links.md — routing map to all system architecture docs
36
36
  .claude/rules/version-numbering.md — version bump rules for affected packages
37
+ docs/CODING-STANDARDS.md — SOLID/Clean-Architecture standard; names
38
+ ATF's Test-Ladder and rdc-harness's
39
+ proof-ledger pattern where either applies
40
+ (regen-root; skip if absent)
37
41
  ```
38
42
 
39
43
  **Step 1b — Identify affected domains, then load the matching architecture doc:**
@@ -112,6 +116,12 @@ description: >-
112
116
  - completeness: independently observable acceptance criteria plus at least one required `decomp-*` and `test-*` checklist row; and
113
117
  - proportionality: estimated files/LOC, declared surfaces, and change kind.
114
118
  - Do not invent architecture evidence. Missing, unregistered, broad-refactor, multi-surface, or disproportionate contracts are intentionally routed to human Design Review.
119
+ - **ATF Test-Ladder / rdc-harness (WIP — best-effort, not a hard gate yet):** if the
120
+ package being planned has its own ATF `STP-001.md` or ships an `rdc-harness`-style
121
+ `tools/proof-ledger.mjs`/`tools/mutate-check.mjs` pair, name it in the work package's
122
+ test requirements as an additional validation signal alongside vitest/tsc. Neither
123
+ system is fleet-wide or hook-enforced yet — do not fabricate a ladder or a proof
124
+ ledger for a package that doesn't already have one.
115
125
 
116
126
  6. **Write plan doc** to `.rdc/plans/<topic-slug>.md` (fallback: `.rdc/plans/<topic-slug>.md` if `.rdc/` does not exist):
117
127
  ```markdown
@@ -37,6 +37,10 @@ description: >-
37
37
  .claude/rules/infrastructure-contract.md — hard deployment + registry rules
38
38
  .claude/rules/work-items-rpc.md — work item schema and RPC patterns
39
39
  .claude/rules/system-quick-links.md — routing map to system architecture docs
40
+ docs/CODING-STANDARDS.md — SOLID/Clean-Architecture standard; names
41
+ ATF's Test-Ladder and rdc-harness's
42
+ proof-ledger pattern where either applies
43
+ (regen-root; skip if absent)
40
44
  ```
41
45
 
42
46
  **Step 2b — Identify affected domains, then load the matching architecture doc:**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/rdc-skills",
3
- "version": "0.35.2",
3
+ "version": "0.35.4",
4
4
  "description": "RDC typed-agent dispatch skill suite for Claude Code - plan, build, review, overnight builds",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -508,8 +508,16 @@ function syncMarketplaceCheckout(mktDir) {
508
508
  const git = (...args) =>
509
509
  execSync(`git -C "${mktDir}" ${args.join(' ')}`, { encoding: 'utf8', stdio: 'pipe' }).trim();
510
510
  try {
511
- if (git('status', '--porcelain')) {
512
- warn(`marketplace clone has local changes at ${mktDir} — left untouched; it may serve stale commands`);
511
+ // PRESERVE-DIRTY means "don't discard a real local edit to a TRACKED file" —
512
+ // `--untracked-files=no` is deliberate. Plain `git status --porcelain` also
513
+ // reports untracked files (`?? some-cruft.txt`), which `git reset --hard`
514
+ // never touches and never conflicts with. Treating that as "dirty" made this
515
+ // sync silently refuse forever the moment ANY stray untracked file appeared
516
+ // in the clone — which is exactly how this checkout sat 39+ commits behind
517
+ // for over a week (2026-08-23), still warning "left untouched" on every run,
518
+ // while a plain `git status` on the same directory read completely clean.
519
+ if (git('status', '--porcelain', '--untracked-files=no')) {
520
+ warn(`marketplace clone has local changes to tracked files at ${mktDir} — left untouched; it may serve stale commands`);
513
521
  return;
514
522
  }
515
523
  const branch = git('rev-parse', '--abbrev-ref', 'HEAD') || 'master';
@@ -1347,7 +1355,7 @@ async function main() {
1347
1355
  // scripts/probe-installed-hooks.mjs verify the real install path instead of
1348
1356
  // re-implementing it — a probe that copies its own way proves nothing about what
1349
1357
  // ships. Without this guard, requiring the module would run a full install.
1350
- module.exports = { copyHookFiles, assertHooksLoadable, registerCodexTarget, RDC_ENV_HOOK_TIMEOUT_SEC };
1358
+ module.exports = { copyHookFiles, assertHooksLoadable, registerCodexTarget, syncMarketplaceCheckout, RDC_ENV_HOOK_TIMEOUT_SEC };
1351
1359
 
1352
1360
  if (require.main === module) {
1353
1361
  main().catch(e => { fail(e.message); process.exit(1); });
@@ -293,7 +293,9 @@ Read the task title and description, then:
293
293
 
294
294
  **Plan verifier escalation:** A separate verifier agent is optional, not default. Dispatch one only when the plan touches 5+ UI routes, 3+ data/API boundaries, auth/security, production deployment, or when this rubric fails twice. The verifier reads the plan and work-item checklists only; it does not write code.
295
295
 
296
- 4. **Read CLAUDE.md files** for all affected packages.
296
+ 4. **Read CLAUDE.md files** for all affected packages, plus `docs/CODING-STANDARDS.md`
297
+ (SOLID/Clean-Architecture standard — regen-root; skip if absent) — carry it into every
298
+ dispatched agent prompt.
297
299
 
298
300
  5. **Classify each task** → assign agent type from the table above.
299
301
 
@@ -625,6 +627,12 @@ Read the task title and description, then:
625
627
  - Runs `npx tsc --noEmit` for every touched app/package
626
628
  - Starts the dev server and probes every modified route (expects HTTP 200, not 500)
627
629
  - Runs vitest for every touched package
630
+ - **ATF Test-Ladder / rdc-harness (WIP — best-effort, not a hard gate yet):** if a
631
+ touched package/repo ships an ATF `STP-001.md` or an `rdc-harness`-style
632
+ `tools/mutate-check.mjs`/`tools/proof-ledger.mjs` pair, run it and quote the result
633
+ alongside tsc/vitest. A red mutation gate is a real finding — report it even though
634
+ it isn't wired into this checklist as required yet. Absence of either system in the
635
+ target is not a failure; do not install one ad hoc.
628
636
  - For every durable workflow, runs the committed E2E harness and rejects the
629
637
  item unless its receipt proves public ingress -> queued/running -> terminal
630
638
  state, persisted outputs, and fixture cleanup on the deploy-equivalent
@@ -69,7 +69,8 @@ This signals the Stop hook that fixit is handling its own documentation.
69
69
 
70
70
  ### 4. Make the fix
71
71
 
72
- Do the minimal work. Scope creep rule: if you discover the fix requires more than originally scoped, **stop immediately**:
72
+ Do the minimal work, conforming to `docs/CODING-STANDARDS.md` where present
73
+ (regen-root; skip if absent). Scope creep rule: if you discover the fix requires more than originally scoped, **stop immediately**:
73
74
  1. Close the work item: `update_work_item_status('<id>', 'blocked', '["Escalated — scope exceeded fixit threshold"]')`
74
75
  2. Delete the marker file
75
76
  3. Tell the user to use `/rdc:build` instead
@@ -147,6 +148,11 @@ Hard rules for this gate:
147
148
  Under `RDC_TEST=1`: echo `[RDC_TEST] skipping delivery (publish/deploy)` and proceed; the
148
149
  gate is validated structurally, not executed.
149
150
 
151
+ **ATF Test-Ladder / rdc-harness (WIP — best-effort, not a hard gate yet):** if the touched
152
+ package/repo already ships an ATF `STP-001.md` or an `rdc-harness`-style
153
+ `tools/mutate-check.mjs`/`tools/proof-ledger.mjs` pair, run it and quote the result. Neither
154
+ is fleet-wide yet — its absence is not itself a fixit failure.
155
+
150
156
  ### 6. Close and clean up
151
157
 
152
158
  Submit implementation report first, move to review, then close as validator.
@@ -11,6 +11,10 @@ description: "Usage `rdc:review [--unattended]` — Post-build quality gate: tsc
11
11
 
12
12
  > **Sandbox contract:** This skill honors `RDC_TEST=1` per `guides/agent-bootstrap.md` § RDC_TEST Sandbox Contract. Destructive external calls short-circuit under the flag.
13
13
 
14
+ > **Standard being enforced:** `docs/CODING-STANDARDS.md` (SOLID/Clean-Architecture —
15
+ > regen-root; skip if absent). Step 8c is where it's actually checked, via this package's
16
+ > own `rdc-solid-score` (also invoked by ATF's Test-Ladder as a consumer, not an owner).
17
+
14
18
 
15
19
  # rdc:review — Quality Gate
16
20
 
@@ -124,7 +128,10 @@ description: "Usage `rdc:review [--unattended]` — Post-build quality gate: tsc
124
128
  output. Either non-empty is a FORM/FIT failure — dispatch
125
129
  `architecture-reviewer` (skill: "architecture-reviewer") for the judgment
126
130
  half if a boundary finding needs a suggested fix, not just a named
127
- violation.
131
+ violation. `rdc-solid-score` is this package's own SOLID validator
132
+ (`scripts/solid-score.mjs`), which ATF's Test-Ladder also invokes at rung
133
+ TI-3/R3 as a dogfooding example — this step is not an ATF integration;
134
+ ATF is a separate consumer of the same tool, not its owner.
128
135
 
129
136
  **Severity gate:**
130
137
  - Any regression, or any boundary violation → verdict cannot be CLEAN.
@@ -133,6 +140,14 @@ description: "Usage `rdc:review [--unattended]` — Post-build quality gate: tsc
133
140
  nothing wrong to report, but say so explicitly rather than silently
134
141
  passing.
135
142
 
143
+ **rdc-harness (WIP — best-effort, not a hard gate yet):** if the reviewed
144
+ repo ships an `rdc-harness`-style `tools/mutate-check.mjs` and
145
+ `tools/proof-ledger.mjs` pair, run `tools/mutate-check.mjs` and quote its
146
+ result. A proof-ledger count is not citable evidence while its own
147
+ mutation gate is red — report that combination as HAS_ISSUES rather than
148
+ passing along a ledger number the gate itself has disowned. Absence of
149
+ this tooling in the target repo is not a finding; do not install it ad hoc.
150
+
136
151
  Under `RDC_TEST=1`: echo `[RDC_TEST] skipping form/fit/function gate` and continue.
137
152
 
138
153
  9. **Verification gate — dispatch the verify agent:**
@@ -44,7 +44,22 @@ assert.match(
44
44
  const skillCount = Array.isArray(plugin.skills_meta)
45
45
  ? plugin.skills_meta.length
46
46
  : Object.keys(plugin.skills_meta || {}).length;
47
- assert.equal(skillCount, 36, 'test fixture should expose all 36 MCP skills from plugin skills_meta');
47
+ // A hardcoded magic number here (previously 36, silently stale against the
48
+ // real 43) rots the instant a skill is added or removed and stops proving
49
+ // anything — it only proves someone remembered to bump a number. Assert
50
+ // against the actual skills/ directory instead: every dir containing a
51
+ // SKILL.md must be represented in plugin.json's skills_meta, and vice versa.
52
+ const skillsDir = join(REPO_ROOT, 'skills');
53
+ const realSkillDirs = require('node:fs')
54
+ .readdirSync(skillsDir, { withFileTypes: true })
55
+ .filter((e) => e.isDirectory() && existsSync(join(skillsDir, e.name, 'SKILL.md')))
56
+ .map((e) => e.name)
57
+ .sort();
58
+ assert.equal(
59
+ skillCount,
60
+ realSkillDirs.length,
61
+ `plugin.json skills_meta (${skillCount}) must match the actual skill directories on disk (${realSkillDirs.length}): ${realSkillDirs.join(', ')}`,
62
+ );
48
63
  assert.match(
49
64
  source,
50
65
  /Available MCP skills.*\/rdc:\* command shorthands/,
@@ -97,6 +112,81 @@ try {
97
112
  rmSync(codexSkills, { recursive: true, force: true });
98
113
  }
99
114
 
115
+ // Regression for the 2026-08-23 incident: a marketplace clone with ZERO real
116
+ // local edits — only a stray untracked file — sat 39+ commits behind for over
117
+ // a week because `git status --porcelain` (which also reports untracked
118
+ // files) was treated as "has local changes, never touch it". `git reset
119
+ // --hard` never touches untracked files, so an untracked file is irrelevant
120
+ // to whether the sync is safe. This builds a real origin + clone pair,
121
+ // reproduces the exact scenario, and asserts the clone actually advances.
122
+ const { syncMarketplaceCheckout } = require(script);
123
+ const gitTestRoot = mkdtempSync(join(tmpdir(), 'rdc-marketplace-sync-'));
124
+ try {
125
+ const originDir = join(gitTestRoot, 'origin');
126
+ const cloneDir = join(gitTestRoot, 'clone');
127
+ const runGit = (cwd, args) => {
128
+ const res = spawnSync('git', args, { cwd, encoding: 'utf8' });
129
+ assert.equal(res.status, 0, `git ${args.join(' ')} failed: ${res.stderr}`);
130
+ return res.stdout.trim();
131
+ };
132
+
133
+ mkdirSync(originDir, { recursive: true });
134
+ runGit(originDir, ['init', '--initial-branch=master']);
135
+ runGit(originDir, ['config', 'user.email', 'test@example.com']);
136
+ runGit(originDir, ['config', 'user.name', 'Test']);
137
+ writeFileSync(join(originDir, 'file.txt'), 'v1\n');
138
+ runGit(originDir, ['add', '.']);
139
+ runGit(originDir, ['commit', '-m', 'v1']);
140
+
141
+ runGit(gitTestRoot, ['clone', originDir, cloneDir]);
142
+
143
+ // Advance origin so the clone is genuinely behind.
144
+ writeFileSync(join(originDir, 'file.txt'), 'v2\n');
145
+ runGit(originDir, ['add', '.']);
146
+ runGit(originDir, ['commit', '-m', 'v2']);
147
+ const originHead = runGit(originDir, ['rev-parse', 'HEAD']);
148
+
149
+ // The exact reproduction: a stray UNTRACKED file, nothing modified/staged.
150
+ writeFileSync(join(cloneDir, 'some-local-cruft.cjs'), 'module.exports = {};\n');
151
+ assert.match(
152
+ runGit(cloneDir, ['status', '--porcelain']),
153
+ /^\?\? some-local-cruft\.cjs$/,
154
+ 'precondition: clone must show ONLY an untracked file, nothing modified',
155
+ );
156
+
157
+ syncMarketplaceCheckout(cloneDir);
158
+
159
+ assert.equal(
160
+ runGit(cloneDir, ['rev-parse', 'HEAD']),
161
+ originHead,
162
+ 'a clone with only an untracked file must still advance to origin — untracked cruft is not a local edit',
163
+ );
164
+ assert.equal(
165
+ existsSync(join(cloneDir, 'some-local-cruft.cjs')),
166
+ true,
167
+ 'the untracked file itself must survive the sync — reset --hard never touches it',
168
+ );
169
+
170
+ // Genuine dirt — a MODIFIED tracked file — must still block the sync.
171
+ writeFileSync(join(originDir, 'file.txt'), 'v3\n');
172
+ runGit(originDir, ['add', '.']);
173
+ runGit(originDir, ['commit', '-m', 'v3']);
174
+ const originHeadV3 = runGit(originDir, ['rev-parse', 'HEAD']);
175
+ writeFileSync(join(cloneDir, 'file.txt'), 'local edit, never committed\n');
176
+ const beforeDirtySync = runGit(cloneDir, ['rev-parse', 'HEAD']);
177
+
178
+ syncMarketplaceCheckout(cloneDir);
179
+
180
+ assert.equal(
181
+ runGit(cloneDir, ['rev-parse', 'HEAD']),
182
+ beforeDirtySync,
183
+ 'a real local edit to a TRACKED file must still block the sync (PRESERVE-DIRTY)',
184
+ );
185
+ assert.notEqual(beforeDirtySync, originHeadV3, 'sanity: origin did advance further in this step');
186
+ } finally {
187
+ rmSync(gitTestRoot, { recursive: true, force: true });
188
+ }
189
+
100
190
  const packagedRoot = mkdtempSync(join(tmpdir(), 'rdc-packaged-truth-'));
101
191
  try {
102
192
  writeFileSync(join(packagedRoot, 'package.json'), JSON.stringify({ version: '9.9.9' }));