Schema Developer FAQs
Some frequently asked questions about developing the NMDC Schema.
What are some effective strategies for collaborative schema development?
LinkML Collaborative Development Google Slides Presentation
How do I migrate from one version of the NMDC schema to another?
How do I view the NMDC schema programmatically?
NMDC SchemaView Documentation SchemaView Documentation
Where is the OWL generation documentation?
OWL Generation -- covers current build process, recommended config, CLI/YAML flag mapping, portal submission guidance, and upcoming LinkML changes.
Where should I put hand-written documentation?
The docs/ directory is a build output directory — make clean deletes
docs/*.md and docs/*.html. Tracked files in docs/ that have no
counterpart in src/docs/ will trigger a warning during make clean, but
untracked files will be silently removed. Place all hand-written documentation
in src/docs/ instead; the build copies it into docs/ automatically.
How do I make sure a directory exists?
Which mechanism to use depends on whether the directory has to be there before the build runs.
.gitkeep for directories that must be present in a fresh clone. Git does
not track empty directories, so a tracked .gitkeep is what makes them
survive a clone. The repo has 8, marking the example-data and asset
directories that targets read from or write into:
src/data/valid/,src/data/invalid/src/data/problem/and itsvalid/andinvalid/subdirectoriesassets/jsons-for-mongodb/,assets/misc/local/
mkdir -p in the recipe for anything the build creates. Do not add a
.gitkeep for a build output directory. The Makefiles do this in 11 places:
Makefile before writing $(DEST)/prefixmap, $(DEST)/pydantic, local,
and the $(DOCDIR) subdirectories; project.Makefile with the $@ and
$(@D) forms, so a target creates its own output directory; and
makefiles/mixs.Makefile for local/mixs_regen.
local/ is worth understanding because it looks contradictory. .gitignore
lists local/, yet local/.gitkeep is tracked, because an ignore rule has no
effect on a file that is already tracked. The result is what you want: a fresh
clone gets an empty local/ directory, and nothing written into it afterward
is ever staged. Confirm with git archive origin/main | tar -t | grep '^local/'.
How do I configure environment variables for development scripts?
Some scripts need API keys or database credentials. Scripts that need them
load from local/.env via python-dotenv. To get started:
# IMPORTANT: Do NOT overwrite an existing local/.env — it may contain
# real credentials that are not recoverable from this repo.
if [ ! -f local/.env ]; then
cp .env.example local/.env
else
echo "local/.env already exists — not overwriting."
echo "Compare with .env.example to see if you need to add new variables:"
echo " diff <(sed 's/=.*//' .env.example) <(sed 's/=.*//' local/.env)"
fi
local/ is gitignored. .env.example in the repo root documents every
variable, which script uses it, and where to get credentials. You only need
to fill in the variables for the scripts you actually run.
The Docker-based migrator workflow has its own separate env file at
nmdc_schema/migrators/.docker/.env.example — see the
migrators README
for details.
What are LinkML readonly metaslots and why shouldn't I assert them?
The LinkML metamodel defines 12 readonly slots that are automatically populated by the schema loader or generators. They belong to two different metamodel element types, which matters when writing tooling that filters on them.
Seven are on SlotDefinition, so they can appear on a slot or in a slot_usage
block: definition_uri, domain_of, from_schema, imported_from,
is_usage_slot, owner, usage_slot_name.
Five are on SchemaDefinition and describe the file a schema was read from. They
never appear on a slot, even after induction: generation_date, metamodel_version,
source_file, source_file_date, source_file_size.
Do not add these to hand-edited schema YAML files under src/schema/. The
loader fills them in at runtime, so asserting them is redundant and can cause
confusion when values drift from what the loader would compute.
For generated files like src/schema/mixs.yaml, the MIxS build pipeline
(makefiles/mixs.Makefile) strips all 12 readonly slots via yq eval as part
of the dematerialization step
(PR #2696).
What architectural changes were made between Oct 2025 and Feb 2026?
| Date | PR | Change |
|---|---|---|
| 2026-02-26 | #2848 | Makefile reorganization: MIxS pipeline moved to makefiles/mixs.Makefile, migrator targets to makefiles/migrators.Makefile. RDF conversion tooling removed. |
| 2026-02-26 | #2849 | LinkML 1.10.0 upgrade: new OWL flags (--enum-inherits-as-subclass-of), Python 3.9 dropped. |
| 2026-02-26 | #2846 | Dead code removal: about.yaml and experimental scripts removed. |
| 2026-02-21 | #2302 | Downloads page: schema-derived JSON and YAML files downloadable via docs website. |
| 2026-02-20 | #2839 | Unified LinkML CLI: build uses linkml generate owl, linkml generate json-schema, etc. instead of legacy gen-owl, gen-json-schema. |
| 2025-10-28 | #2696 | Dematerialize mixs.yaml: all 12 readonly metaslots stripped from generated mixs.yaml (35% line reduction). |