diff --git a/scripts/ci/catalog.js b/scripts/ci/catalog.js index 566178f5c..2a079082d 100644 --- a/scripts/ci/catalog.js +++ b/scripts/ci/catalog.js @@ -294,7 +294,7 @@ function parseAgentsDocExpectations(agentsContent) { expectations.push({ category: pattern.category, - mode: pattern.mode === 'minimum' && match[2] ? 'minimum' : pattern.mode, + mode: pattern.mode === 'minimum' ? (match[2] ? 'minimum' : 'exact') : pattern.mode, expected: Number(match[1]), source: `${pattern.source} (${pattern.category})` }); @@ -373,7 +373,7 @@ function parseZhAgentsDocExpectations(agentsContent) { expectations.push({ category: pattern.category, - mode: pattern.mode === 'minimum' && match[2] ? 'minimum' : pattern.mode, + mode: pattern.mode === 'minimum' ? (match[2] ? 'minimum' : 'exact') : pattern.mode, expected: Number(match[1]), source: `${pattern.source} (${pattern.category})` }); diff --git a/tests/ci/catalog.test.js b/tests/ci/catalog.test.js index 4fddd4702..366ce0326 100644 --- a/tests/ci/catalog.test.js +++ b/tests/ci/catalog.test.js @@ -225,6 +225,44 @@ function runTests() { } })) passed++; else failed++; + if (test('rejects unmarked skill undercounts in English and Chinese agent trees', () => { + const testDir = createTestDir(); + try { + writeCatalogFixture(testDir, { actualCounts: { agents: 1, skills: 2, commands: 1 } }); + for (const relativePath of ['AGENTS.md', 'docs/zh-CN/AGENTS.md']) { + const filePath = path.join(testDir, relativePath); + fs.writeFileSync(filePath, fs.readFileSync(filePath, 'utf8').replace('skills/ - 2 ', 'skills/ - 1 ')); + } + const result = runCatalogCheck({ root: testDir }); + for (const source of ['AGENTS.md project structure (skills)', 'docs/zh-CN/AGENTS.md project structure (skills)']) { + const check = result.checks.find(entry => entry.source === source); + assert.ok(check, `Missing check for ${source}`); + assert.strictEqual(check.ok, false, `${source} must reject an exact undercount`); + } + } finally { + cleanupTestDir(testDir); + } + })) passed++; else failed++; + + if (test('accepts explicitly marked lower-bound skill counts in agent trees', () => { + const testDir = createTestDir(); + try { + writeCatalogFixture(testDir, { actualCounts: { agents: 1, skills: 2, commands: 1 } }); + for (const relativePath of ['AGENTS.md', 'docs/zh-CN/AGENTS.md']) { + const filePath = path.join(testDir, relativePath); + fs.writeFileSync(filePath, fs.readFileSync(filePath, 'utf8').replace('skills/ - 2 ', 'skills/ - 1+ ')); + } + const result = runCatalogCheck({ root: testDir }); + for (const source of ['AGENTS.md project structure (skills)', 'docs/zh-CN/AGENTS.md project structure (skills)']) { + const check = result.checks.find(entry => entry.source === source); + assert.ok(check, `Missing check for ${source}`); + assert.strictEqual(check.ok, true, `${source} must preserve explicit lower-bound semantics`); + } + } finally { + cleanupTestDir(testDir); + } + })) passed++; else failed++; + if (test('reports mismatches from every tracked catalog document', () => { const testDir = createTestDir(); try {