Bounded-Context Scheme Emission
Implementation status — 2026-05-30: EXECUTED (S022-final design; green).
emitters/contexts.py→opda-contexts.ttlemitsopda:BoundedContextScheme+ the six industryskos:Concepts (eachskos:inScheme+skos:topConceptOf+skos:prefLabel+skos:definition+opda:hasSteward+dct:source) + theopda:consumesFromannotation property (rdfs:subPropertyOf prov:wasInfluencedBy). Per the S022-final design, noopda:servesContext/opda:overlaysContext/opda:definedInContextpredicate is emitted, and no dormant CONSTRUCT/cross-check shape ships — membership is a documented on-demand query held as a reviewable constant incontexts.py(never materialised). Foundationowl:versionInfobumped; wired into theemitumbrella + byte-identity CI + anemit-contextsCLI command. F1 firewall + “no retired predicates” structural tests green. Theprofiles.py:250defect is moot (the predicate is gone).
Context and Problem Statement
ODR-0020 ratified the bounded-context model: one flat opda:BoundedContextScheme of the six industry contexts; upstream authorities as opda:Organisation reached by opda:consumesFrom; spanning concerns derived, not declared; and a four-bucket term→context mapping derived from the profiles. None of it is emitted. This ADR is the implementation — how the generator (opda-gen, ADR-0007 / ADR-0008) produces it deterministically.
Three facts make this more than “emit some triples”:
- No emitter exists. The six contexts, the scheme, and the predicates (
opda:servesContext,opda:consumesFrom,opda:definedInContext) are absent from every TTL. opda:overlaysContextis mis-targeted.tools/opda-gen/src/opda_gen/emitters/profiles.py:250hardcodes the profile-LAYER IRI<https://w3id.org/opda/profiles/foundation>as the object ofopda:overlaysContext. The ODR-0020 derivation readsoverlaysContextto discover a term’s context — so a profile-layer object yields nothing mappable. This is a bug fix and the precondition for derivation.- Only one profile is emitted.
baspi5.ttlis the sole profile insource/03-standards/ontology/profiles/; the other forms (ta6/7/10, lpe1, fme1, piq, rds, oc1, llc1, con29…) are not yet generated. The full term→context map therefore cannot be complete until those profile emitters land; this ADR delivers the scheme, the corrected wiring for the profiles that exist, and the (dormant) derivation, and records the remaining profiles as downstream work.
The emission must satisfy the generator-first determinism + byte-identity CI of ADR-0007 §6a and must ship the derivation dormant per ODR-0019 Rule 8.
Decision Drivers
- Realise ODR-0020 deterministically — the scheme + concepts are generator output, never hand-edited (ADR-0007 §6a).
- Single source of truth = the profiles —
opda:servesContextis derived, not authored (ODR-0020 Rule 5). - Dormant gate — the derivation rule ships off until a named term-grain consumer (ODR-0019 Rule 8).
- No regression — existing 23 TTLs stay byte-identical except the intended
baspi5.ttloverlaysContext fix and the foundation version bump. - House style — mirror
emitters/vocabularies.py(ADR-0010): a dedicated module emitting a dedicated TTL via the canonical serialiser.
Considered Options
- Option A — new
emitters/contexts.py→opda-contexts.ttl(CHOSEN). A dedicated module emits the scheme + the six concepts + the three annotation-property declarations, besidevocabularies.py;profiles.pygains aCONTEXT_OFoverlay→context map and re-pointsoverlaysContext; the dormant SHACL-AF CONSTRUCT lands inemitters/shapes.py. - Option B — fold contexts into
vocabularies.py/opda-vocabularies.ttl. Rejected: bounded contexts are not value vocabularies (they carry the DDD context map, not enum members); per-module file discipline (ADR-0011) keeps concerns in separate artefacts. - Option C — hand-author
opda-contexts.ttl. Rejected: violates generator-first (ADR-0007); theDO NOT HAND-EDITheaders and byte-identity CI forbid it.
Decision Outcome
Chosen option: Option A, because it mirrors the ratified vocabularies.py substrate pattern, keeps the context scheme in its own deterministically-regenerated artefact, and isolates the profiles.py bug fix from the new emission. Work items:
emitters/contexts.py→opda-contexts.ttl. Emitopda:BoundedContextScheme a skos:ConceptScheme+ the sixskos:Concepts (opda:EstateAgencyContext,opda:ConveyancingContext,opda:MortgageLendingContext,opda:SurveyingContext,opda:PropertyDataServicesContext,opda:PropertyTechnologyContext), eachskos:inScheme+skos:topConceptOf+skos:prefLabel+skos:definition+opda:hasSteward(Literal, from the/modelling/bounded-contextstable). Wire the new module underowl:importsfrom the foundation per ADR-0011.- Declare the predicates.
opda:servesContext,opda:consumesFrom,opda:definedInContextasowl:AnnotationProperty(membership/provenance, not logical typing) with A9 metadata, inopda-contexts.ttl(or the annotations graph per three-graph separation). - Fix
profiles.py:250. AddCONTEXT_OF = {"baspi5": OPDA.EstateAgencyContext, "ta6": OPDA.ConveyancingContext, …}(all overlays) and emitvctx opda:overlaysContext CONTEXT_OF[overlay_id]; retain the profile-layer link under a distinctopda:profileLayerpredicate if still required by ODR-0010. For now this correctsbaspi5.ttl(→opda:EstateAgencyContext); the map is ready for the other profiles as they are emitted. - Dormant derivation rule. Author the SHACL-AF
CONSTRUCT { ?term opda:servesContext ?ctx } WHERE { ?vc opda:overlaysContext ?ctx ; opda:requires ?term . FILTER(STRSTARTS(STR(?term), STR(opda:))) }inemitters/shapes.py, emitted toopda-shapes.ttlbut excluded from the active validation set (dormant) per ODR-0017 + ODR-0019 Rule 8. - Regenerate + version. Bump
foundation.ttlowl:versionInfofor the new emitted artefact (the scheme rides the foundationowl:versionIRI— no per-scheme version); regenerate; confirm byte-identity on second run.
Consequences
- Good, because the bounded-context scheme becomes machine-readable and regenerable, closing the gap ODR-0019/0020 opened; the
/modelling/bounded-contextsprose finally has a TTL counterpart. - Good, because the
opda:overlaysContextbug fix makes the ODR-0020 derivation possible at all — before this, the only context edge in the graph pointed at a layer, not a context. - Good, because membership stays a derived view (no hand-authored
servesContext), so it cannot drift from the profiles. - Bad, because the term→context map is incomplete until the five unwritten form-profile emitters (ta6/7/10, lpe1, fme1, piq, rds, oc1, llc1, con29…) land — this ADR delivers the scheme + the wiring + one corrected profile, not the full mapping. The remaining profile emitters are downstream work (their own ADR or the descriptive-layer backlog).
- Bad, because a new emitted artefact + foundation version bump touches the byte-identity baseline; the CI baseline must be re-pinned in the same commit.
- Neutral, because upstream authorities and spanning concerns need no new emission (they reuse
opda:Organisation/prov:Agentand ODR-0006/0007); onlyopda:consumesFromis newly declared. - Neutral, because the derivation ships dormant — no behaviour change for current consumers until the Rule 8 gate is opened.
Confirmation
- Byte-identity CI (ADR-0007 §6a): second regeneration produces identical bytes; baseline re-pinned.
- Structural tests (
tools/opda-gen/tests/):opda-contexts.ttldeclares exactly oneskos:ConceptSchemeand sixskos:Concepts, eachskos:topConceptOfthe scheme; the three annotation properties are declared; no domain term carriesskos:inScheme opda:BoundedContextScheme(the ODR-0020 Rule 5 firewall). - Bug-fix test:
baspi5.ttlemitsopda:overlaysContext opda:EstateAgencyContext(not theprofiles/foundationlayer IRI). - Dormancy test: the SHACL-AF CONSTRUCT parses and is present in
opda-shapes.ttlbut is excluded from the active validation profile (fires no result on the 15 exemplars). odr-review/ CI guard: no upstream authority is typedskos:Concept; no hand-authoredopda:servesContexttriple exists in source TTL.
Amendment — Council Session 021 (2026-05-30; SUPERSEDED by the Session 022 amendment below)
Session 021 (9-voice Full Council; 9–0 hybrid verdict) revises this ADR’s authority model and scope. The governance directive (one-go delivery, full coverage, no gates) places these changes in the same delivery as the original five work-items — ADR-0028 and ADR-0029 are executed alongside, not sequenced behind.
- Authority inversion (work-item 2 reframed).
opda:definedInContextis the authoritative home predicate, generated from each term’sdct:sourceprovenance (data-dictionary originating-form), default descriptive → foundation/shared-kernel, residue via ODR-0008 §Q1a’s register — not hand-authored, not homonym-gated (per the ODR-0019 Rule 8 S021 carve-out).opda:servesContextis derived/advisory (the existing CONSTRUCT, work-item 4). Each of the three predicate declarations additionally carriesrdfs:isDefinedBy→ ODR-0019/0020 +dct:source. - Cross-check shape (new, dormant). Author in
emitters/shapes.py→opda-shapes.ttlash:WarningSHACL-AF shape that flags any term whose derivedopda:servesContextset has no authoredopda:definedInContextbacking (a profile requires a term in a context the ontology has not claimed — a placement-review flag, never a redefinition).implements: ODR-0017; severity ∈ {Info, Warning}; shipped excluded from the active set. - Total-cover CI + firewall guards (new — ODR-0020 Rule 5 S021). Add F2 (no
opda:Organisationtyped a contextskos:Concept), F3 (no hand-authoredopda:servesContextin source TTL), and the total-cover assertion (every owned domain term hasdefinedInContextORconsumesFromOR is on the D2 scaffolding allow-list) to §Confirmation, alongside the existing F1 firewall test. - House-style deltas pinned. The context scheme’s emission test pins: (i) every concept carries
skos:topConceptOf(flat scheme — already work-item 1); (ii) the scheme is kept out of the ODR-0011 §8aufoCategoryvalue-vocabulary lint (it is a perspectival facet, none of the seven value categories — governed by ODR-0019/0020); fire the §8a eighth-category re-open trigger only if tooling later demands an explicit triple. - Scope under the one-go directive. The “incomplete until the unwritten form-profile emitters land” limitation (Consequences, 4th bullet) no longer applies as a staged limitation: ADR-0029 emits all profiles and ADR-0028 the full 935-leaf descriptive walk + the generated home-pass in the same delivery. The CI baseline is re-pinned once for the complete delivery. No ADR-0005 §G “deferred remainder” row is created — nothing is deferred. (
opda:servesContextCONSTRUCT activation vs the active validation set remains governed by ODR-0019 Rule 8; delivering it active is a one-line ODR-0019 amendment folded into the same delivery if the WG lifts dormancy.)
Amendment — Council Session 022 (2026-05-30; SUPERSEDES the Session 021 amendment above; council-ratified — greenfield, no WG)
Session 022 (Queen Baker; DA Davis; 6–0) reviewed this ADR against published convention and reversed the bespoke parts of the S021 amendment. The emission shrinks:
opda:definedInContextis NOT declared or emitted — retired. It reinvents three published standards. Home =rdfs:isDefinedBy→ owning module (concern; emit mechanically) +dct:source(provenance; already emitted) +dct:subject→ context concept (community-ownership; authored-or-absent, gated, none today). The S021 “authority inversion / generated home” item is withdrawn.- No cross-check shape, no total-cover CI, no F2/F3 — they policed a stored
servesContext+ an authored home that S022 deletes. F1 firewall retained. opda:servesContext= a dormant SHACL-AF rule, NEVER materialised (run on demand; activates only on a named term-grain consumer, ODR-0019 Rule 8).opda:overlaysContextis DROPPED (not re-pointed, not replaced) — the work-item 3CONTEXT_OF→…Contextplan is withdrawn. Per governance directive (2026-05-30 — “the SHACL overlay IS the form; no profile-object/PROF layer”): the form↔base link is structural (the shapes’sh:targetClasson theopda:base — ODR-0010); the form↔community link is one standard triple on the form graph (dct:subject/dct:publisher→ its context concept). Noprof:Profile, noprof:isProfileOf, no spike.opda:ValidationContextstays exactly as ODR-0010 defines it (not re-typed, nothing layered on). This moots theprofiles.py:250bug.opda:requiresdropped (redundant — the shapes’sh:path/sh:minCountset already enumerates it).- KEEP (S022 6–0): the
emitters/contexts.pySKOS scheme + 6 concepts (work-item 1),opda:consumesFrom, per-shapedct:source. A form is a DCAP; the constraint table a DCTAP;profiles.pyalready runs the TAP→SHACL step.
Work-item 1 (scheme) stands. Work-items 2–4 are revised per the above; the byte-identity discipline (5) stands. See [session-022 §Disposition].
More Information
- Realises: ODR-0020 — Bounded-Context Scheme and Mapping (the decision); refines ODR-0019 (the pattern + Rule 8 gate).
- Generator framework: ADR-0007 (deterministic emission), ADR-0008 (infrastructure —
implements), ADR-0010 (the SKOS substrate this mirrors), ADR-0011 (module emission +owl:importswiring), ADR-0013 (the profile emitter being corrected). - Files touched:
tools/opda-gen/src/opda_gen/emitters/contexts.py(new);…/emitters/profiles.py:250(overlaysContext fix +CONTEXT_OF);…/emitters/shapes.py(dormant CONSTRUCT);source/03-standards/ontology/opda-contexts.ttl(new, generated);…/profiles/baspi5.ttl(regenerated);…/foundation.ttl(versionInfo). - Council provenance: ODR session-020.
- Implementation-planning follow-on: ODR session-021 — Bounded-Context Implementation Plan (2026-05-30). Proposes revising this ADR to: declare
opda:definedInContextauthoritative (generated-from-provenance) andopda:servesContextderived/advisory; add a dormant cross-check shape (sh:Warningwhen a derivedservesContexthas no authoreddefinedInContextbacking); add a total-cover CI assertion (every domain term hasdefinedInContextORconsumesFromOR is on the scaffolding allow-list); pin two house-style deltas (skos:topConceptOfon every concept; keep the context scheme out of the ODR-0011 §8aufoCategoryvalue-lint). The two downstream builds below are promoted to ADR-0028 (descriptive walk + generated home-pass) and ADR-0029 (ProfileSpecrefactor + 14-profile rollout). The revision stands on directing authority (greenfield — no WG gate; ADR-0006). - Downstream: the unwritten form-profile emitters (ta6/7/10, lpe1, fme1, piq, rds, oc1, llc1, con29) complete the term→context map as each lands — tracked by ADR-0029 per session-021.
Comments
Loading comments…
Sign in to post a comment