@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.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +45 -0
- package/commands/build.md +9 -1
- package/commands/fixit.md +7 -1
- package/commands/plan.md +10 -0
- package/commands/preplan.md +4 -0
- package/package.json +1 -1
- package/scripts/install-rdc-skills.js +11 -3
- package/skills/build/SKILL.md +9 -1
- package/skills/fixit/SKILL.md +7 -1
- package/skills/review/SKILL.md +16 -1
- package/tests/install-rdc-skills.test.mjs +91 -1
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
|
|
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
|
package/commands/preplan.md
CHANGED
|
@@ -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
|
@@ -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
|
-
|
|
512
|
-
|
|
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); });
|
package/skills/build/SKILL.md
CHANGED
|
@@ -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
|
package/skills/fixit/SKILL.md
CHANGED
|
@@ -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
|
|
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.
|
package/skills/review/SKILL.md
CHANGED
|
@@ -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
|
-
|
|
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' }));
|