fix(install): reference all curated skills in modules + reverse-coverage guard (#2431) (#2440)

* fix(install): reference all curated skills in modules + add reverse-coverage guard (#2431)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(install): normalize path separators in delivery-gate dry-run assertion (#2431)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: affaan <affaan@itomarkets.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
devin-ai-integration[bot] 2026-07-08 17:14:52 -04:00 committed by GitHub
parent 67537ea480
commit 38a7ebbe32
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 349 additions and 14 deletions

View file

@ -52,6 +52,29 @@ function writeInstallComponentsManifest(testDir, components) {
});
}
function writeInstallModulesManifest(testDir, modules) {
writeJson(path.join(testDir, 'manifests', 'install-modules.json'), {
version: 1,
modules,
});
}
function writeInstallProfilesManifest(testDir, profiles) {
writeJson(path.join(testDir, 'manifests', 'install-profiles.json'), {
version: 1,
profiles,
});
}
function writeSkillFixture(testDir, skillId, description) {
const skillDir = path.join(testDir, 'skills', skillId);
fs.mkdirSync(skillDir, { recursive: true });
fs.writeFileSync(
path.join(skillDir, 'SKILL.md'),
`---\nname: ${skillId}\ndescription: ${description}\n---\n# ${skillId}\n`
);
}
function stripShebang(source) {
let s = source;
if (s.charCodeAt(0) === 0xFEFF) s = s.slice(1);
@ -2793,6 +2816,99 @@ function runTests() {
assert.ok(result.stdout.includes('Validated'), 'Should output validation count');
})) passed++; else failed++;
if (test('fails when a curated skill is not referenced by any install module', () => {
const testDir = createTestDir();
try {
writeInstallModulesManifest(testDir, [
{
id: 'skill-alpha',
kind: 'skills',
description: 'Alpha skill',
paths: ['skills/alpha'],
targets: ['claude'],
dependencies: [],
defaultInstall: false,
cost: 'light',
stability: 'stable',
},
{
id: 'skill-beta',
kind: 'skills',
description: 'Beta skill',
paths: ['skills/beta'],
targets: ['claude'],
dependencies: [],
defaultInstall: false,
cost: 'light',
stability: 'stable',
},
]);
writeInstallProfilesManifest(testDir, {
core: { description: 'Core', modules: ['skill-alpha', 'skill-beta'] },
developer: { description: 'Developer', modules: ['skill-alpha', 'skill-beta'] },
security: { description: 'Security', modules: ['skill-alpha', 'skill-beta'] },
research: { description: 'Research', modules: ['skill-alpha', 'skill-beta'] },
full: { description: 'Full', modules: ['skill-alpha', 'skill-beta'] },
});
writeSkillFixture(testDir, 'alpha', 'Alpha skill');
writeSkillFixture(testDir, 'beta', 'Beta skill');
let result = runValidatorWithDirs('validate-install-manifests', {
REPO_ROOT: testDir,
MODULES_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-modules.json'),
PROFILES_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-profiles.json'),
COMPONENTS_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-components.json'),
MODULES_SCHEMA_PATH: modulesSchemaPath,
PROFILES_SCHEMA_PATH: profilesSchemaPath,
COMPONENTS_SCHEMA_PATH: componentsSchemaPath,
});
assert.strictEqual(result.code, 0, `Should pass with both skills referenced, got stderr: ${result.stderr}`);
writeInstallModulesManifest(testDir, [
{
id: 'skill-alpha',
kind: 'skills',
description: 'Alpha skill',
paths: ['skills/alpha'],
targets: ['claude'],
dependencies: [],
defaultInstall: false,
cost: 'light',
stability: 'stable',
},
{
id: 'skill-beta',
kind: 'skills',
description: 'Beta skill',
paths: ['skills/beta-restored'],
targets: ['claude'],
dependencies: [],
defaultInstall: false,
cost: 'light',
stability: 'stable',
},
]);
writeSkillFixture(testDir, 'beta-restored', 'Beta skill restored');
result = runValidatorWithDirs('validate-install-manifests', {
REPO_ROOT: testDir,
MODULES_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-modules.json'),
PROFILES_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-profiles.json'),
COMPONENTS_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-components.json'),
MODULES_SCHEMA_PATH: modulesSchemaPath,
PROFILES_SCHEMA_PATH: profilesSchemaPath,
COMPONENTS_SCHEMA_PATH: componentsSchemaPath,
});
assert.strictEqual(result.code, 1, 'Should fail when beta is no longer referenced');
assert.ok(
result.stderr.includes('curated skill skills/beta is not referenced by any install module'),
`Should report unreferenced skill, got: ${result.stderr}`
);
} finally {
cleanupTestDir(testDir);
}
})) passed++; else failed++;
if (test('exits 0 when install manifests do not exist', () => {
const testDir = createTestDir();
const result = runValidatorWithDirs('validate-install-manifests', {

View file

@ -372,6 +372,28 @@ function runTests() {
}
})) passed++; else failed++;
if (test('full profile dry-runs include delivery-gate in the install plan', () => {
const homeDir = createTempDir('install-apply-home-');
const projectDir = createTempDir('install-apply-project-');
try {
const result = run(['--profile', 'full', '--dry-run', '--json'], { cwd: projectDir, homeDir });
assert.strictEqual(result.code, 0, result.stderr);
const parsed = JSON.parse(result.stdout);
assert.strictEqual(parsed.dryRun, true);
assert.ok(parsed.plan.selectedModuleIds.includes('workflow-quality'));
assert.ok(
parsed.plan.operations.some(operation => (
String(operation.sourceRelativePath || '').replace(/\\/g, '/').startsWith('skills/delivery-gate/')
)),
'Full profile dry-run should include the delivery-gate skill'
);
} finally {
cleanup(homeDir);
cleanup(projectDir);
}
})) passed++; else failed++;
if (test('supports minimal profile dry-runs without hooks through the installer', () => {
const homeDir = createTempDir('install-apply-home-');
const projectDir = createTempDir('install-apply-project-');