@skill-graph/cli 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +247 -0
- package/LICENSE +200 -0
- package/NOTICE +62 -0
- package/README.md +398 -0
- package/SKILL_GRAPH.md +443 -0
- package/bin/skill-graph.js +374 -0
- package/docs/ADOPTION.md +117 -0
- package/docs/CONFORMANCE.md +66 -0
- package/docs/PRIMER.md +384 -0
- package/docs/QUICKSTART-30MIN.md +333 -0
- package/docs/ROUTING-METRICS.md +120 -0
- package/docs/SKILL-MD-FORMAT-COMPATIBILITY.md +127 -0
- package/docs/SKILL_AUDIT_CHECKLIST.md +199 -0
- package/docs/SKILL_AUDIT_LOOP.md +195 -0
- package/docs/SKILL_METADATA_PROTOCOL.md +609 -0
- package/docs/_archived/marketplace-publication-priority-2026-05-18.md +239 -0
- package/docs/adr/0001-predicate-set.md +69 -0
- package/docs/adr/0002-json-ld-context.md +82 -0
- package/docs/adr/0003-ontoclean-rigidity-tags.md +65 -0
- package/docs/adr/0004-persistent-identifiers.md +74 -0
- package/docs/adr/0005-freshness-consolidation.md +70 -0
- package/docs/adr/0006-revise-predicate-rename.md +105 -0
- package/docs/adr/0007-audit-loop-cadence.md +99 -0
- package/docs/adr/0008-skill-surface-split-and-curation-policy.md +93 -0
- package/docs/category-consumers.md +168 -0
- package/docs/concept-map.md +194 -0
- package/docs/diagrams/drift-states.mmd +21 -0
- package/docs/diagrams/manifest-pipeline.mmd +25 -0
- package/docs/diagrams/routing-harness.mmd +41 -0
- package/docs/diagrams/starter-graph.mmd +53 -0
- package/docs/field-decision-guide.md +315 -0
- package/docs/field-rationale.md +211 -0
- package/docs/field-reference.generated.md +624 -0
- package/docs/field-reference.md +1426 -0
- package/docs/glossary.md +190 -0
- package/docs/head-noun-glossary.md +63 -0
- package/docs/images/audit-phases.png +0 -0
- package/docs/images/drift-states.png +0 -0
- package/docs/images/graded-mode.png +0 -0
- package/docs/images/manifest-pipeline.png +0 -0
- package/docs/images/routing-harness.png +0 -0
- package/docs/images/skill-anatomy.png +0 -0
- package/docs/images/starter-graph.png +0 -0
- package/docs/images/system-model.png +0 -0
- package/docs/integrations/github-actions.md +155 -0
- package/docs/manifest-field-mapping.md +443 -0
- package/docs/marketplace-publication-queue.generated.md +240 -0
- package/docs/marketplace-release-agent-prompt.md +82 -0
- package/docs/marketplace-skill-candidate-list.md +272 -0
- package/docs/marketplace-syndication.md +222 -0
- package/docs/migration-sample-review.md +155 -0
- package/docs/migrations/v4-to-v5.md +168 -0
- package/docs/migrations/v5-to-v6.md +221 -0
- package/docs/name-exceptions.yaml +37 -0
- package/docs/plans/marketplace-p1-public-migration-plan.md +41 -0
- package/docs/plans/multi-root-workspace.md +148 -0
- package/docs/plans/scripts-roadmap.md +107 -0
- package/docs/plans/v4-schema-bump.md +160 -0
- package/docs/plans/wave-2-extraction.md +122 -0
- package/docs/positioning-vs-marketplaces.md +175 -0
- package/docs/proposals/skill-audit-loop-positioning.md +160 -0
- package/docs/quality-doctrine.md +138 -0
- package/docs/recommended-skills.md +150 -0
- package/docs/research/skill-comprehension-eval-research.md +1830 -0
- package/docs/research/skill-retrieval-evidence.md +66 -0
- package/docs/skill-metadata-protocol.md +471 -0
- package/docs/skills-sh-maintainer-cleanup-request.md +80 -0
- package/examples/audits/a11y/findings.md +52 -0
- package/examples/audits/a11y/scorecard.md +21 -0
- package/examples/audits/a11y/verdict.md +44 -0
- package/examples/audits/debugging/findings.md +59 -0
- package/examples/audits/debugging/scorecard.md +22 -0
- package/examples/audits/debugging/verdict.md +33 -0
- package/examples/audits/documentation/findings.md +59 -0
- package/examples/audits/documentation/scorecard.md +22 -0
- package/examples/audits/documentation/verdict.md +33 -0
- package/examples/evals/a11y.json +140 -0
- package/examples/evals/api-design.json +52 -0
- package/examples/evals/code-review.json +52 -0
- package/examples/evals/data-modeling.json +52 -0
- package/examples/evals/database-migration.json +52 -0
- package/examples/evals/debugging.json +118 -0
- package/examples/evals/dependency-architecture.json +52 -0
- package/examples/evals/design-system-architecture.json +52 -0
- package/examples/evals/error-tracking.json +52 -0
- package/examples/evals/event-contract-design.json +52 -0
- package/examples/evals/form-ux-architecture.json +52 -0
- package/examples/evals/framework-fit-analysis.json +52 -0
- package/examples/evals/graph-audit.json +139 -0
- package/examples/evals/information-architecture.json +52 -0
- package/examples/evals/interaction-feedback.json +52 -0
- package/examples/evals/interaction-patterns.json +52 -0
- package/examples/evals/layout-composition.json +52 -0
- package/examples/evals/lint-overlay.json +117 -0
- package/examples/evals/microcopy.json +52 -0
- package/examples/evals/observability-modeling.json +52 -0
- package/examples/evals/pattern-recognition.json +96 -0
- package/examples/evals/performance-engineering.json +52 -0
- package/examples/evals/refactor.json +128 -0
- package/examples/evals/semiotics.json +52 -0
- package/examples/evals/skill-infrastructure.json +96 -0
- package/examples/evals/skill-router.json +140 -0
- package/examples/evals/skill-router.routing.json +113 -0
- package/examples/evals/system-interface-contracts.json +52 -0
- package/examples/evals/task-analysis.json +52 -0
- package/examples/evals/testing-strategy.json +118 -0
- package/examples/evals/type-safety.json +249 -0
- package/examples/evals/visual-design-foundations.json +52 -0
- package/examples/evals/webhook-integration.json +52 -0
- package/examples/exports/a11y.skill-md.md +80 -0
- package/examples/exports/debugging.skill-md.md +80 -0
- package/examples/exports/refactor.skill-md.md +78 -0
- package/examples/exports/testing-strategy.skill-md.md +81 -0
- package/examples/projects/markdown-static-site/README.md +115 -0
- package/examples/projects/markdown-static-site/skills/content-source-router/SKILL.md +131 -0
- package/examples/projects/markdown-static-site/skills/image-optimization-pipeline-config/SKILL.md +132 -0
- package/examples/projects/markdown-static-site/skills/link-rot-detection/SKILL.md +103 -0
- package/examples/projects/markdown-static-site/skills/markdown-post-frontmatter-validation/SKILL.md +133 -0
- package/examples/projects/markdown-static-site/skills/migrate-posts-to-v2-frontmatter/SKILL.md +140 -0
- package/examples/projects/saas-stripe-postgres/README.md +208 -0
- package/examples/projects/saas-stripe-postgres/db/migrations/0004_canonicalize_orders.sql +37 -0
- package/examples/projects/saas-stripe-postgres/db/schema.sql +112 -0
- package/examples/projects/saas-stripe-postgres/skills/migrate-orders-to-canonical-schema/SKILL.md +149 -0
- package/examples/projects/saas-stripe-postgres/skills/nextjs-server-action-validation/SKILL.md +154 -0
- package/examples/projects/saas-stripe-postgres/skills/payment-provider-router/SKILL.md +153 -0
- package/examples/projects/saas-stripe-postgres/skills/postgres-rls-pattern/SKILL.md +163 -0
- package/examples/projects/saas-stripe-postgres/skills/stripe-webhook-signature-verification/SKILL.md +137 -0
- package/examples/protocol/skill-metadata-template.md +301 -0
- package/examples/protocol/skills.manifest.sample.json +13245 -0
- package/examples/skill-metadata-template.md +317 -0
- package/examples/skills.manifest.sample.json +13519 -0
- package/examples/tests/v3-1-skos-fixture/SKILL.md +93 -0
- package/marketplace/README.md +17 -0
- package/marketplace/skills/a11y/SKILL.md +66 -0
- package/marketplace/skills/acid-fundamentals/SKILL.md +106 -0
- package/marketplace/skills/agent-engineering/SKILL.md +386 -0
- package/marketplace/skills/agent-eval-design/SKILL.md +55 -0
- package/marketplace/skills/ai-native-development/SKILL.md +294 -0
- package/marketplace/skills/api-design/SKILL.md +60 -0
- package/marketplace/skills/architecture-decision-records/SKILL.md +55 -0
- package/marketplace/skills/background-jobs/SKILL.md +265 -0
- package/marketplace/skills/bounded-context-mapping/SKILL.md +55 -0
- package/marketplace/skills/cap-theorem-tradeoffs/SKILL.md +127 -0
- package/marketplace/skills/client-server-boundary/SKILL.md +187 -0
- package/marketplace/skills/code-review/SKILL.md +120 -0
- package/marketplace/skills/color-system-design/SKILL.md +43 -0
- package/marketplace/skills/component-architecture/SKILL.md +126 -0
- package/marketplace/skills/compression/SKILL.md +112 -0
- package/marketplace/skills/conceptual-modeling/SKILL.md +181 -0
- package/marketplace/skills/connection-pooling/SKILL.md +105 -0
- package/marketplace/skills/constraint-awareness/SKILL.md +287 -0
- package/marketplace/skills/content-monitor/SKILL.md +209 -0
- package/marketplace/skills/context-engineering/SKILL.md +320 -0
- package/marketplace/skills/context-graph/SKILL.md +174 -0
- package/marketplace/skills/context-management/SKILL.md +174 -0
- package/marketplace/skills/context-window/SKILL.md +239 -0
- package/marketplace/skills/contract-testing/SKILL.md +120 -0
- package/marketplace/skills/cron-scheduling/SKILL.md +223 -0
- package/marketplace/skills/dark-mode-implementation/SKILL.md +47 -0
- package/marketplace/skills/data-modeling/SKILL.md +59 -0
- package/marketplace/skills/data-modeling-fundamentals/SKILL.md +117 -0
- package/marketplace/skills/database-migration/SKILL.md +429 -0
- package/marketplace/skills/debugging/SKILL.md +67 -0
- package/marketplace/skills/dependency-architecture/SKILL.md +58 -0
- package/marketplace/skills/design-module-composition/SKILL.md +43 -0
- package/marketplace/skills/design-system-architecture/SKILL.md +61 -0
- package/marketplace/skills/design-thinking/SKILL.md +44 -0
- package/marketplace/skills/diagnosis/SKILL.md +296 -0
- package/marketplace/skills/diff-analysis/SKILL.md +188 -0
- package/marketplace/skills/e2e-test-design/SKILL.md +113 -0
- package/marketplace/skills/entity-relationship-modeling/SKILL.md +218 -0
- package/marketplace/skills/epistemic-grounding/SKILL.md +112 -0
- package/marketplace/skills/error-boundary/SKILL.md +235 -0
- package/marketplace/skills/error-tracking/SKILL.md +261 -0
- package/marketplace/skills/eval-driven-development/SKILL.md +147 -0
- package/marketplace/skills/evaluation/SKILL.md +113 -0
- package/marketplace/skills/event-contract-design/SKILL.md +60 -0
- package/marketplace/skills/event-storming/SKILL.md +56 -0
- package/marketplace/skills/form-ux-architecture/SKILL.md +60 -0
- package/marketplace/skills/framework-fit-analysis/SKILL.md +59 -0
- package/marketplace/skills/frontend-architecture/SKILL.md +43 -0
- package/marketplace/skills/generative-ui/SKILL.md +118 -0
- package/marketplace/skills/graph-audit/SKILL.md +81 -0
- package/marketplace/skills/guardrails/SKILL.md +118 -0
- package/marketplace/skills/hooks-patterns/SKILL.md +185 -0
- package/marketplace/skills/http-semantics/SKILL.md +136 -0
- package/marketplace/skills/ideation/SKILL.md +41 -0
- package/marketplace/skills/indexing-strategy/SKILL.md +108 -0
- package/marketplace/skills/information-architecture/SKILL.md +59 -0
- package/marketplace/skills/integration-test-design/SKILL.md +111 -0
- package/marketplace/skills/intent-recognition/SKILL.md +136 -0
- package/marketplace/skills/interaction-feedback/SKILL.md +59 -0
- package/marketplace/skills/interaction-patterns/SKILL.md +59 -0
- package/marketplace/skills/journey-mapping/SKILL.md +41 -0
- package/marketplace/skills/keywords/SKILL.md +213 -0
- package/marketplace/skills/knowledge-modeling/SKILL.md +232 -0
- package/marketplace/skills/layout-composition/SKILL.md +59 -0
- package/marketplace/skills/linguistics/SKILL.md +429 -0
- package/marketplace/skills/lint-overlay/SKILL.md +76 -0
- package/marketplace/skills/mental-models/SKILL.md +126 -0
- package/marketplace/skills/merge-queue/SKILL.md +94 -0
- package/marketplace/skills/methodology/SKILL.md +317 -0
- package/marketplace/skills/microcopy/SKILL.md +232 -0
- package/marketplace/skills/middleware-patterns/SKILL.md +363 -0
- package/marketplace/skills/mobile-responsive-ux/SKILL.md +287 -0
- package/marketplace/skills/mutation-testing/SKILL.md +112 -0
- package/marketplace/skills/naming-conventions/SKILL.md +112 -0
- package/marketplace/skills/observability-modeling/SKILL.md +59 -0
- package/marketplace/skills/ontology-modeling/SKILL.md +67 -0
- package/marketplace/skills/owasp-security/SKILL.md +153 -0
- package/marketplace/skills/pattern-recognition/SKILL.md +472 -0
- package/marketplace/skills/performance-budgets/SKILL.md +185 -0
- package/marketplace/skills/performance-engineering/SKILL.md +58 -0
- package/marketplace/skills/performance-testing/SKILL.md +125 -0
- package/marketplace/skills/printify/SKILL.md +42 -0
- package/marketplace/skills/prioritization/SKILL.md +118 -0
- package/marketplace/skills/problem-framing/SKILL.md +41 -0
- package/marketplace/skills/problem-locating-solving/SKILL.md +203 -0
- package/marketplace/skills/project-knowledge-extraction/SKILL.md +54 -0
- package/marketplace/skills/prompt-craft/SKILL.md +134 -0
- package/marketplace/skills/prompt-injection-defense/SKILL.md +132 -0
- package/marketplace/skills/property-based-testing/SKILL.md +100 -0
- package/marketplace/skills/prototyping/SKILL.md +43 -0
- package/marketplace/skills/query-optimization/SKILL.md +144 -0
- package/marketplace/skills/real-time-updates/SKILL.md +324 -0
- package/marketplace/skills/ref-patterns/SKILL.md +284 -0
- package/marketplace/skills/refactor/SKILL.md +65 -0
- package/marketplace/skills/rendering-models/SKILL.md +142 -0
- package/marketplace/skills/replication-patterns/SKILL.md +110 -0
- package/marketplace/skills/research-synthesis/SKILL.md +41 -0
- package/marketplace/skills/route-handler-design/SKILL.md +347 -0
- package/marketplace/skills/schema-evolution/SKILL.md +140 -0
- package/marketplace/skills/security-fundamentals/SKILL.md +139 -0
- package/marketplace/skills/semantic-center/SKILL.md +194 -0
- package/marketplace/skills/semantic-relations/SKILL.md +250 -0
- package/marketplace/skills/semantics/SKILL.md +366 -0
- package/marketplace/skills/semiotics/SKILL.md +230 -0
- package/marketplace/skills/seo-strategy/SKILL.md +260 -0
- package/marketplace/skills/server-actions-design/SKILL.md +243 -0
- package/marketplace/skills/server-components-design/SKILL.md +190 -0
- package/marketplace/skills/sharding-strategy/SKILL.md +123 -0
- package/marketplace/skills/shopify/SKILL.md +42 -0
- package/marketplace/skills/skill-infrastructure/SKILL.md +320 -0
- package/marketplace/skills/skill-router/SKILL.md +71 -0
- package/marketplace/skills/skill-scaffold/SKILL.md +105 -0
- package/marketplace/skills/snapshot-testing/SKILL.md +120 -0
- package/marketplace/skills/spec-driven-development/SKILL.md +148 -0
- package/marketplace/skills/state-machine-modeling/SKILL.md +56 -0
- package/marketplace/skills/state-management/SKILL.md +134 -0
- package/marketplace/skills/streaming-architecture/SKILL.md +194 -0
- package/marketplace/skills/summarization/SKILL.md +156 -0
- package/marketplace/skills/suspense-patterns/SKILL.md +265 -0
- package/marketplace/skills/system-interface-contracts/SKILL.md +59 -0
- package/marketplace/skills/task-analysis/SKILL.md +201 -0
- package/marketplace/skills/taxonomy-design/SKILL.md +66 -0
- package/marketplace/skills/test-coverage-strategy/SKILL.md +108 -0
- package/marketplace/skills/test-doubles-design/SKILL.md +98 -0
- package/marketplace/skills/test-driven-development/SKILL.md +96 -0
- package/marketplace/skills/testing-strategy/SKILL.md +67 -0
- package/marketplace/skills/theme-system-design/SKILL.md +43 -0
- package/marketplace/skills/tool-call-flow/SKILL.md +229 -0
- package/marketplace/skills/tool-call-strategy/SKILL.md +292 -0
- package/marketplace/skills/transaction-isolation/SKILL.md +98 -0
- package/marketplace/skills/type-safety/SKILL.md +177 -0
- package/marketplace/skills/typography-system/SKILL.md +43 -0
- package/marketplace/skills/usability-testing/SKILL.md +43 -0
- package/marketplace/skills/user-research/SKILL.md +43 -0
- package/marketplace/skills/vercel-composition-patterns/SKILL.md +157 -0
- package/marketplace/skills/version-control/SKILL.md +233 -0
- package/marketplace/skills/visual-design-foundations/SKILL.md +59 -0
- package/marketplace/skills/visual-hierarchy/SKILL.md +43 -0
- package/marketplace/skills/webhook-integration/SKILL.md +331 -0
- package/marketplace/skills/writing-humanizer/SKILL.md +380 -0
- package/package.json +67 -0
- package/schemas/manifest.schema.json +811 -0
- package/schemas/manifest.v2.schema.json +164 -0
- package/schemas/manifest.v3.schema.json +758 -0
- package/schemas/manifest.v4.schema.json +755 -0
- package/schemas/manifest.v5.schema.json +755 -0
- package/schemas/manifest.v6.schema.json +811 -0
- package/schemas/skill.context.jsonld +279 -0
- package/schemas/skill.schema.json +919 -0
- package/schemas/skill.v2.schema.json +201 -0
- package/schemas/skill.v3.schema.json +827 -0
- package/schemas/skill.v4.schema.json +822 -0
- package/schemas/skill.v5.schema.json +830 -0
- package/schemas/skill.v6.schema.json +946 -0
- package/schemas/vocabulary/keywords.json +180 -0
- package/schemas/vocabulary/workspace_tags.json +23 -0
- package/scripts/__tests__/migrate-skill-v2-to-v3.test.js +161 -0
- package/scripts/__tests__/migrate-skill-v3-to-v4.test.js +158 -0
- package/scripts/__tests__/test-export-parser-drift.js +149 -0
- package/scripts/__tests__/test-marketplace-export.js +114 -0
- package/scripts/__tests__/test-router-paths.js +82 -0
- package/scripts/__tests__/test-stability-promotion.js +244 -0
- package/scripts/__tests__/test-v3-1-alias-contract.js +109 -0
- package/scripts/__tests__/test-v3-1-skos-runtime.js +116 -0
- package/scripts/backfill-schema-version.js +198 -0
- package/scripts/build-field-reference.js +160 -0
- package/scripts/build-retrieval-baseline.js +511 -0
- package/scripts/check-markdown-links.js +211 -0
- package/scripts/check-protocol-consistency.js +979 -0
- package/scripts/export-marketplace-skills.js +610 -0
- package/scripts/export-skill.js +374 -0
- package/scripts/generate-manifest.js +787 -0
- package/scripts/lib/alias-contract.js +83 -0
- package/scripts/lib/audit-prompt-builder.js +771 -0
- package/scripts/lib/mock-grader.js +134 -0
- package/scripts/lib/parse-frontmatter.js +429 -0
- package/scripts/lib/roots.js +119 -0
- package/scripts/lint/check-archetype-sections.js +185 -0
- package/scripts/lint/check-category-enum.js +83 -0
- package/scripts/lint/check-routing-eval.js +146 -0
- package/scripts/lint/check-routing-quality.js +211 -0
- package/scripts/lint/check-stability-promotion.js +220 -0
- package/scripts/lint/format-code-frame.js +206 -0
- package/scripts/marketplace-install.js +125 -0
- package/scripts/migrate-category-to-enum.js +169 -0
- package/scripts/migrate-skill-v2-to-v3.js +424 -0
- package/scripts/migrate-skill-v3-to-v4.js +200 -0
- package/scripts/migrate-skill-v5-to-v6.js +304 -0
- package/scripts/restructure-by-category.js +85 -0
- package/scripts/seed-publication-classification.js +282 -0
- package/scripts/skill-audit.js +893 -0
- package/scripts/skill-graph-drift.js +483 -0
- package/scripts/skill-graph-route.js +766 -0
- package/scripts/skill-graph-routing-eval.js +393 -0
- package/scripts/skill-lint.js +1317 -0
- package/scripts/skill-overlap.js +213 -0
- package/scripts/verify-skill-md-export.js +201 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Regression tests for marketplace export generation and gates.
|
|
4
|
+
*
|
|
5
|
+
* These tests require the canonical skills library (a sibling repo at
|
|
6
|
+
* ../skills/skills or configured via .skill-graph/config.json). In CI
|
|
7
|
+
* environments where only the skill-graph tooling repo is checked out,
|
|
8
|
+
* the tests skip gracefully rather than failing.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { workspaceRoot, loadWorkspaceConfig, resolveSkillRoots } = require('../lib/roots');
|
|
16
|
+
const {
|
|
17
|
+
EXPORT_DESCRIPTION_OVERRIDES,
|
|
18
|
+
MARKETPLACE_DESCRIPTION_LIMIT,
|
|
19
|
+
SKILL_GRAPH_PROTOCOL,
|
|
20
|
+
buildMarketplaceSkillText,
|
|
21
|
+
collectCanonicalSkills,
|
|
22
|
+
exportDescriptionForSkill,
|
|
23
|
+
rewriteLocalMarkdownLinksToCanonicalRepo,
|
|
24
|
+
scanPrivacyText,
|
|
25
|
+
} = require('../export-marketplace-skills');
|
|
26
|
+
const { parseFrontmatter } = require('../lib/parse-frontmatter');
|
|
27
|
+
const { validateExportedFrontmatter } = require('../verify-skill-md-export');
|
|
28
|
+
|
|
29
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
30
|
+
|
|
31
|
+
function fail(msg) {
|
|
32
|
+
process.stderr.write(`FAIL test-marketplace-export: ${msg}\n`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function assert(condition, msg) {
|
|
37
|
+
if (!condition) fail(msg);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Skip gracefully when the canonical skills library is not present (e.g. CI
|
|
41
|
+
// environments that only check out the skill-graph tooling repo).
|
|
42
|
+
const _root = workspaceRoot();
|
|
43
|
+
const _skillRoots = resolveSkillRoots(_root, loadWorkspaceConfig(_root));
|
|
44
|
+
const _sourceDir = _skillRoots[0] && _skillRoots[0].absPath;
|
|
45
|
+
if (!_sourceDir || !fs.existsSync(_sourceDir)) {
|
|
46
|
+
process.stdout.write(
|
|
47
|
+
`SKIP test-marketplace-export: canonical skills library not found at ${_sourceDir || '(no path resolved)'} — skipping\n`
|
|
48
|
+
);
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const skills = collectCanonicalSkills();
|
|
53
|
+
assert(skills.length > 0, 'canonical skills should be discovered');
|
|
54
|
+
|
|
55
|
+
const longDescriptions = [];
|
|
56
|
+
for (const skill of skills) {
|
|
57
|
+
const sourceLength = String(skill.fm.description || '').length;
|
|
58
|
+
const exportDescription = exportDescriptionForSkill(skill);
|
|
59
|
+
assert(
|
|
60
|
+
exportDescription.description.length <= MARKETPLACE_DESCRIPTION_LIMIT,
|
|
61
|
+
`${skill.fm.name} export description exceeds marketplace limit`
|
|
62
|
+
);
|
|
63
|
+
if (sourceLength > MARKETPLACE_DESCRIPTION_LIMIT) {
|
|
64
|
+
longDescriptions.push(skill.fm.name);
|
|
65
|
+
assert(
|
|
66
|
+
EXPORT_DESCRIPTION_OVERRIDES[skill.fm.name],
|
|
67
|
+
`${skill.fm.name} needs an explicit export description override`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const overrideNames = Object.keys(EXPORT_DESCRIPTION_OVERRIDES).sort();
|
|
73
|
+
assert(
|
|
74
|
+
JSON.stringify(overrideNames) === JSON.stringify(longDescriptions.sort()),
|
|
75
|
+
'description overrides should exist only for over-limit canonical descriptions'
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const a11y = skills.find(skill => skill.fm.name === 'a11y');
|
|
79
|
+
assert(a11y, 'a11y fixture skill should exist');
|
|
80
|
+
const exportedA11y = buildMarketplaceSkillText(a11y);
|
|
81
|
+
const exportedA11yFm = parseFrontmatter(exportedA11y);
|
|
82
|
+
assert(exportedA11yFm, 'marketplace export should have frontmatter');
|
|
83
|
+
const shape = validateExportedFrontmatter(exportedA11yFm);
|
|
84
|
+
assert(shape.errors.length === 0, `marketplace export should be plain SKILL.md shape: ${shape.errors.join('; ')}`);
|
|
85
|
+
assert(
|
|
86
|
+
exportedA11yFm.metadata.skill_graph_canonical_skill === 'skills/a11y/SKILL.md',
|
|
87
|
+
'marketplace export should preserve canonical source path'
|
|
88
|
+
);
|
|
89
|
+
assert(
|
|
90
|
+
exportedA11yFm.metadata.skill_graph_protocol === SKILL_GRAPH_PROTOCOL &&
|
|
91
|
+
SKILL_GRAPH_PROTOCOL === 'Skill Metadata Protocol v4',
|
|
92
|
+
'marketplace export should preserve current Skill Metadata Protocol provenance'
|
|
93
|
+
);
|
|
94
|
+
assert(
|
|
95
|
+
exportedA11y.includes('https://github.com/jacob-balslev/skill-graph/blob/main/examples/evals/a11y.json'),
|
|
96
|
+
'marketplace export should rewrite repo-local links to canonical GitHub URLs'
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const rewritten = rewriteLocalMarkdownLinksToCanonicalRepo(
|
|
100
|
+
'See [eval](../../examples/evals/a11y.json) and [external](https://example.com).',
|
|
101
|
+
'skills/a11y/SKILL.md'
|
|
102
|
+
);
|
|
103
|
+
assert(
|
|
104
|
+
rewritten.includes('https://github.com/jacob-balslev/skill-graph/blob/main/examples/evals/a11y.json'),
|
|
105
|
+
'relative links should be rewritten to canonical GitHub URLs'
|
|
106
|
+
);
|
|
107
|
+
assert(rewritten.includes('[external](https://example.com)'), 'external links should be preserved');
|
|
108
|
+
|
|
109
|
+
const fakeSecret = 'sk-' + 'A'.repeat(24);
|
|
110
|
+
const fakeLeak = `C:\\Users\\Example\\secret.txt\nperson@example.com\n${fakeSecret}`;
|
|
111
|
+
const findings = scanPrivacyText(fakeLeak, path.join(REPO_ROOT, 'marketplace', 'skills', 'fake', 'SKILL.md'));
|
|
112
|
+
assert(findings.length >= 3, 'privacy scan should detect paths, email addresses, and token-like values');
|
|
113
|
+
|
|
114
|
+
process.stdout.write('PASS test-marketplace-export: marketplace export gates covered\n');
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test: activation.paths include/exclude semantics.
|
|
4
|
+
*
|
|
5
|
+
* Guards the reference router against treating `!pattern` negations as positive
|
|
6
|
+
* path matches. Negations only subtract from earlier positive includes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { routeSkills, matchesGlob, matchPathList } = require('../skill-graph-route');
|
|
12
|
+
|
|
13
|
+
function fail(msg) {
|
|
14
|
+
process.stderr.write(`FAIL test-router-paths: ${msg}\n`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function pass(msg) {
|
|
19
|
+
process.stdout.write(`PASS test-router-paths: ${msg}\n`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function assert(condition, msg) {
|
|
23
|
+
if (!condition) fail(msg);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const a11ySkill = {
|
|
27
|
+
id: 'a11y',
|
|
28
|
+
name: 'a11y',
|
|
29
|
+
path: 'skills/a11y/SKILL.md',
|
|
30
|
+
type: 'capability',
|
|
31
|
+
scope: 'portable',
|
|
32
|
+
health: { eval_state: 'passing' },
|
|
33
|
+
activation: {
|
|
34
|
+
keywords: [],
|
|
35
|
+
triggers: [],
|
|
36
|
+
paths: [
|
|
37
|
+
'**/*.{html,tsx,jsx,vue,svelte}',
|
|
38
|
+
'**/*.css',
|
|
39
|
+
'!**/*.test.{ts,tsx,js,jsx}',
|
|
40
|
+
'!**/dist/**',
|
|
41
|
+
'!**/node_modules/**',
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function route(pathArg) {
|
|
47
|
+
return routeSkills({
|
|
48
|
+
skills: [a11ySkill],
|
|
49
|
+
}, {
|
|
50
|
+
query: 'unrelated prompt',
|
|
51
|
+
project: null,
|
|
52
|
+
maxResults: 5,
|
|
53
|
+
minEvalState: 'unverified',
|
|
54
|
+
pathArg,
|
|
55
|
+
todayISO: '2026-05-13',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
assert(matchesGlob('src/components/Button.tsx', '**/*.{html,tsx,jsx,vue,svelte}'), 'brace glob should match .tsx paths');
|
|
60
|
+
assert(matchesGlob('src/components/Button.test.tsx', '!**/*.test.{ts,tsx,js,jsx}'), 'matchesGlob should test the underlying negated glob');
|
|
61
|
+
|
|
62
|
+
const included = matchPathList('src/components/Button.tsx', a11ySkill.activation.paths);
|
|
63
|
+
assert(included.matched, 'positive .tsx path should match');
|
|
64
|
+
assert(included.pattern === '**/*.{html,tsx,jsx,vue,svelte}', 'path reason should be the positive include pattern');
|
|
65
|
+
|
|
66
|
+
const excluded = matchPathList('src/components/Button.test.tsx', a11ySkill.activation.paths);
|
|
67
|
+
assert(!excluded.matched, 'test path should be excluded by negation');
|
|
68
|
+
assert(excluded.excludedBy === '!**/*.test.{ts,tsx,js,jsx}', 'excludedBy should identify the subtractive pattern');
|
|
69
|
+
|
|
70
|
+
const negationOnly = matchPathList('src/components/Button.tsx', ['!**/*.test.{ts,tsx,js,jsx}']);
|
|
71
|
+
assert(!negationOnly.matched, 'a negation-only list must not create a positive match');
|
|
72
|
+
|
|
73
|
+
const includedRoute = route('src/components/Button.tsx');
|
|
74
|
+
assert(includedRoute.selected.length === 1, 'router should select a11y for included .tsx path');
|
|
75
|
+
assert(includedRoute.selected[0].reasons.includes('path:**/*.{html,tsx,jsx,vue,svelte}'), 'router should cite the positive path include');
|
|
76
|
+
assert(!includedRoute.selected[0].reasons.some(r => r.startsWith('path:!')), 'router must not cite negation as a positive path reason');
|
|
77
|
+
|
|
78
|
+
const excludedRoute = route('src/components/Button.test.tsx');
|
|
79
|
+
assert(excludedRoute.selected.length === 0, 'router should not select a11y for excluded test path');
|
|
80
|
+
|
|
81
|
+
pass('activation.paths negations subtract only from prior positive includes');
|
|
82
|
+
process.exit(0);
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test: stability promotion lint gate (check 14).
|
|
4
|
+
*
|
|
5
|
+
* Six test cases:
|
|
6
|
+
* 1. All-pass: stability=stable + all 5 criteria met → 0 warnings
|
|
7
|
+
* 2. Criterion 1 fail: eval_state=unverified → exactly 1 warning
|
|
8
|
+
* 3. Criterion 2 fail: eval_score below threshold → exactly 1 warning
|
|
9
|
+
* 4. Criterion 3 fail: routing_eval absent → exactly 1 warning
|
|
10
|
+
* 5. Criterion 4 fail: drift_check.last_verified stale → exactly 1 warning
|
|
11
|
+
* 6. Criterion 5 fail: no grounding.truth_sources → exactly 1 warning
|
|
12
|
+
*
|
|
13
|
+
* All failures must be WARN level (errors array must remain empty).
|
|
14
|
+
* Experimental / absent stability must produce 0 warnings (not gated).
|
|
15
|
+
*
|
|
16
|
+
* Exit 0 on all pass, 1 on first failure.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
checkStabilityPromotion,
|
|
23
|
+
STABLE_EVAL_SCORE_THRESHOLD,
|
|
24
|
+
STABLE_DRIFT_MAX_DAYS,
|
|
25
|
+
} = require('../lint/check-stability-promotion');
|
|
26
|
+
|
|
27
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
function fail(msg) {
|
|
30
|
+
process.stderr.write(`FAIL test-stability-promotion: ${msg}\n`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function pass(msg) {
|
|
35
|
+
process.stdout.write(`PASS test-stability-promotion: ${msg}\n`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function assert(condition, msg) {
|
|
39
|
+
if (!condition) fail(msg);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Build a minimal SKILL.md source text containing a `stability:` key in the
|
|
44
|
+
* frontmatter block. Used to give the locateKey helper something to parse.
|
|
45
|
+
*/
|
|
46
|
+
function buildSourceText(overrides = {}) {
|
|
47
|
+
const fm = Object.assign(
|
|
48
|
+
{
|
|
49
|
+
stability: 'stable',
|
|
50
|
+
eval_state: 'passing',
|
|
51
|
+
eval_score: STABLE_EVAL_SCORE_THRESHOLD,
|
|
52
|
+
routing_eval: 'present',
|
|
53
|
+
scope: 'codebase',
|
|
54
|
+
},
|
|
55
|
+
overrides
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Minimal YAML frontmatter (values are not full-YAML-parsed here; only the
|
|
59
|
+
// key locator is exercised by the test, not real parse-frontmatter).
|
|
60
|
+
const lines = ['---'];
|
|
61
|
+
for (const [k, v] of Object.entries(fm)) {
|
|
62
|
+
if (v !== undefined && v !== null) {
|
|
63
|
+
lines.push(`${k}: ${typeof v === 'string' ? v : JSON.stringify(v)}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
lines.push('---', '# Test skill body');
|
|
67
|
+
return lines.join('\n');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Run checkStabilityPromotion with a pre-built frontmatter object.
|
|
72
|
+
* Uses a fixed reference date so drift tests are deterministic.
|
|
73
|
+
*/
|
|
74
|
+
function run(fm, today) {
|
|
75
|
+
const sourceText = buildSourceText({ stability: fm.stability || 'stable' });
|
|
76
|
+
return checkStabilityPromotion({ filePath: 'test/SKILL.md', sourceText, fm, today });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Fixed reference date for deterministic drift tests.
|
|
80
|
+
const TODAY = new Date('2026-05-18T00:00:00Z');
|
|
81
|
+
|
|
82
|
+
// ISO date that is within the allowed window (45 days ago from TODAY).
|
|
83
|
+
const RECENT_DATE = '2026-04-03';
|
|
84
|
+
// ISO date that is outside the allowed window (100 days ago from TODAY).
|
|
85
|
+
const STALE_DATE = '2026-01-28';
|
|
86
|
+
|
|
87
|
+
// ── Shared base frontmatter (all criteria satisfied) ─────────────────────────
|
|
88
|
+
|
|
89
|
+
const BASE_FM = {
|
|
90
|
+
stability: 'stable',
|
|
91
|
+
eval_state: 'passing',
|
|
92
|
+
eval_score: STABLE_EVAL_SCORE_THRESHOLD,
|
|
93
|
+
routing_eval: 'present',
|
|
94
|
+
drift_check: { last_verified: RECENT_DATE },
|
|
95
|
+
scope: 'codebase',
|
|
96
|
+
grounding: {
|
|
97
|
+
domain_object: 'test',
|
|
98
|
+
truth_sources: ['schemas/skill.schema.json'],
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// ── Test 1: All criteria met — 0 warnings ────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
(function testAllPass() {
|
|
105
|
+
const result = run(BASE_FM, TODAY);
|
|
106
|
+
assert(result.errors.length === 0, 'test1: must have no errors');
|
|
107
|
+
assert(result.warnings.length === 0, `test1: all-pass should emit 0 warnings, got ${result.warnings.length}: ${result.warnings.map(w => w.message).join('; ')}`);
|
|
108
|
+
pass('test1 (all criteria met) → 0 warnings');
|
|
109
|
+
})();
|
|
110
|
+
|
|
111
|
+
// ── Test 2: Criterion 1 fail — eval_state=unverified ─────────────────────────
|
|
112
|
+
|
|
113
|
+
(function testCriterion1Fail() {
|
|
114
|
+
const fm = Object.assign({}, BASE_FM, { eval_state: 'unverified' });
|
|
115
|
+
const result = run(fm, TODAY);
|
|
116
|
+
assert(result.errors.length === 0, 'test2: must have no errors (WARN only)');
|
|
117
|
+
assert(
|
|
118
|
+
result.warnings.length === 1,
|
|
119
|
+
`test2: criterion-1 fail should emit exactly 1 warning, got ${result.warnings.length}: ${result.warnings.map(w => w.message).join('; ')}`
|
|
120
|
+
);
|
|
121
|
+
assert(
|
|
122
|
+
result.warnings[0].message.includes('criterion 1'),
|
|
123
|
+
`test2: warning must reference criterion 1, got: ${result.warnings[0].message}`
|
|
124
|
+
);
|
|
125
|
+
assert(
|
|
126
|
+
result.warnings[0].message.includes('unverified'),
|
|
127
|
+
`test2: warning must mention "unverified", got: ${result.warnings[0].message}`
|
|
128
|
+
);
|
|
129
|
+
pass('test2 (eval_state=unverified) → 1 warning on criterion 1');
|
|
130
|
+
})();
|
|
131
|
+
|
|
132
|
+
// ── Test 3: Criterion 2 fail — eval_score below threshold ────────────────────
|
|
133
|
+
|
|
134
|
+
(function testCriterion2Fail() {
|
|
135
|
+
const fm = Object.assign({}, BASE_FM, { eval_score: 3.5 });
|
|
136
|
+
const result = run(fm, TODAY);
|
|
137
|
+
assert(result.errors.length === 0, 'test3: must have no errors (WARN only)');
|
|
138
|
+
assert(
|
|
139
|
+
result.warnings.length === 1,
|
|
140
|
+
`test3: criterion-2 fail should emit exactly 1 warning, got ${result.warnings.length}: ${result.warnings.map(w => w.message).join('; ')}`
|
|
141
|
+
);
|
|
142
|
+
assert(
|
|
143
|
+
result.warnings[0].message.includes('criterion 2'),
|
|
144
|
+
`test3: warning must reference criterion 2, got: ${result.warnings[0].message}`
|
|
145
|
+
);
|
|
146
|
+
assert(
|
|
147
|
+
result.warnings[0].message.includes('3.5'),
|
|
148
|
+
`test3: warning must mention the failing score, got: ${result.warnings[0].message}`
|
|
149
|
+
);
|
|
150
|
+
pass('test3 (eval_score=3.5 < 4.0) → 1 warning on criterion 2');
|
|
151
|
+
})();
|
|
152
|
+
|
|
153
|
+
// ── Test 4: Criterion 3 fail — routing_eval absent ───────────────────────────
|
|
154
|
+
|
|
155
|
+
(function testCriterion3Fail() {
|
|
156
|
+
const fm = Object.assign({}, BASE_FM, { routing_eval: 'absent' });
|
|
157
|
+
const result = run(fm, TODAY);
|
|
158
|
+
assert(result.errors.length === 0, 'test4: must have no errors (WARN only)');
|
|
159
|
+
assert(
|
|
160
|
+
result.warnings.length === 1,
|
|
161
|
+
`test4: criterion-3 fail should emit exactly 1 warning, got ${result.warnings.length}: ${result.warnings.map(w => w.message).join('; ')}`
|
|
162
|
+
);
|
|
163
|
+
assert(
|
|
164
|
+
result.warnings[0].message.includes('criterion 3'),
|
|
165
|
+
`test4: warning must reference criterion 3, got: ${result.warnings[0].message}`
|
|
166
|
+
);
|
|
167
|
+
assert(
|
|
168
|
+
result.warnings[0].message.includes('routing_eval'),
|
|
169
|
+
`test4: warning must mention routing_eval, got: ${result.warnings[0].message}`
|
|
170
|
+
);
|
|
171
|
+
pass('test4 (routing_eval=absent) → 1 warning on criterion 3');
|
|
172
|
+
})();
|
|
173
|
+
|
|
174
|
+
// ── Test 5: Criterion 4 fail — drift_check.last_verified stale ───────────────
|
|
175
|
+
|
|
176
|
+
(function testCriterion4Fail() {
|
|
177
|
+
const fm = Object.assign({}, BASE_FM, { drift_check: { last_verified: STALE_DATE } });
|
|
178
|
+
const result = run(fm, TODAY);
|
|
179
|
+
assert(result.errors.length === 0, 'test5: must have no errors (WARN only)');
|
|
180
|
+
assert(
|
|
181
|
+
result.warnings.length === 1,
|
|
182
|
+
`test5: criterion-4 fail should emit exactly 1 warning, got ${result.warnings.length}: ${result.warnings.map(w => w.message).join('; ')}`
|
|
183
|
+
);
|
|
184
|
+
assert(
|
|
185
|
+
result.warnings[0].message.includes('criterion 4'),
|
|
186
|
+
`test5: warning must reference criterion 4, got: ${result.warnings[0].message}`
|
|
187
|
+
);
|
|
188
|
+
assert(
|
|
189
|
+
result.warnings[0].message.includes('days ago'),
|
|
190
|
+
`test5: warning must mention days-ago age, got: ${result.warnings[0].message}`
|
|
191
|
+
);
|
|
192
|
+
pass('test5 (drift_check.last_verified=stale) → 1 warning on criterion 4');
|
|
193
|
+
})();
|
|
194
|
+
|
|
195
|
+
// ── Test 6: Criterion 5 fail — no grounding.truth_sources (codebase scope) ───
|
|
196
|
+
|
|
197
|
+
(function testCriterion5Fail() {
|
|
198
|
+
const fm = Object.assign({}, BASE_FM, {
|
|
199
|
+
scope: 'codebase',
|
|
200
|
+
grounding: { domain_object: 'test', truth_sources: [] },
|
|
201
|
+
});
|
|
202
|
+
const result = run(fm, TODAY);
|
|
203
|
+
assert(result.errors.length === 0, 'test6: must have no errors (WARN only)');
|
|
204
|
+
assert(
|
|
205
|
+
result.warnings.length === 1,
|
|
206
|
+
`test6: criterion-5 fail should emit exactly 1 warning, got ${result.warnings.length}: ${result.warnings.map(w => w.message).join('; ')}`
|
|
207
|
+
);
|
|
208
|
+
assert(
|
|
209
|
+
result.warnings[0].message.includes('criterion 5'),
|
|
210
|
+
`test6: warning must reference criterion 5, got: ${result.warnings[0].message}`
|
|
211
|
+
);
|
|
212
|
+
assert(
|
|
213
|
+
result.warnings[0].message.includes('truth_sources'),
|
|
214
|
+
`test6: warning must mention truth_sources, got: ${result.warnings[0].message}`
|
|
215
|
+
);
|
|
216
|
+
pass('test6 (grounding.truth_sources empty, scope=codebase) → 1 warning on criterion 5');
|
|
217
|
+
})();
|
|
218
|
+
|
|
219
|
+
// ── Bonus: experimental stability must produce 0 warnings ─────────────────────
|
|
220
|
+
|
|
221
|
+
(function testExperimentalSkipped() {
|
|
222
|
+
const fm = Object.assign({}, BASE_FM, { stability: 'experimental' });
|
|
223
|
+
const result = run(fm, TODAY);
|
|
224
|
+
assert(result.errors.length === 0, 'bonus: must have no errors');
|
|
225
|
+
assert(result.warnings.length === 0, `bonus: experimental stability must not trigger the gate, got ${result.warnings.length} warnings`);
|
|
226
|
+
pass('bonus (stability=experimental) → gate skipped, 0 warnings');
|
|
227
|
+
})();
|
|
228
|
+
|
|
229
|
+
// ── Bonus: portable scope is exempt from criterion 5 ─────────────────────────
|
|
230
|
+
|
|
231
|
+
(function testPortableScopeExempt() {
|
|
232
|
+
const fm = Object.assign({}, BASE_FM, {
|
|
233
|
+
scope: 'portable',
|
|
234
|
+
grounding: undefined, // no grounding at all — still exempt
|
|
235
|
+
});
|
|
236
|
+
const result = run(fm, TODAY);
|
|
237
|
+
// Portable has no grounding required, so criterion 5 should NOT fire.
|
|
238
|
+
// Other criteria still apply; count only criterion-5 warnings.
|
|
239
|
+
const criterion5Warnings = result.warnings.filter(w => w.message.includes('criterion 5'));
|
|
240
|
+
assert(criterion5Warnings.length === 0, `bonus portable: portable scope must be exempt from criterion 5, got: ${criterion5Warnings.map(w => w.message).join('; ')}`);
|
|
241
|
+
pass('bonus (scope=portable) → criterion 5 skipped');
|
|
242
|
+
})();
|
|
243
|
+
|
|
244
|
+
process.stdout.write('All stability-promotion tests passed.\n');
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test: v3.1 preferred field aliases round-trip and mismatch gates.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { buildSkillEntry } = require('../generate-manifest');
|
|
10
|
+
const { checkAliasParity } = require('../lib/alias-contract');
|
|
11
|
+
|
|
12
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
13
|
+
const FIXTURE_PATH = path.join(REPO_ROOT, 'examples', 'tests', 'v3-1-alias-fixture.md');
|
|
14
|
+
|
|
15
|
+
function fail(msg) {
|
|
16
|
+
process.stderr.write(`FAIL test-v3-1-alias-contract: ${msg}\n`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function pass(msg) {
|
|
21
|
+
process.stdout.write(`PASS test-v3-1-alias-contract: ${msg}\n`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function assert(condition, msg) {
|
|
25
|
+
if (!condition) fail(msg);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const fm = {
|
|
29
|
+
schema_version: 4,
|
|
30
|
+
name: 'alias-fixture',
|
|
31
|
+
urn: 'urn:skill:skill-graph:alias-fixture',
|
|
32
|
+
description: 'Fixture skill used to verify v3.1 alias pass-through and parity checks.',
|
|
33
|
+
version: '1.0.0',
|
|
34
|
+
type: 'capability',
|
|
35
|
+
archetype: 'capability',
|
|
36
|
+
category: 'testing',
|
|
37
|
+
domain: 'skill-system/testing',
|
|
38
|
+
scope: 'portable',
|
|
39
|
+
owner: 'skill-graph-maintainer',
|
|
40
|
+
freshness: '2026-05-13',
|
|
41
|
+
reviewed_at: '2026-05-13',
|
|
42
|
+
drift_check: {
|
|
43
|
+
last_verified: '2026-05-13',
|
|
44
|
+
verified_at: '2026-05-13',
|
|
45
|
+
},
|
|
46
|
+
eval_artifacts: 'none',
|
|
47
|
+
eval_state: 'unverified',
|
|
48
|
+
routing_eval: 'absent',
|
|
49
|
+
comprehension_state: 'absent',
|
|
50
|
+
eval: {
|
|
51
|
+
artifacts: 'none',
|
|
52
|
+
content_state: 'unverified',
|
|
53
|
+
routing_coverage: 'absent',
|
|
54
|
+
comprehension_state: 'absent',
|
|
55
|
+
},
|
|
56
|
+
'allowed-tools': 'Read Grep',
|
|
57
|
+
allowed_tools: 'Read Grep',
|
|
58
|
+
compatibility: {
|
|
59
|
+
runtimes: ['claude-code>=2.0'],
|
|
60
|
+
agent_runtimes: ['claude-code>=2.0'],
|
|
61
|
+
node: '>=20',
|
|
62
|
+
node_version: '>=20',
|
|
63
|
+
},
|
|
64
|
+
grounding: {
|
|
65
|
+
domain_object: 'Alias contract',
|
|
66
|
+
subject: 'Alias contract',
|
|
67
|
+
grounding_mode: 'repo_specific',
|
|
68
|
+
claim_scope: 'repo_specific',
|
|
69
|
+
truth_sources: ['schemas/skill.schema.json'],
|
|
70
|
+
failure_modes: ['silent_alias_drop'],
|
|
71
|
+
evidence_priority: 'repo_code_first',
|
|
72
|
+
},
|
|
73
|
+
portability: {
|
|
74
|
+
readiness: 'scripted',
|
|
75
|
+
targets: ['skill-md'],
|
|
76
|
+
export_targets: ['skill-md'],
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const parityErrors = checkAliasParity(fm);
|
|
81
|
+
assert(parityErrors.length === 0, `fixture should satisfy alias parity, got: ${parityErrors.join('; ')}`);
|
|
82
|
+
|
|
83
|
+
const entry = buildSkillEntry(fm, FIXTURE_PATH, 'alias-fixture', null);
|
|
84
|
+
assert(entry.urn === fm.urn, 'urn should pass through');
|
|
85
|
+
assert(entry.archetype === fm.archetype, 'archetype alias should pass through');
|
|
86
|
+
assert(entry.domain === fm.domain, 'domain should pass through');
|
|
87
|
+
assert(entry.allowed_tools === fm.allowed_tools, 'allowed_tools alias should pass through');
|
|
88
|
+
assert(entry.health.reviewed_at === fm.reviewed_at, 'reviewed_at alias should project to health.reviewed_at');
|
|
89
|
+
assert(entry.health.eval && entry.health.eval.content_state === 'unverified', 'nested eval alias should project to health.eval');
|
|
90
|
+
assert(entry.compatibility.agent_runtimes[0] === 'claude-code>=2.0', 'compatibility.agent_runtimes should pass through');
|
|
91
|
+
assert(entry.compatibility.node_version === '>=20', 'compatibility.node_version should pass through');
|
|
92
|
+
assert(entry.grounding.subject === 'Alias contract', 'grounding.subject should pass through');
|
|
93
|
+
assert(entry.grounding.claim_scope === 'repo_specific', 'grounding.claim_scope should pass through');
|
|
94
|
+
assert(entry.portability.export_targets[0] === 'skill-md', 'portability.export_targets should pass through');
|
|
95
|
+
|
|
96
|
+
const mismatch = { ...fm, archetype: 'workflow' };
|
|
97
|
+
const mismatchErrors = checkAliasParity(mismatch);
|
|
98
|
+
assert(mismatchErrors.some(e => e.includes('archetype')), 'alias mismatch should be reported by lint helper');
|
|
99
|
+
|
|
100
|
+
let threw = false;
|
|
101
|
+
try {
|
|
102
|
+
buildSkillEntry(mismatch, FIXTURE_PATH, 'alias-fixture', null);
|
|
103
|
+
} catch (e) {
|
|
104
|
+
threw = /alias contract violation/.test(e.message);
|
|
105
|
+
}
|
|
106
|
+
assert(threw, 'manifest generator should refuse mismatched aliases');
|
|
107
|
+
|
|
108
|
+
pass('v3.1 aliases pass through and mismatches fail');
|
|
109
|
+
process.exit(0);
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test: v3.1 SKOS predicate runtime migration (R1)
|
|
4
|
+
*
|
|
5
|
+
* Verifies that the manifest generator iterates the complete v3.1 predicate set
|
|
6
|
+
* (related, broader, narrower, boundary, disjoint_with, verify_with, depends_on,
|
|
7
|
+
* adjacent) and emits all declared relations into the manifest entry.
|
|
8
|
+
*
|
|
9
|
+
* Before R1, the iteration list in `generate-manifest.js` was hardcoded to
|
|
10
|
+
* ['adjacent', 'boundary', 'verify_with', 'depends_on'] and silently dropped
|
|
11
|
+
* `related`, `broader`, `narrower`, `disjoint_with`. Authors who followed
|
|
12
|
+
* ADR 0001 v3.1 SKOS naming would see lint warnings nudging them to the new
|
|
13
|
+
* names but their relations would never reach the manifest.
|
|
14
|
+
*
|
|
15
|
+
* This test asserts the regression is closed: build a manifest entry from the
|
|
16
|
+
* fixture, confirm every declared predicate survives the round-trip.
|
|
17
|
+
*
|
|
18
|
+
* Exit 0 on pass, 1 on failure.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
'use strict';
|
|
22
|
+
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
const { parseFrontmatter } = require('../lib/parse-frontmatter');
|
|
26
|
+
const { buildSkillEntry } = require('../generate-manifest');
|
|
27
|
+
|
|
28
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
29
|
+
const FIXTURE_PATH = path.join(REPO_ROOT, 'examples', 'tests', 'v3-1-skos-fixture', 'SKILL.md');
|
|
30
|
+
|
|
31
|
+
const EXPECTED_PREDICATES = [
|
|
32
|
+
// v3.1 SKOS additions
|
|
33
|
+
'related',
|
|
34
|
+
'broader',
|
|
35
|
+
'narrower',
|
|
36
|
+
// v3.0 stable + canonical (per ADR 0006)
|
|
37
|
+
'boundary',
|
|
38
|
+
'verify_with',
|
|
39
|
+
'depends_on',
|
|
40
|
+
// v3.1 separate orthogonal relation per ADR 0006 Option B
|
|
41
|
+
'disjoint_with',
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
function fail(msg) {
|
|
45
|
+
process.stderr.write(`FAIL test-v3-1-skos-runtime: ${msg}\n`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function pass(msg) {
|
|
50
|
+
process.stdout.write(`PASS test-v3-1-skos-runtime: ${msg}\n`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function main() {
|
|
54
|
+
if (!fs.existsSync(FIXTURE_PATH)) {
|
|
55
|
+
fail(`fixture missing at ${path.relative(REPO_ROOT, FIXTURE_PATH)}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const text = fs.readFileSync(FIXTURE_PATH, 'utf8');
|
|
59
|
+
const fm = parseFrontmatter(text);
|
|
60
|
+
if (!fm) fail('fixture has no parseable frontmatter');
|
|
61
|
+
|
|
62
|
+
// Guard: every expected predicate is declared in the fixture frontmatter.
|
|
63
|
+
for (const kind of EXPECTED_PREDICATES) {
|
|
64
|
+
const arr = fm.relations && fm.relations[kind];
|
|
65
|
+
if (!Array.isArray(arr) || arr.length === 0) {
|
|
66
|
+
fail(`fixture frontmatter missing relations.${kind} — fixture is the test substrate, must declare every predicate under test`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const entry = buildSkillEntry(fm, FIXTURE_PATH, 'v3-1-skos-fixture', null);
|
|
71
|
+
|
|
72
|
+
if (!entry || typeof entry !== 'object') fail('buildSkillEntry returned non-object');
|
|
73
|
+
if (!entry.relations || typeof entry.relations !== 'object') {
|
|
74
|
+
fail('manifest entry has no `relations` block — generator dropped all relations');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Round-trip assertion: every predicate declared in the fixture must appear
|
|
78
|
+
// in the manifest entry's relations block with the same target list shape.
|
|
79
|
+
const missing = [];
|
|
80
|
+
const sizeMismatch = [];
|
|
81
|
+
for (const kind of EXPECTED_PREDICATES) {
|
|
82
|
+
if (!Array.isArray(entry.relations[kind])) {
|
|
83
|
+
missing.push(kind);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (entry.relations[kind].length !== fm.relations[kind].length) {
|
|
87
|
+
sizeMismatch.push(`${kind}: fixture has ${fm.relations[kind].length}, manifest has ${entry.relations[kind].length}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (missing.length > 0) {
|
|
92
|
+
fail(`predicates dropped from manifest entry: ${missing.join(', ')}. Generator iteration list does not include these keys.`);
|
|
93
|
+
}
|
|
94
|
+
if (sizeMismatch.length > 0) {
|
|
95
|
+
fail(`predicate target counts differ between fixture and manifest:\n - ${sizeMismatch.join('\n - ')}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Sanity: the back-compat alias `adjacent` continues to flow through if declared.
|
|
99
|
+
// Fixture omits `adjacent` deliberately (it would draw a deprecation warning),
|
|
100
|
+
// so this test only confirms the iteration list KNOWS about adjacent. We
|
|
101
|
+
// synthesise a parallel fixture in-memory to verify.
|
|
102
|
+
const adjacentFm = {
|
|
103
|
+
...fm,
|
|
104
|
+
relations: { adjacent: ['documentation'] },
|
|
105
|
+
};
|
|
106
|
+
const adjacentEntry = buildSkillEntry(adjacentFm, FIXTURE_PATH, 'v3-1-skos-fixture-adjacent', null);
|
|
107
|
+
if (!adjacentEntry.relations || !Array.isArray(adjacentEntry.relations.adjacent) || adjacentEntry.relations.adjacent.length !== 1) {
|
|
108
|
+
fail('back-compat regression: relations.adjacent is dropped by generator (should still flow through as an alias for related per ADR 0001)');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
pass(`all ${EXPECTED_PREDICATES.length} v3.1 predicates round-trip cleanly + adjacent back-compat preserved`);
|
|
112
|
+
pass('R1 closed: ADR 0001 SKOS additions and ADR 0006 boundary/disjoint_with split are implemented in scripts/generate-manifest.js');
|
|
113
|
+
process.exit(0);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
main();
|