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
co-authored by Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> affaan Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 67537ea480
commit 38a7ebbe32
5 changed files with 349 additions and 14 deletions
+92 -14
View File
@@ -199,7 +199,23 @@
"skills/springboot-tdd",
"skills/springboot-verification",
"skills/ui-to-vue",
"skills/vue-patterns"
"skills/vue-patterns",
"skills/accessibility",
"skills/bun-runtime",
"skills/design-system",
"skills/django-celery",
"skills/flutter-dart-code-review",
"skills/frontend-a11y",
"skills/generating-python-installer",
"skills/hexagonal-architecture",
"skills/motion-advanced",
"skills/motion-foundations",
"skills/motion-patterns",
"skills/nextjs-turbopack",
"skills/nuxt4-patterns",
"skills/react-native-patterns",
"skills/tinystruct-patterns",
"skills/vite-patterns"
],
"targets": [
"claude",
@@ -233,7 +249,8 @@
"skills/jpa-patterns",
"skills/mysql-patterns",
"skills/postgres-patterns",
"skills/prisma-patterns"
"skills/prisma-patterns",
"skills/redis-patterns"
],
"targets": [
"claude",
@@ -280,7 +297,28 @@
"skills/strategic-compact",
"skills/tdd-workflow",
"skills/verification-loop",
"skills/windows-desktop-e2e"
"skills/windows-desktop-e2e",
"skills/agent-self-evaluation",
"skills/architecture-decision-records",
"skills/browser-qa",
"skills/ck",
"skills/click-path-audit",
"skills/codebase-onboarding",
"skills/codehealth-mcp",
"skills/config-gc",
"skills/context-budget",
"skills/delivery-gate",
"skills/ecc-guide",
"skills/ecc-recipes",
"skills/growth-log",
"skills/inherit-legacy-style",
"skills/intent-driven-development",
"skills/loop-design-check",
"skills/product-lens",
"skills/repo-scan",
"skills/rules-distill",
"skills/santa-method",
"skills/git-workflow"
],
"targets": [
"claude",
@@ -313,7 +351,10 @@
"skills/data-throughput-accelerator",
"skills/latency-critical-systems",
"skills/parallel-execution-optimizer",
"skills/recursive-decision-ledger"
"skills/recursive-decision-ledger",
"skills/agent-eval",
"skills/benchmark",
"skills/benchmark-methodology"
],
"targets": [
"claude",
@@ -354,7 +395,12 @@
"skills/security-bounty-hunter",
"skills/springboot-security",
"skills/evm-token-decimals",
"the-security-guide.md"
"the-security-guide.md",
"skills/gateguard",
"skills/healthcare-cdss-patterns",
"skills/healthcare-emr-patterns",
"skills/healthcare-eval-harness",
"skills/safety-guard"
],
"targets": [
"claude",
@@ -387,7 +433,8 @@
"skills/scientific-db-uspto-database",
"skills/scientific-pkg-gget",
"skills/scientific-thinking-literature-review",
"skills/scientific-thinking-scholar-evaluation"
"skills/scientific-thinking-scholar-evaluation",
"skills/documentation-lookup"
],
"targets": [
"claude",
@@ -422,7 +469,11 @@
"skills/product-capability",
"skills/social-graph-ranker",
"skills/seo",
"skills/market-research"
"skills/market-research",
"skills/brand-discovery",
"skills/competitive-platform-analysis",
"skills/competitive-report-structure",
"skills/marketing-campaign"
],
"targets": [
"claude",
@@ -465,7 +516,8 @@
"skills/project-flow-ops",
"skills/terminal-ops",
"skills/unified-notifications-ops",
"skills/workspace-surface-audit"
"skills/workspace-surface-audit",
"skills/mailtrap-email-integration"
],
"targets": [
"claude",
@@ -525,7 +577,8 @@
"description": "Social publishing and distribution skills.",
"paths": [
"skills/crosspost",
"skills/x-api"
"skills/x-api",
"skills/social-publisher"
],
"targets": [
"claude",
@@ -557,7 +610,8 @@
"skills/remotion-video-creation",
"skills/ui-demo",
"skills/video-editing",
"skills/videodb"
"skills/videodb",
"skills/taste"
],
"targets": [
"claude",
@@ -613,7 +667,8 @@
"skills/swift-actor-persistence",
"skills/swift-concurrency-6-2",
"skills/swift-protocol-di-testing",
"skills/swiftui-patterns"
"skills/swiftui-patterns",
"skills/ios-icon-gen"
],
"targets": [
"claude",
@@ -661,7 +716,20 @@
"skills/search-first",
"skills/team-agent-orchestration",
"skills/token-budget-advisor",
"skills/team-builder"
"skills/team-builder",
"skills/agent-payment-x402",
"skills/autonomous-agent-harness",
"skills/gan-style-harness",
"skills/hermes-imports",
"skills/openclaw-persona-forge",
"skills/opensource-pipeline",
"skills/orch-add-feature",
"skills/orch-build-mvp",
"skills/orch-change-feature",
"skills/orch-fix-defect",
"skills/orch-pipeline",
"skills/orch-refine-code",
"skills/plan-orchestrate"
],
"targets": [
"claude",
@@ -695,7 +763,14 @@
"skills/netmiko-ssh-automation",
"skills/network-bgp-diagnostics",
"skills/network-config-validation",
"skills/network-interface-health"
"skills/network-interface-health",
"skills/canary-watch",
"skills/flox-environments",
"skills/homelab-pihole-dns",
"skills/homelab-vlan-segmentation",
"skills/homelab-wireguard-vpn",
"skills/kubernetes-patterns",
"skills/uncloud"
],
"targets": [
"claude",
@@ -721,7 +796,10 @@
"kind": "skills",
"description": "Production machine-learning engineering workflows for data contracts, reproducible training, evaluation, deployment, monitoring, and rollback.",
"paths": [
"skills/mle-workflow"
"skills/mle-workflow",
"skills/ml-adoption-playbook",
"skills/pytorch-patterns",
"skills/recsys-pipeline-architect"
],
"targets": [
"claude",
+78
View File
@@ -318,6 +318,84 @@
"skills/windows-desktop-e2e/",
"skills/workspace-surface-audit/",
"skills/x-api/",
"skills/accessibility/",
"skills/agent-eval/",
"skills/agent-payment-x402/",
"skills/agent-self-evaluation/",
"skills/architecture-decision-records/",
"skills/autonomous-agent-harness/",
"skills/benchmark/",
"skills/benchmark-methodology/",
"skills/brand-discovery/",
"skills/browser-qa/",
"skills/bun-runtime/",
"skills/canary-watch/",
"skills/ck/",
"skills/click-path-audit/",
"skills/codebase-onboarding/",
"skills/codehealth-mcp/",
"skills/competitive-platform-analysis/",
"skills/competitive-report-structure/",
"skills/config-gc/",
"skills/context-budget/",
"skills/delivery-gate/",
"skills/design-system/",
"skills/django-celery/",
"skills/documentation-lookup/",
"skills/ecc-guide/",
"skills/ecc-recipes/",
"skills/flox-environments/",
"skills/flutter-dart-code-review/",
"skills/frontend-a11y/",
"skills/gan-style-harness/",
"skills/gateguard/",
"skills/generating-python-installer/",
"skills/git-workflow/",
"skills/growth-log/",
"skills/healthcare-cdss-patterns/",
"skills/healthcare-emr-patterns/",
"skills/healthcare-eval-harness/",
"skills/hermes-imports/",
"skills/hexagonal-architecture/",
"skills/homelab-pihole-dns/",
"skills/homelab-vlan-segmentation/",
"skills/homelab-wireguard-vpn/",
"skills/inherit-legacy-style/",
"skills/intent-driven-development/",
"skills/ios-icon-gen/",
"skills/kubernetes-patterns/",
"skills/loop-design-check/",
"skills/mailtrap-email-integration/",
"skills/marketing-campaign/",
"skills/ml-adoption-playbook/",
"skills/motion-advanced/",
"skills/motion-foundations/",
"skills/motion-patterns/",
"skills/nextjs-turbopack/",
"skills/nuxt4-patterns/",
"skills/openclaw-persona-forge/",
"skills/opensource-pipeline/",
"skills/orch-add-feature/",
"skills/orch-build-mvp/",
"skills/orch-change-feature/",
"skills/orch-fix-defect/",
"skills/orch-pipeline/",
"skills/orch-refine-code/",
"skills/plan-orchestrate/",
"skills/product-lens/",
"skills/pytorch-patterns/",
"skills/react-native-patterns/",
"skills/recsys-pipeline-architect/",
"skills/redis-patterns/",
"skills/repo-scan/",
"skills/rules-distill/",
"skills/safety-guard/",
"skills/santa-method/",
"skills/social-publisher/",
"skills/taste/",
"skills/tinystruct-patterns/",
"skills/uncloud/",
"skills/vite-patterns/",
"the-security-guide.md",
"!**/__pycache__/**",
"!**/*.pyc",
+41
View File
@@ -16,6 +16,11 @@ const COMPONENTS_MANIFEST_PATH = path.join(REPO_ROOT, 'manifests/install-compone
const MODULES_SCHEMA_PATH = path.join(REPO_ROOT, 'schemas/install-modules.schema.json');
const PROFILES_SCHEMA_PATH = path.join(REPO_ROOT, 'schemas/install-profiles.schema.json');
const COMPONENTS_SCHEMA_PATH = path.join(REPO_ROOT, 'schemas/install-components.schema.json');
const CURATED_SKILLS_DIR = path.join(REPO_ROOT, 'skills');
// Empty by default; add only curated skills that are intentionally unshipped.
const INTENTIONALLY_UNSHIPPED_SKILL_IDS = new Set([
'skill-comply', // meta/measurement dev-skill; ships committed .pyc artifacts and a nested .gitignore, revisit after packaging cleanup
]);
const COMPONENT_FAMILY_PREFIXES = {
baseline: 'baseline:',
language: 'lang:',
@@ -36,6 +41,18 @@ function normalizeRelativePath(relativePath) {
return String(relativePath).replace(/\\/g, '/').replace(/\/+$/, '');
}
function isCuratedSkillReferenced(claimedPaths, skillId) {
const skillRoot = `skills/${skillId}`;
for (const claimedPath of claimedPaths.keys()) {
if (claimedPath === skillRoot || claimedPath.startsWith(`${skillRoot}/`)) {
return true;
}
}
return false;
}
function validateSchema(ajv, schemaPath, data, label) {
const schema = readJson(schemaPath, `${label} schema`);
const validate = ajv.compile(schema);
@@ -131,6 +148,30 @@ function validateInstallManifests() {
}
}
if (fs.existsSync(CURATED_SKILLS_DIR)) {
const entries = fs.readdirSync(CURATED_SKILLS_DIR, { withFileTypes: true });
for (const entry of entries) {
if (!entry.isDirectory() || entry.name.startsWith('.')) {
continue;
}
const skillMdPath = path.join(CURATED_SKILLS_DIR, entry.name, 'SKILL.md');
if (!fs.existsSync(skillMdPath)) {
continue;
}
if (
!INTENTIONALLY_UNSHIPPED_SKILL_IDS.has(entry.name)
&& !isCuratedSkillReferenced(claimedPaths, entry.name)
) {
console.error(
`ERROR: curated skill skills/${entry.name} is not referenced by any install module`
);
hasErrors = true;
}
}
}
const profiles = profilesData.profiles || {};
const components = Array.isArray(componentsData.components) ? componentsData.components : [];
const expectedProfileIds = ['core', 'developer', 'security', 'research', 'full'];
+116
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', {
+22
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-');