ADR 0013: Distinguish the terms we own from the terms the services own
Status
Accepted
Context
CONTEXT.md is one flat glossary. Every term in it reads as equally binding,
and every place the code disagrees is filed under Known legacy names – a
list whose framing is that the disagreement is debt, tolerated until someone
gets to it.
For most of the glossary that framing is right. But it is wrong for a small set
of terms, and being wrong about those has produced the same review argument
repeatedly: whether a docstring may say site, whether service= may name a
collection, whether prose about NWIS is bound by a word chosen from the Water
Data API.
The two sets behave differently because their authority differs.
Terms like chunk, page, fan-out, plan, interruption, dialect and leaf appear nowhere in any USGS API’s vocabulary. They were invented here to describe machinery this package owns. Nothing external constrains them, so when the package spells one of them two ways – the resolution chain’s code said tier for what its founding records, ADRs 0009 and 0010, call a source – that is simply an inconsistency, and one that can be removed by deciding.
Terms like monitoring location and collection are different. The services name those things, and they do not agree with each other:
Concept |
NWIS |
WQP |
Water Data |
NGWMN |
|---|---|---|---|---|
a place where measurements are recorded |
|
|
|
the |
a named set of records |
|
|
|
|
No decision here makes those agree. A caller who has read the WQP
documentation looks for Station; one reading Water Data’s looks for
monitoring_location_id. An adapter that renamed either would be harder to
use, not easier, and the parameter names are public surface besides.
Treating both sets under one rule forces a choice between two bad options: abandon the glossary, and the shared modules lose the vocabulary that lets them be shared; or enforce it everywhere, and every adapter’s public surface drifts from the API it wraps.
Decision
The glossary holds two kinds of term, and they carry different obligations.
Core terms are ours. The package invented them and no service has a claim on them: everything under Retrieval, Failure and resumption, Configuration and Boundaries, plus Collection family and Metadata. One spelling, enforced everywhere it appears – prose, identifiers, tests. A second spelling of a core term is a defect, not a variation, and is fixed rather than recorded. This is what makes the lower-level modules shareable: transport, configuration and the OGC engine can be written once because the words they are written in answer to nothing outside this package.
Domain terms belong to the services. Monitoring location and collection name things the services define and spell differently. For these the glossary chooses one term for prose, so that documents about the package are internally consistent. It does not choose for the wire, and it does not choose for an adapter’s public surface: each adapter keeps its own service’s spelling in its parameters, and reproduces that service’s vocabulary faithfully where it appears in returned data.
An adapter is where the two meet. Its public surface speaks its service’s language; what it hands to the shared modules speaks the core’s. The translation is the adapter’s job, and a divergence at that boundary is the design working rather than debt.
Two rules follow:
A term the glossary does not define is not used in the glossary. A word that earns a place in
CONTEXT.md’s prose earns an entry. Naming a term only to say what an ADR calls it is a cross-reference, not a definition, and does not license using the word elsewhere.Only core misnamings are legacy. Known legacy names records a core term the code spells wrongly and cannot be renamed. A domain term at an adapter’s surface is not a legacy name; it is that adapter speaking its service’s language, and belongs with the term’s own entry.
Consequences
The recurring question – may this docstring say site? – has an answer that does not depend on who is reviewing. In
nwisit may, because that is the spelling its service and its parameters use. Intransportit may not, because nothing there is about NWIS.Enforcement splits. A core term can be checked mechanically, since one spelling is correct everywhere. A domain term cannot: the correct spelling depends on which adapter the prose is about, so it stays a review judgement.
Known legacy names becomes shorter and means something narrower. The entries it loses are not resolved; they move to the term they belong to, as part of its definition rather than a list of exceptions.
A glossary entry now carries an obligation to say which kind it is. That is a small cost per term and the reason the distinction is usable at all.
The package’s own inconsistencies in core vocabulary become defects with a deadline rather than curiosities. The resolution chain’s
tier-for-source identifiers are the standing example.
Compliance
CONTEXT.md marks each domain term as such and names the per-service
spellings in the entry itself, so a reader who needs to know whether a word is
negotiable can see it without asking.
The mechanical part is that the glossary must define what it uses:
tests/architecture_test.py asserts every ADR NNNN citation resolves, and
the same file is where a check that CONTEXT.md defines its own vocabulary
would go. None is proposed yet – a word-list check over prose has a poor
precision record in this repository, and the failure it would catch is one a
reader notices immediately.
Whether a given adapter docstring should say site or monitoring location remains a review judgement, and is meant to.
Notes
Prompted by review of ADR 0000: Record each explanation once, in the place that owns it, where a reviewer found
CONTEXT.md using tier in its own prose without defining it. The word was
already there before that record was written; recording each explanation once
made the gap visible rather than creating it.
The per-service spellings in the table above were read from the adapters on 2026-09-01.