Contributing¶
One source, everything else derived¶
modules/ is the source of truth. Every other artefact in the repository is produced from
it, and the build fails if a committed artefact disagrees with what the schemas say.
| what | where | who writes it |
|---|---|---|
| schemas, module metadata | modules/<module>/ |
you |
| prose about a schema | docs/modules/<module>/.../index.md |
you, in a seeded page |
| site pages and assets | docs/*.md, docs/stylesheets, docs/javascripts |
you |
| RDF readings, instances, labels | generated/ |
make generate |
| SSSOM mapping sets | generated/mappings/ |
make generate, not committed |
| schema page tree and nav | docs/modules/, zensical.toml nav region |
make generate |
| schemas at their published paths | docs/<module>/<version>/ |
make generate, not committed |
| mapping sets at their published paths | docs/mappings/**.sssom.tsv |
make generate, not committed |
Run make generate after touching a schema, and make check before opening a pull
request. make check is what CI runs.
Adding a schema¶
- Write
modules/<module>/<Name>.schema.json. To extend an existing schema, reference it fromallOf; the first$refdecides where the schema sits in the documentation tree. - Run
make generate. This seedsdocs/modules/<module>/.../<name>/index.md, adds it to the navigation, regenerates the RDF readings and rebuilds the mapping sets. - Write the prose. The seeded page carries only what is derivable, rendered by macros.
Put explanation under a
## How it worksheading: what the schema decides, what it deliberately leaves out, and why a mapping is a close match rather than an exact one. - Run
make validate. A schema that does not round-trip through RDF is not finished.
An instance file starts with $schema. Editors read it to offer validation while the file is
being edited, and some stop looking after the first key, so the order is not cosmetic.
make check enforces it.
Pages are seeded once and never overwritten. If a page stops documenting the schema its
location claims, or documents a schema that no longer exists, make check says so rather
than silently rewriting your prose. A page that has to move because its schema gained a
parent is moved with its content intact.
Mappings¶
A mapping is authored next to the term it maps, never in a separate file:
- the consensus reading of a term goes in
@context; - every community alternative goes in
x-oold-context, tagged with itsmapping_set_idand the exact ontology version it was checked against.
A value alias is a term whose values are vocabulary entries rather than free text, coerced
with @vocab: unit is one, and SEC or PERCENT are its members. List them as an enum
of the vocabulary's own local names, and give each one a name in x-enum-varnames, in the
same order:
"unit": {
"enum": ["SEC", "MilliSEC", "HR"],
"x-enum-varnames": ["second", "milli_second", "hour"]
}
The names are what a generated binding uses, so they must be lower case ASCII identifiers
and unique within the enum. Where the value is already such a name (second, milli_metre),
it is the name and x-enum-varnames is not needed. make check enforces that. Write the
names out rather than deriving them: no standardised representation for arbitrary units is both canonical and ASCII. UCUM
is ASCII but not canonical, since L/(m2.s) and L.m-2.s-1 are the same unit; SI symbols
are canonical but not ASCII. For a composed unit, normalise the
siunitx spelling, so \liter\per\meter\squared\per\second
becomes liter_per_meter_squared_per_second.
The SSSOM sets in generated/mappings/ are built from those declarations. Do not edit
them; edit the schema and regenerate. Each set is published as SSSOM TSV at the identifier
its mapping_set_id names, so https://w3id.org/oo-ld/schemas/mappings/emmo.sssom.tsv
resolves to the set itself, and the version of the set is stamped inside the file rather
than in the identifier, so a release does not have to touch every schema.
Pin the ontology, not the promise of one: object_source_version may name a commit or a
snapshot where a published version IRI is stale. The files are not carried here; they are
pinned by hash in ontologies.lock.json. make ontologies fetches and verifies them, and
make labels rereads the human-readable labels for the terms the schemas map. A hash
mismatch means upstream moved under the pin, so the mappings that target it need re-checking
before the lock is updated; python scripts/fetch_ontologies.py --update refetches and
rewrites the hashes once you have decided the mappings still hold. See
Pinned ontologies for how a pin is added or moved.
Versions¶
Three number lines, each with a different job:
- Schemas carry
x-oold-version, their own semver. - Modules carry the version in
modules/<module>/module.json. A module is the unit that is published and conformed to, so its bump level is the highest level found among its schemas: a removed property or a changed consensus mapping is major, an added optional property or a new synonym is minor, a description is a patch. A removed schema is major, an added one minor. - The repository is released by date, because it aggregates modules that version independently and no single semver level could describe eleven of them at once.
make bump computes the versions rather than incrementing them: it reads the last release,
raises each schema by the level of what changed since, and raises the module by the highest
level among its schemas. Running it twice therefore lands on the same number, which is what
makes it safe in a pre-commit hook. make check verifies the same floors, so a bump that is
too small fails review rather than shipping.
make release stamps the dated release, records which module version it documents in
generated/versions.json, and regenerates. The schema pages read that index back, which is
how a reader finds the documentation for a module version that is no longer current.
Publishing checks both promises before it writes: scripts/check_publish.py refuses to
rewrite an exact release and refuses to move a compatibility line with anything above a patch,
so a forgotten bump fails the deploy rather than changing what someone already fetched.
Between releases, schemas may run ahead of their module. That state is published at
<module>/dev/, which is explicitly not a conformance target. A released version is
written once at <module>/<major.minor>/ and is never rewritten by anything above a patch,
because a conformance IRI that changes meaning is worse than no IRI at all.
Style¶
- No em dashes, no decorative comments, no emoji.
- Comments explain why, not what. If a workaround exists, name what it works around.
- Prose is honest about limits: a mapping that is only a close match says so, and says why.