ワンクリックで
check-schema-drift
Identify drift between specification markdown files and JSON schemas
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Identify drift between specification markdown files and JSON schemas
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Detect drift between go-glx Go types and JSON schemas
Identify drift between docs/ folder and specification/schemas/code
Review example GLX archives for compliance with specification and code
Review the GLX specification for issues, contradictions, and ambiguities
| name | check-schema-drift |
| description | Identify drift between specification markdown files and JSON schemas |
| allowed-tools | ["Read","Grep","Glob","Bash(git rev-parse:*)","Bash(date -u:*)","Bash(make check-schemas:*)","Bash(gh issue list:*)"] |
| model | claude-opus-4-8 |
You are tasked with identifying any drift between the GLX specification markdown files and the JSON schemas.
Before analyzing, read .claude/drift-allowlist.yaml (validated by .claude/drift-allowlist.schema.json) — the single source of truth for known, triaged drift, shared with the check-code-drift skill. If a finding concerns the same file and symbol as an entry (match on symbol identity, not exact string), suppress it: permanent: true is by-design (not drift); an entry with a tracking_issue is a temporary deferral (refer to the issue rather than re-reporting). Everything not in the allowlist is reported normally. The allowlist holds only per-symbol exceptions — not class-level methodology — and is human-curated, so don't invent entries.
IMPORTANT: The source of truth hierarchy is:
Specification (*.md) → Schema (*.schema.json) → Go Code (types.go)
SOURCE OF TRUTH DERIVED FROM SPEC DERIVED FROM SCHEMA
This means:
Schema dialect: the schemas are JSON Schema 2020-12 (#794). Subschemas live under $defs (not draft-07 definitions), and property co-requirements use dependentRequired (not dependencies). Do not suggest draft-07 idioms when proposing schema fixes.
Analyze all entity types and compare:
Do not use a static list of schema names. Instead, enumerate at scan time:
specification/schema/v1/*.schema.json, then exclude glx-file.schema.json and any file that does not end in .schema.json. Each remaining file is an entity schema.specification/schema/v1/glx-file.schema.json is treated separately as the archive root.specification/schema/v1/vocabularies/*.schema.json. Classify each by naming convention:
-properties.schema.json → property vocabularyperson.schema.json ↔ specification/4-entity-types/person.md; event-types.schema.json ↔ specification/5-standard-vocabularies/event-types.glx; etc.This approach automatically covers schemas added in the future and eliminates the stale-list problem documented in issue #986 (where study, research-log, and five type vocabularies were silently never checked because they were missing from a static list).
Always use the glob patterns above rather than bare directory listing. If you do list the directory, exclude these explicitly — they are not subject to drift analysis:
specification/schema/v1/embed.go — Go embed file for binary distributionspecification/5-standard-vocabularies/embed.goREADME.md in a schema directory.schema.jsonComparing embed.go or a README against a non-existent spec section produces pure false positives. Do not flag them.
For glx-file.schema.json, compare against specification/3-archive-organization.md. Verify:
metadata object fields match spec^[a-zA-Z0-9-]{1,64}$) matches specCompare specification/4-entity-types/vocabularies.md and specification/5-standard-vocabularies/*.glx against specification/schema/v1/vocabularies/*.schema.json. For each vocabulary schema, verify:
vocabularies.md (label, description, gedcom, fields, etc.)value_type/reference_type/vocabulary_type mutual exclusivity per spec.glx template files conform to their corresponding schema. This is now deterministic: make check-schemas runs validate-schemas.mjs Step 4 (#839), which validates every specification/5-standard-vocabularies/*.glx against its vocabularies/<stem>.schema.json (with $refs resolved against the Step-3 AJV instance). So a non-conforming .glx is tool-backed, not LLM-simulated: run make check-schemas, and on a non-zero exit emit each reported error as a category: vocabulary finding with validator_caught: true, llm_only: false. Reserve llm_only: true for the semantic checks the validator cannot make (e.g. a schema's structure not matching the format documented in vocabularies.md)For each entity type, verify:
Compare fields documented in the spec markdown against properties in the JSON schema. Do this in both directions: spec→schema (missing in schema) and schema→spec (undocumented in spec).
When a schema has additionalProperties: false (all top-level entity schemas and the archive root), drift direction determines severity — see the Severity Rubric section below.
EXCLUDE rows whose field name reads "Entity ID (map key)" — that row documents the parent-map keying constraint (the entity is keyed by its ID in the parent map, enforced via patternProperties in glx-file.schema.json), not a property on the entity itself. The entity schema's required array must not contain it, so flagging a missing "Entity ID (map key)" is a pure false positive on every entity. Do not flag it.
Category: field_presence
Compare "Required Fields" tables in markdown with required arrays in JSON schema. A field being required vs optional has different failure modes in each direction.
Category: required_optional
Compare field types in markdown tables with JSON schema types. Check for type mismatches (e.g., markdown says "string", schema says "array").
Note: oneOf[string, array] in the schema where the spec says string | string[] is a known idiom and is not drift — treat as info.
Category: field_type
Use a two-tier rule — paraphrase scored as drift is the dominant false-positive source in description comparison.
If unsure which tier applies, treat it as wording-only.
IGNORE inline lists of vocabulary-defined values in description strings (e.g., a properties field description listing common property names like "locator, text_from_source, ..."). These are informational hints, not normative.
Category: field_type (constraint differences surfaced through descriptions)
Check for complex validation rules (patterns, minItems, anyOf, allOf, not). Verify these are documented in the markdown. Look for undocumented constraints in schemas.
Category: constraint
Verify entity ID pattern constraints match between docs and schemas. Check that ^[a-zA-Z0-9-]{1,64}$ is consistently applied where needed.
Category: entity_id_pattern
$ref ResolutionVerify all $ref values in schemas point to files that actually exist (e.g., "$ref": "person.schema.json", "$ref": "vocabularies/event-types.schema.json"). Check for orphaned schemas — schema files with no $ref pointing to them and no corresponding spec section.
Category: ref_type
Every entity type documented in the spec should have a corresponding schema file. Every schema file should have a corresponding spec section. Every vocabulary .glx file should have a corresponding vocabulary schema. Flag any mismatches in either direction.
Category: file_existence
These patterns are known to be complex and drift-prone:
allOf/anyOf/not constraints enforcing that property/value and participant are mutually exclusivevalue/date object pattern for properties that change over timecitations, sources, or media required on assertionsproperties field — additionalProperties: true on entity properties maps (intentional, not drift)Category: special_focus
Severity levels (critical | major | minor | info) are defined in the shared rubric at .claude/skills/check-suite/severity-rubric.md. Reference it — do not redefine the four levels here. The table below maps each finding category for this skill to its default severity, implementing the proposal in GitHub issue #838.
| Category | Condition | Severity |
|---|---|---|
field_presence | Spec documents a field; schema missing it; entity has additionalProperties: false | critical — glx validate rejects spec-valid archives |
field_presence | Spec documents a field; schema missing it; entity uses additionalProperties: true (e.g., a properties map) | info — vocab-defined, no data loss |
field_presence | Schema has a field; spec does not document it | major — functional but undocumented |
required_optional | Schema marks a field required; spec marks it optional | critical — glx validate rejects spec-valid archives |
required_optional | Schema marks a field optional; spec marks it required | major — spec contract violated, but glx validate permits it |
field_type | Type mismatch that affects validation (e.g., spec says string, schema says array) | critical |
field_type | Compatible widening — oneOf[string, array] in schema where spec says string | string[] | info — known idiom, not drift |
field_type | Schema description adds or removes a constraint vs. spec | major |
field_type | Pure rewording or added example, no constraint change | info — do NOT flag |
entity_id_pattern | Pattern in schema (^[a-zA-Z0-9-]{1,64}$) differs from spec | critical — affects identifier validity |
entity_id_pattern | Pattern constraint present in schema, no spec mention | major — undocumented constraint |
ref_type | $ref points to a non-existent file | critical — schema compilation fails |
ref_type | Orphaned schema file: no $ref points to it, no spec section | major — confirm intentional |
constraint | allOf/anyOf/not in schema; not documented in spec | major |
constraint | Spec documents a constraint; schema does not enforce it | critical — silent data acceptance |
constraint | Assertion mutual exclusivity (property/value ↔ participant) missing or mis-shaped | critical — semantic data corruption |
constraint | Evidence requirement on assertion (anyOf [citations, sources, media]) missing | critical |
constraint | Entity properties map uses additionalProperties: true | info — intentional idiom |
constraint | Entity object uses additionalProperties: true where spec implies false | major |
special_focus | Participant object inconsistent across Event/Relationship/Assertion schemas | critical — cross-entity inconsistency |
special_focus | Temporal property structure (value/date object) missing or mismatched | major |
file_existence | Spec-documented entity has no schema file | critical |
file_existence | Schema file has no corresponding spec section | major |
file_existence | Vocabulary .glx template fails validation against its corresponding schema | critical — shipped example archives would fail validation |
file_existence | Vocabulary schema file has no corresponding .glx template | minor |
vocabulary | Vocabulary schema structure deviates from the format in vocabularies.md | major |
vocabulary | Property vocabulary schema does not enforce value_type/reference_type/vocabulary_type mutual exclusivity | critical |
When uncertain, assign info. A false critical costs more reviewer trust than a missed minor.
validator_caught / llm_only AssignmentThese two fields are required on every finding and must be logical inverses of each other.
validator_caught: true, llm_only: false — the finding is already surfaced by a deterministic tool: make check-schemas (validate-schemas.mjs), glx validate, or a CI check. Broken $ref values and schema-invalidity-against-meta-schema fall here.validator_caught: false, llm_only: true — the finding comes from semantic comparison of spec prose vs. schema structure. All spec↔schema field/description/constraint comparisons fall here.Wire this to the provenance make check-schemas run: if that run exits non-zero, any finding attributable to a broken $ref or invalid schema compilation should be marked validator_caught: true.
Before the findings, record a provenance header at the top of the report so a run is reproducible and silent coverage gaps are detectable:
git rev-parse HEADdate -u +%Y-%m-%dT%H:%M:%SZ*.schema.json paths you compared (derived from the glob at scan time, not a memorized list). Listing what you visited versus what you were told to visit catches the case where a file was silently dropped.make check-schemas exit status — run it and record the exit code. This target (node specification/validate-schemas.mjs) validates every entity and vocabulary *.schema.json against the JSON Schema meta-schema, compiles each under ajv strict mode, compiles glx-file.schema.json with all entity/vocabulary schemas registered as $ref targets (so a broken cross-schema reference fails here), and (Step 4, #839) validates every specification/5-standard-vocabularies/*.glx template file against its vocabularies/<stem>.schema.json. A non-zero status means schema-level or vocabulary-template validity is broken; note this prominently and treat findings with lower confidence.The commit SHA and schema file list also populate the commit and checked_files fields of the machine-readable block below.
Produce the report in two parts, in this order: (1) a human-readable prose report grouped by scope, then (2) a single machine-readable findings-json block.
Group findings using the exact headings below, so every finding is addressable by scope and target. Emit one section per schema you actually visited (derived from the glob).
| Scope | Heading to use |
|---|---|
| Entity schemas | ## Entity: [name] |
| Archive root | ## Archive Root: glx-file |
| Type vocabularies | ## Vocabulary (type): [name] |
| Property vocabularies | ## Vocabulary (property): [name] |
Under each heading, emit either a no-drift line or a drift block:
## Entity: [name]
No drift detected — schema matches specification.
OR
Drift detected — schema needs updates:
### Field Presence
- Schema is missing field `field_name` documented in specification (spec→schema).
- Schema has field `field_name` not documented in specification (schema→spec).
### Required vs Optional
- Schema marks `field_name` as required; specification marks it optional.
### Field Types
- Schema has `field_name` typed as X; specification documents it as type Y.
### Constraints
- Specification documents constraint X but schema does not enforce it (spec→schema).
- Schema enforces undocumented constraint X (schema→spec).
Frame all drift as "what the schema needs to change" to match the specification.
After the prose, append exactly one fenced code block with the info-string findings-json so the eval harness (#796) can grep for it deterministically. The block MUST be valid JSON — no comments, no trailing commas. Always emit it, even on a fully clean run ("findings": []).
The block MUST conform to .claude/skills/check-suite/findings.schema.json. The schema uses additionalProperties: false at both the top level and per finding, so any unrecognized field is a validation failure.
Top-level fields:
command — always "check-schema-drift".commit — HEAD SHA from provenance (7–40 hex chars).checked_files — flat array of repo-relative paths actually examined this run: all .schema.json paths opened plus the spec .md files and any .glx templates you read. Derived from the glob, not a memorized list.findings — array of finding objects (empty array on a clean run).positive_notes — optional array of strings for things verified as correctly aligned. Route no-drift confirmations here rather than as info findings.summary — critical, major, minor, info, and suppressed_as_duplicate_of_known_issue (integer counts; all five are required by the shared schema).Per-finding required fields — these are the only fields the schema accepts on a finding:
file — repo-relative path of the drifting artifact (the .schema.json).line — 1-based line in file, or null when not line-addressable.severity — one of critical | major | minor | info per the rubric table above.category — one of: field_presence | required_optional | field_type | entity_id_pattern | ref_type | constraint | special_focus | file_existence | vocabulary. Must match the category used to look up severity in the rubric table.message — one sentence framed as "what the schema needs to change." Include drift direction in the message text (e.g., "Spec documents field X; schema is missing it" vs. "Schema has field X not documented in spec").fix — optional concrete remediation.validator_caught — true if make check-schemas or another deterministic tool already flags this; false otherwise.llm_only — logical inverse of validator_caught.Note: the shared schema has no scope, target, drift_direction, spec_path, or json_pointer fields. Convey that information in message and fix instead.
Example (illustrative — populate from actual glob and analysis; emit "findings": [] on a clean run):
{
"command": "check-schema-drift",
"commit": "8709e09",
"checked_files": [
"specification/schema/v1/person.schema.json",
"specification/4-entity-types/person.md",
"specification/schema/v1/citation.schema.json",
"specification/4-entity-types/citation.md"
],
"findings": [
{
"file": "specification/schema/v1/person.schema.json",
"line": null,
"severity": "critical",
"category": "field_presence",
"message": "Spec documents field `aliases` on person (spec→schema); schema is missing it and sets additionalProperties: false, so glx validate rejects archives using this field.",
"fix": "Add `aliases` to the properties object in person.schema.json.",
"validator_caught": false,
"llm_only": true
}
],
"positive_notes": [
"citation.schema.json: all required and optional fields match specification."
],
"summary": {
"critical": 1,
"major": 0,
"minor": 0,
"info": 0,
"suppressed_as_duplicate_of_known_issue": 0
}
}
check-code-drift skill for downstream impact.check-spec skill instead.check-spec skill should be redirected here.Two layers suppress already-known drift; apply both before finalizing:
.claude/drift-allowlist.yaml for known, triaged drift on a specific file + symbol. Those are suppressed there and need no further action here.gh issue list -R genealogix/glx --state open --limit 100 --json number,title --jq '.[] | "#\(.number) \(.title)"'.suppressed_as_duplicate_of_known_issue in the findings-json summary (do not also add it to findings[]).findings[].This keeps the report focused on newly discovered drift. As .claude/drift-allowlist.yaml accumulates entries (#797), more drift is caught by layer 1, reducing the gh issue list scans in layer 2.
At the end of the prose report, provide:
These counts must agree with the summary block in the findings-json output.
anyOf constraint, document this clearlyproperties fields on entities use additionalProperties: true intentionally — this is not drift