mirror of
https://github.com/affaan-m/ECC.git
synced 2026-08-17 21:15:40 +02:00
* 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:
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
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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'];
|
||||
|
||||
@@ -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', {
|
||||
|
||||
@@ -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-');
|
||||
|
||||
Reference in New Issue
Block a user