Help us improve
Share bugs, ideas, or general feedback.
From linkml-reviewer
Use when reviewing new or modified LinkML schema files (.yaml) in agr_curation_schema, or when proposing changes to Alliance of Genome Resources schema classes, slots, enums, or associations
npx claudepluginhub alliance-genome/agr_claude_code --plugin linkml-reviewerHow this skill is triggered — by the user, by Claude, or both
Slash command
/linkml-reviewer:linkml-reviewThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Expert review guide for the Alliance of Genome Resources LinkML schema (`agr_curation_schema`). Reviews changes for correctness, consistency, and convention compliance.
Validates OpenAPI, JSON Schema, and GraphQL API specs with linting, structural analysis, completeness checks, breaking change detection, and consistency enforcement. Generates reports.
Create and manage DeepSchemas for automatic file validation on write/edit and review rule generation using YAML requirements and JSON Schemas. Matches files via globs.
Builds, validates, governs RDF/OWL ontologies and knowledge graphs using Open Ontologies MCP server with Oxigraph triple store. For creating, modifying, querying, or managing RDF data.
Share bugs, ideas, or general feedback.
Expert review guide for the Alliance of Genome Resources LinkML schema (agr_curation_schema). Reviews changes for correctness, consistency, and convention compliance.
CRITICAL: You MUST run this bash block when the skill is first loaded, before doing anything else.
# Check for skill updates
PLUGIN_JSON=$(ls -t ~/.claude/plugins/cache/alliance-plugins/linkml-reviewer/*/.claude-plugin/plugin.json 2>/dev/null | head -1)
INSTALLED_VERSION=$(grep -o '"version": "[^"]*"' "$PLUGIN_JSON" 2>/dev/null | cut -d'"' -f4)
LATEST_VERSION=$(curl -sf --max-time 3 https://raw.githubusercontent.com/alliance-genome/agr_claude_code/main/plugins/linkml-reviewer/.claude-plugin/plugin.json 2>/dev/null | grep -o '"version": "[^"]*"' | cut -d'"' -f4)
if [ -z "$LATEST_VERSION" ]; then
echo "Skill version: ${INSTALLED_VERSION} (could not check for updates)"
elif [ "$INSTALLED_VERSION" != "$LATEST_VERSION" ]; then
echo "*** UPDATE AVAILABLE *** Installed v${INSTALLED_VERSION}, latest v${LATEST_VERSION}"
echo "Run: /plugin marketplace update alliance-plugins"
else
echo "Skill version: ${INSTALLED_VERSION} (up to date)"
fi
Before reviewing: Read the reference files in
reference/for current schema inventory data (class counts, inheritance tree, import graph, DTO suffix mappings).
Every convention below is tagged:
| Tag | Meaning | Review Action |
|---|---|---|
| ENFORCED | Schema validates this; violations cause build failures | Flag as error |
| ADVISORY | Documented convention, not machine-enforced | Flag as warning |
| TECH-DEBT | Known issue in existing schema; flag only in new code | Flag as info |
model/schema/, ~12,118 lines, 358 classes (38 abstract), 13 enumsallianceModel.yaml -- aggregates all sub-schemas via imports; has NO classes/slots/enumscore.yaml -- base classes, common slots (imported by 26/27 files)ingest.yaml -- Ingest class (tree_root: true) with ~50 *_ingest_set slotsgen-json-schema --indent 4 --closed -t Ingest [ENFORCED]test/data/*.json (32 valid) + test/data/invalid/*.json (4 invalid)AuditedObject -> CurieObject -> SubmittedObject -> BiologicalEntity [abstract] -> GenomicEntity -> Gene, Allele, Variant, AGM...
Parallel DTO hierarchy: AuditedObjectDTO -> SubmittedObjectDTO -> BiologicalEntityDTO [abstract] -> GenomicEntityDTO -> GeneDTO...
See inheritance-tree.md for the complete tree.
| Convention | Status |
|---|---|
| CamelCase names | ADVISORY |
Must have is_a pointing to valid parent | ADVISORY |
Must have meaningful description | ADVISORY |
New YAML file -> add to allianceModel.yaml imports | ENFORCED |
Abstract classes marked abstract: true | ENFORCED |
| New domain class -> add corresponding DTO class | ADVISORY |
New entity type -> add *_ingest_set slot to ingest.yaml | ADVISORY |
Test data added to test/data/ | ADVISORY |
| Convention | Status |
|---|---|
| snake_case names | ADVISORY |
| Multivalued -> plural name; single-valued -> singular | ADVISORY |
range set explicitly (don't rely on default_range: string) | ADVISORY |
Entity-prefixed names (gene_symbol, allele_full_name) | ADVISORY |
slot_usage for inheritance customization | ADVISORY |
internal is required: true on AuditedObject | ENFORCED |
For inlined DTOs: inlined: true + inlined_as_list: true | ADVISORY |
| Non-DTO Range | DTO Suffix | Example |
|---|---|---|
| OntologyTerm subclass (single) | _curie | taxon -> taxon_curie |
| Reference/Person (single) | _curie | created_by -> created_by_curie |
| Reference (multi) | _curies | references -> reference_curies |
| VocabularyTerm (single) | _name | genetic_sex -> genetic_sex_name |
| VocabularyTerm (multi) | _names | disease_qualifiers -> disease_qualifier_names |
| Entity (single) | _identifier | subject -> gene_identifier |
| Entity (multi) | _identifiers | -> with_gene_identifiers |
| Inlined DTO (single) | _dto | data_provider -> data_provider_dto |
| Inlined DTOs (multi) | _dtos | cross_references -> cross_reference_dtos |
| String/boolean/integer | same name | is_extinct -> is_extinct |
See dto-suffix-reference.md for complete mapping.
| Type | When to Use | Examples |
|---|---|---|
| Enum | Small, stable, technical/system values that will NOT change | strand_enum, pubmed_publication_status_enum |
| OntologyTerm | Externally maintained ontology terms | SOTerm, DOTerm, GOTerm, ZECOTerm |
| VocabularyTerm | Alliance-curated biological/curation values | genetic_sex, disease_qualifiers, note types |
Rule: Biological/curation values MUST use VocabularyTerms, not enums. [ADVISORY]
Note: CV names are documented in comments only (
# CV 'Genetic Sex'), not enforced by schema. [ADVISORY]
| Convention | Status |
|---|---|
Inherit from Association, SingleReferenceAssociation, or EvidenceAssociation | ADVISORY |
relation slot present and constrained via slot_usage | ADVISORY |
Entity-prefixed subject/object names (NOT generic subject/object) | ADVISORY |
| Corresponding AssociationDTO class for ingest | ADVISORY |
| Convention | Status |
|---|---|
Ingest is sole tree_root: true class | ENFORCED |
All *_ingest_set slots use mixins: [object_set] | ENFORCED |
Ingest set range is a DTO class | ADVISORY |
Every schema file should include:
default_prefix: alliance
default_range: string
default_curi_maps:
- obo_context
- idot_context
- semweb_context
- monarch_context
emit_prefixes:
- rdf
- rdfs
- xsd
Check consistency with existing files.
is_a points to valid parent? [ADVISORY]abstract: true if abstract? [ENFORCED]allianceModel.yaml? [ENFORCED]description (not "Dummy" or placeholder)? [ADVISORY]*_ingest_set slot added to ingest.yaml? [ADVISORY]test/data/? [ADVISORY]multivalued set? Plural if multivalued, singular if not? [ADVISORY]range set explicitly? [ADVISORY]required set where needed? [ADVISORY]inlined: true and inlined_as_list: true? [ADVISORY]relation slot present and constrained? [ADVISORY]OntologyTerm)? [ADVISORY]_name/_curie)? [ADVISORY]*_ingest_set uses mixins: [object_set]? [ENFORCED]internal explicitly required at every nesting level? [ENFORCED]default_prefix, default_range, default_curi_maps present? [ADVISORY]"linkml_version": "2.0.0"? [ADVISORY]gen-json-schema --closed validation? [ENFORCED]| Mistake | Why It Matters |
|---|---|
| Adding slot to class but not to corresponding DTO | Ingest will miss the field |
Using range: OntologyTerm instead of specific subclass (SOTerm) | Loses type safety |
Forgetting *_ingest_set slot in both slots: section AND Ingest class | Ingest won't include the data |
| Using enum for values that should be VocabularyTerms | Enums can't be managed at runtime |
Missing internal: required: true when overriding AuditedObject | Breaks validation |
| Wrong DTO suffix convention | Inconsistent ingest API |
multivalued: true with singular name (or vice versa) | Confusing API surface |
| Association without entity-prefixed subject/object names | Breaks persistence conventions |
Missing inlined: true + inlined_as_list: true for inlined DTO collections | JSON Schema won't inline correctly |
New YAML file not added to allianceModel.yaml imports | File is invisible to build |
| Inconsistent prefix declarations vs other schema files | Subtle resolution bugs |
internal required but no ifabsent default: Every ingest object at every depth must explicitly set "internal": false/true. The notes say "Default value is true" but this is documentation only.# CV 'Genetic Sex' has no technical enforcement.domain constraints are documentation only [ADVISORY]: A slot with domain: Gene can still be used elsewhere.evidence vs evidence_item near-duplication [TECH-DEBT]: Difference is multivalued vs single-valued; naming doesn't make this obvious.AnatomicalSiteDTO inherits from AuditedObject not AuditedObjectDTO [TECH-DEBT]: Diverges from standard DTO pattern.emit_prefixes)interactor_A/B_* naming breaks snake_caseifabsent defaults used anywherecurationReport.yaml ID mismatch)| File | Contents |
|---|---|
| schema-inventory.md | File inventory with class/slot/enum counts |
| inheritance-tree.md | Complete class hierarchy with source files |
| import-graph.md | Mermaid import dependency graph |
| dto-suffix-reference.md | Complete slot-to-DTO suffix mappings |