diff --git a/tools/readiness-core/test/index.test.mjs b/tools/readiness-core/test/index.test.mjs index 812e3d7a..b2367965 100644 --- a/tools/readiness-core/test/index.test.mjs +++ b/tools/readiness-core/test/index.test.mjs @@ -34,3 +34,26 @@ test('scanSkillDirectories finds skills in target directory', async () => { await fs.rm(fixtureDir, { recursive: true, force: true }); }); + +test('scanSkillDirectories deduplicates skill names across scanned roots', async () => { + // Regression test for double-counting: the same skill installed in two + // scanned roots (e.g. .grok/skills/foo and skills/foo) must count once. + // Duplicates inflate the Loop Readiness score by flipping the skillsOne + // signal to skillsTwoPlus for what is really a single skill. + const fixtureDir = path.join(process.cwd(), '.test-fixture-dedup'); + await fs.rm(fixtureDir, { recursive: true, force: true }); + await fs.mkdir(path.join(fixtureDir, '.grok', 'skills', 'foo'), { recursive: true }); + await fs.mkdir(path.join(fixtureDir, 'skills', 'foo'), { recursive: true }); + await fs.mkdir(path.join(fixtureDir, 'skills', 'bar'), { recursive: true }); + + const skills = await scanSkillDirectories(fixtureDir); + assert.strictEqual( + skills.filter((name) => name === 'foo').length, + 1, + 'Same skill name under two roots should count once', + ); + assert.ok(skills.includes('bar'), 'Distinct skill names should still count separately'); + assert.strictEqual(skills.length, 2, 'Should report exactly foo and bar'); + + await fs.rm(fixtureDir, { recursive: true, force: true }); +});