Status: Draft specification, version 0.1 Scope: A token-minimal, machine-oriented text encoding of MORK graphs (../../ontology/mork/spec/Mork.ttl) with a deterministic decoding algorithm to OWL 2. Related: ADR-A18 (lowering boundary), ADR-A19 (staged compiler), ADR-A22 (governance and versioning), ADR-A25 (LLM participation), ADR-A26 (provenance chain).
MORK records mapping intent as first-class graph objects so that mappings can be reviewed, versioned, governed, compiled deterministically and audited back to evidence. The graph is the product. Its serialisation is not, and every standard serialisation of OWL — Turtle, RDF/XML, OWL/XML, functional syntax, Manchester syntax, JSON-LD — is expensive when a language model must generate it: long prefixed names are repeated on every triple, every individual is re-typed as owl:NamedIndividual, every mapping restates its scheme membership, and reified owl:Axiom annotations cost fifty tokens to say “this match is lexical”.
MCN is a notation in which an LLM (or any mapping agent) emits MORK content with the minimum number of tokens that still permits lossless, deterministic decoding to an OWL 2 ontology document. It is not designed to be read by people, though it is legible enough to debug. Its design rules are:
Mork.ttl plus the SKOS, SHACL-targeting and Foundation terms MORK data actually uses.^^xsd:… suffixes.{…} form replaces the owl:Axiom reification pattern and makes dangling annotations syntactically impossible (§16 records a real instance of that bug in the repository’s own examples).! lines carry arbitrary OWL axioms and raw triples, so completeness never depends on the codebook.mork:inputHash and the invalidation policy in ADR-A27).MCN is the proposal wire format in the sense of ADR-A25: an LLM emits MCN, a deterministic decoder produces the RDF graph, and everything downstream (OWL DL checking, SHACL validation with ontology/mork/shapes/constraints.ttl, compilation) operates on the graph exactly as it does today. No compiler, shape or reasoner needs to know MCN exists.
Two example files from the repository were hand-encoded in MCN and decoded with the reference decoder (tools/mork/src/mcn_decoder.py, codebook in mcn_codebook.py, tests in test_mcn_decoder.py); the decoded graphs were compared to the originals with an isomorphism-aware diff (rdflib graph_diff). Token counts use the o200k_base BPE vocabulary as a proxy for current frontier tokenizers.
| Document | Triples | MCN | Turtle (as written, comments stripped) | JSON-LD (compacted) | RDF/XML | N-Triples |
|---|---|---|---|---|---|---|
| ontology/mork/examples/Mork2RML/loan_mapping.ttl | 40 | 219 | 614 (2.8×) | 1418 (6.5×) | 1383 (6.3×) | 1881 (8.6×) |
| ontology/mork/examples/Zoo/UncertainMappings.ttl | 160 | 1366 | 2674 (2.0×) | 5308 (3.9×) | 5929 (4.3×) | 10235 (7.5×) |
The Zoo document is dominated by prose (mappingNote, mappingRecommendation, a 200-token skos:example), which no notation can shrink. Separating that irreducible literal content from structural overhead:
| Document | Literal prose | MCN structural | Turtle structural | Structural ratio | Structural tokens per triple (MCN / Turtle) |
|---|---|---|---|---|---|
| loan_mapping | 11 | 208 | 603 | 2.9× | 5.2 / 15.1 |
| UncertainMappings | 710 | 656 | 1964 | 3.0× | 4.1 / 12.3 |
Per construct, the savings are larger where Turtle is most verbose:
| Construct | MCN | Turtle | Ratio |
|---|---|---|---|
A Datum node with scheme, mappingFor, deferredMapping, conceptName, dataRef |
18 | 73 | 4.1× |
One annotated match (exactTBoxMatch + mappingNote on the axiom) |
9 | 54 | 6.0× |
| Prefix/base header of the loan example | 50 | 118 | 2.4× |
The loan example’s 219 tokens include 50 tokens of prefix IRIs; §6.1 describes a profile mechanism that removes those from documents exchanged inside a known deployment.
Both round-trips were exact: 40/40 triples for the loan example; 140 of 143 triples for the Zoo example (counting after removing the owl:NamedIndividual typing both sides assert), where the three differences are a defect in the original Turtle — three owl:annotatedTarget values are in the wrong namespace and so annotate nothing — not in the encoding.
An MCN document denotes an RDF graph. The graph, when it contains an owl:Ontology header (directive @o) and only OWL 2 DL-compatible constructs, is an OWL 2 ontology document under the OWL 2 RDF-based mapping and can be serialised in any OWL syntax by any conformant tool. Decoding is a total function from well-formed MCN text to a graph; ill-formed text is rejected with an error, never silently repaired.
The decoded graph consists of four kinds of contribution:
| Source | Contributes |
|---|---|
Directives (@) |
ontology header, imports, prefix bindings, base IRI, decoder options |
Block headers (%) |
one scheme individual (mork:MappingScheme etc.) plus a context applied to following node lines |
| Node lines | one named individual per line: typing, scheme membership, property assertions, annotated assertions, inline reified nodes, artefact payloads |
Expression lines (=) |
a mork:TemplateExpression tree |
Axiom lines (!) |
OWL declarations, class/property axioms, GCIs, disjointness axioms, raw triples |
A document is processed top to bottom in a single pass. Later lines may reference IRIs introduced by earlier or later lines; references are never checked for existence by the decoder (an IRI is an IRI). Repeated node lines for the same subject merge, exactly as repeated subjects merge in Turtle.
LF; CR LF is accepted.; (continuation; the ; is dropped and the remainder appended with one space).# is a comment and is ignored. Comments are for humans editing MCN by hand; generators SHOULD NOT emit them.The first character of a logical line determines its kind:
| First char | Kind | Section |
|---|---|---|
@ |
directive | §6 |
% |
block header | §7 |
! |
OWL axiom line | §11 |
| anything else | node line, or expression line if the second token is = |
§9, §10.1 |
Within a logical line, tokens are separated by one or more spaces or tabs. The following token classes exist:
| Class | Form | Notes |
|---|---|---|
| bare | run of characters containing no whitespace and none of , { } [ ] |
identifiers, codes, unquoted literals, CURIEs |
| quoted | "…" with escapes \" \\ \n \t \uXXXX |
optionally followed without a space by @lang or ^prefix:dt |
| iri | <…> |
absolute IRI |
| inline | [ … ] |
nested node, §9.6 |
| annotation | { … } |
attaches to the immediately preceding value, no space, §9.7 |
| list | value , value , … |
no spaces around commas, §9.5 |
Brackets and braces nest; a quoted string inside them may contain any bracket character.
An identifier in subject or object position resolves to an IRI by these rules, tried in order:
<…> → the IRI as written.. (table in §8.4) → its fixed IRI.: → the default target prefix declared by @t plus the local name. Error if no @t is in force.: → a CURIE against the prefix table (§6). Error if the prefix is unbound._: → a blank node with that label (only meaningful in !T lines).@b concatenated with the token. Error if no @b is in force.Local ids match [A-Za-z_][A-Za-z0-9_.\-]*. They are opaque: a decoder never interprets an id’s spelling. Generators SHOULD use short ids; ids need to be memorable to the generator (a model recalls Map_Loan more reliably than m7), so brevity is a recommendation, not a rule.
Predeclared prefixes (never need @p): mork, owl, rdf, rdfs, xsd, skos, sh, swrl, swrlb, rr, rml, fnd, dct — bound to the IRIs used in Mork.ttl and Executable.ttl (rml is http://semweb.mmlab.be/ns/rml#).
A value in a data-kind slot (§8) is a literal. Its lexical form and datatype are determined as follows:
| Written as | Result |
|---|---|
"text" |
xsd:string (plain literal) |
"text"@en |
language-tagged |
"text"^xsd:date |
typed with the given datatype (note the single ^) |
| bare token, code has a fixed datatype | that datatype (e.g. w 85 → "85"^^xsd:integer) |
| bare token, code’s datatype is inferred | t/f → xsd:boolean; -?[0-9]+ → xsd:integer; -?[0-9]+\.[0-9]+ → xsd:decimal; else xsd:string |
| bare token, code has no datatype | xsd:string |
So c #Species is the string "#Species", v t is boolean true, v "t" is the string "t", and ef 2026-10-01T00:00:00Z is a typed xsd:dateTime without any annotation. A bare token that must contain a space, comma, bracket or brace is written quoted.
Every property and class has a code. Codes are the entire reason MCN is cheap, so their design is constrained:
MD, RA, GT). The first letter names the family (M mapping, C concept, R representation, X ontology proxy, I intent, G generative support, E expression, S scheme).> broader, < narrower, ~ related, s= s~ s> s< s- for the SKOS mapping relations).f, xt, xr, cn, df, w, n, c, r, v) are one or two characters.xt exact T-Box, bt broad T-Box, nt narrow T-Box; lx lexical, sx semantic, tx structural) so a model can generate them reliably from a short system-prompt codebook.pb, pv, tl). Because kind is fixed, bare tokens are never ambiguous: c Loan is a string, xt Loan is an IRI.dct:created 2026-01-01, ex:myProp foo) is accepted everywhere a code is, as an escape for properties without codes. Its kind is object unless the value is quoted, so literals for CURIE properties are always written quoted (dct:created "2026-01-01"^xsd:date).The full codebook is in §8.
The decoder adds the following without them being written. Generators MUST NOT write them (doing so is legal but wasteful).
| Implicit fact | Rule |
|---|---|
rdf:type owl:NamedIndividual |
on every node-line subject, block header, inline node and minted expression node |
| Scheme membership | every node line inside a %M block gets mork:mappingScheme <scheme>; %T → mork:conceptScheme; %R → mork:representationScheme; %O → mork:ontologicalScheme; %I → mork:intentScheme; %K and %U → skos:inScheme |
| Default type | a node line with no type token gets the block’s default type (%M → mork:DataMapping, %T → mork:DataConcept, %R → mork:Representation, %O → mork:OwlAxiom, %I → mork:IntentNode, %K → sh:NodeShape, %U → swrl:Imp). In %X there is no default. |
| Implied generative types | a subject with gs becomes mork:ShapeMapping; gr → mork:RuleMapping; gt → mork:TransformMapping; gc → mork:ProjectionMapping. These mirror the owl:equivalentClass definitions in Mork.ttl; asserting them explicitly costs one token and is optional. |
| Inline node typing | a positional inline node (§9.6) gets the class its parent code implies (tg → TargetingSpec, pb → ParameterBinding, pv → the provenance class matching the subject’s generative type, hb → TemplateBinding) |
swrlCompactSyntax |
a gr payload string is also asserted verbatim as mork:swrlCompactSyntax on the mapping, as the ontology recommends |
| Datatypes | per code, §3.5 |
| Inverses | not materialised by default. The loan example in the repository documents why: emitting both directions of compositeNarrowerMapping/compositeBroaderMapping double-counts edges in the dependency analyser. @opt inv asks the decoder to add the inverse of every asserted edge whose property has a declared owl:inverseOf. |
What the decoder never adds: mappingScheme for nodes in %X, Datum typing from the presence of df (that is the reasoner’s job and generators should write MD when they mean it), or any GCI-derived consequence.
| Directive | Form | Effect |
|---|---|---|
@b |
@b <iri> |
Base IRI for local ids. The author includes the trailing # or /. May appear more than once; each occurrence applies to subsequent lines. |
@o |
@o <ontology-iri> [<version-iri>] |
Asserts <ontology-iri> a owl:Ontology (and owl:versionIRI). Required for the output to be an OWL ontology document; optional for a bare graph. |
@i |
@i <iri>,<iri>,… |
owl:imports on the ontology declared by @o. Error if no @o precedes it. |
@p |
@p prefix=<iri> |
Binds a prefix. Rebinding a predeclared prefix is an error. |
@t |
@t prefix |
Names the default target prefix: tokens of the form :Local resolve against it. Intended for the target ontology, which is referenced from almost every mapping. |
@u |
@u name |
Loads a named profile (§6.1). |
@opt |
@opt flag … |
Decoder options: inv (materialise inverses), strict (treat lint warnings in §14.2 as errors), bnode-shapes (emit SHACL property shapes as blank nodes instead of minted IRIs). |
@v |
@v 0.1 |
MCN version the document was written against. Optional; defaults to the decoder’s version. |
Directive lines are processed where they occur; a @b or @t can change mid-document, which allows one document to carry several schemes with different bases.
A profile is a named, out-of-band bundle of @b, @p, @t and @opt settings maintained by a deployment (for example one per target ontology). @u lattice-insure-o replaces the ~50 tokens of prefix IRIs that would otherwise head every document. Profiles are resolved by the decoder from a registry it is configured with; a document that uses @u is only decodable with that registry, and the decoder MUST record the profile’s content hash in the decoded graph as mork:inputHash on the ontology node (so decoded output remains reproducible). Profiles are RECOMMENDED for agent-to-decoder traffic inside a deployment and NOT RECOMMENDED for MCN that is stored or exchanged.
A block header opens a scheme and a context that lasts until the next block header or end of file.
%M id (code value)* MappingScheme members: mork:mappingScheme default type M
%T id (code value)* TaxonomyScheme members: mork:conceptScheme default type C
%R id [.fmt] (code value)* RepresentationScheme members: mork:representationScheme default type R
%O id (code value)* OntologicalScheme members: mork:ontologicalScheme default type X
%I id (code value)* IntentScheme members: mork:intentScheme default type I
%K id (code value)* ConstraintScheme members: skos:inScheme default type SH
%U id (code value)* RuleScheme members: skos:inScheme default type SW
%X no scheme, no membership, no default type
The header is itself a node line for the scheme individual: %M MS md .prod mv 1.2 declares MS as a MappingScheme in ProductionMode with version 1.2. In %R a reserved format token immediately after the id (.json, .xml, .yaml) is shorthand for fm.
A node that belongs to two schemes (a DataConcept that is also a Representation, as in the Rep_Issuer example in Mork.ttl) is written once inside one block with the second membership given explicitly (rs Rep_Scheme), or written as two node lines in two blocks; both produce the same graph.
%X is for content that has no scheme: shadow property declarations, external T-Box fragments, standalone template expressions, artefacts referenced from several mappings.
The tables in this section are normative and complete with respect to Mork.ttl at the commit this document was written against: every class, object property, datatype property, annotation property and named individual in the ontology has a code or a reserved token. The Notes column records defaults and inference behaviour.
Multiple types are joined with +: M+MD, C+RE. A CURIE whose local name begins with an uppercase letter may be used as a type (ex:Widget). A leading + forces type interpretation of any identifier regardless of spelling — a lowercase CURIE (+ex:widget) or a local id (+CompilationMode, needed when the base is the ontology’s own namespace).
Mapping
| Code | Class | Notes |
|---|---|---|
M |
mork:DataMapping |
default type inside %M |
MD |
mork:Datum |
|
MU |
mork:UncertainMapping |
|
MX |
mork:DeferredContext |
|
ML |
mork:Lookup |
|
MW |
mork:WeightedMatch |
|
MI |
mork:Interpolation |
a skos:OrderedCollection, not a DataMapping |
MH |
mork:Hypothesis |
normally inferred; rarely asserted |
MN |
mork:IndexedMapping |
|
MG |
mork:GenerativeMapping |
abstract; prefer MS/MR/MT/MP |
MS |
mork:ShapeMapping |
implied by gs |
MR |
mork:RuleMapping |
implied by gr |
MT |
mork:TransformMapping |
implied by gt |
MP |
mork:ProjectionMapping |
implied by gc |
Concept
| Code | Class | Notes |
|---|---|---|
C |
mork:DataConcept |
default type inside %T |
CO |
mork:Objectification |
Representation
| Code | Class | Notes |
|---|---|---|
R |
mork:Representation |
default type inside %R |
RE |
mork:Entity |
|
RA |
mork:Attribute |
|
RY |
mork:Array |
|
RS |
mork:Association |
|
RC |
mork:Composition |
|
RH |
mork:Chaining |
|
RL |
mork:Collection |
|
RO |
mork:OrderedCollection |
|
RU |
mork:UnorderedCollection |
|
RT |
mork:SetListing |
|
RM |
mork:Membership |
abstract by convention |
RN |
mork:AnonymousElement |
|
RV |
mork:ScalarValueElement |
|
RX |
mork:CollectionElement |
|
RF |
mork:Referencing |
|
RI |
mork:IdentifiableElement |
Ontology proxy (members of an OntologicalScheme)
| Code | Class | Notes |
|---|---|---|
X |
mork:OwlAxiom |
default type inside %O |
XC |
mork:OwlClass |
|
XO |
mork:OwlObjectProperty |
|
XD |
mork:OwlDataProperty |
|
XG |
mork:Digraph |
deprecated in Mork.ttl |
XI |
mork:DeferredConceptIRI |
the class; the punned individual is .dci |
Intent
| Code | Class | Notes |
|---|---|---|
I |
mork:IntentNode |
default type inside %I |
IQ |
mork:QualitativeIntent |
|
IN |
mork:QuantitativeConstraint |
|
IS |
mork:SpatialScope |
|
IT |
mork:TemporalScope |
|
IX |
mork:ExclusionIntent |
|
II |
mork:InclusionIntent |
|
IR |
mork:NormativeReferenceItent |
IRI spelling preserved exactly as declared in Mork.ttl |
Generative support
| Code | Class | Notes |
|---|---|---|
GT |
mork:TargetingSpec |
positional form [mode target] |
GP |
mork:ParameterBinding |
positional form [name type value] |
GC |
mork:ConstraintProvenance |
positional form [creator model conf status] |
GR |
mork:RuleProvenance |
positional form [creator model conf status] |
GF |
mork:TransformProvenance |
positional form [creator model conf status] |
GJ |
mork:ProjectionProvenance |
positional form [creator model conf status] |
GS |
mork:ShapeTemplate |
|
GU |
mork:RuleTemplate |
|
GQ |
mork:QueryTemplate |
Template expression
| Code | Class | Notes |
|---|---|---|
E |
mork:TemplateExpression |
normally produced by id = expr lines |
EL |
mork:LiteralExpression |
|
ER |
mork:RefExpression |
|
EC |
mork:ConcatExpression |
|
EI |
mork:InterpolationExpression |
|
EK |
mork:LookupExpression |
|
EB |
mork:TemplateBinding |
positional form [name expr] |
Scheme (normally introduced by block headers; usable as explicit types in %X)
| Code | Class | Notes |
|---|---|---|
SM |
mork:MappingScheme |
block header %M |
ST |
mork:TaxonomyScheme |
block header %T |
SR |
mork:RepresentationScheme |
block header %R |
SO |
mork:OntologicalScheme |
block header %O |
SI |
mork:IntentScheme |
block header %I |
SK |
mork:ConstraintScheme |
block header %K |
SU |
mork:RuleScheme |
block header %U |
Miscellaneous and external artefact types
| Code | Class | Notes |
|---|---|---|
CM |
mork:CompilationMode |
|
SF |
mork:SerializationFormat |
|
SH |
sh:NodeShape |
payload form SH "<shacl-compact>", §10.3 |
SP |
sh:PropertyShape |
|
SW |
swrl:Imp |
payload form SW "<swrl-compact>", §10.2 |
TM |
rr:TriplesMap |
payload form TM "<rml-compact>", §10.4 |
PI |
fnd:PersistentIdentity |
Kind: object takes an IRI, inline node or payload; data takes a literal typed per the last column (inferred per §3.5); polymorphic is resolved by subject type per §8.3.
Scheme membership (normally implicit via block headers)
| Code | Property | Kind | Literal type |
|---|---|---|---|
ms |
mork:mappingScheme |
object | |
cs |
mork:conceptScheme |
object | |
rs |
mork:representationScheme |
object | |
os |
mork:ontologicalScheme |
object | |
is |
mork:intentScheme |
object | |
in |
skos:inScheme |
object |
Mapping selection
| Code | Property | Kind | Literal type |
|---|---|---|---|
f |
mork:mappingFor |
object | |
hm |
mork:hasMapping |
object | |
iv |
mork:indicativeMapping |
object |
Exact match
| Code | Property | Kind | Literal type |
|---|---|---|---|
xt |
mork:exactTBoxMatch |
object | |
xr |
mork:exactRBoxMatch |
object | |
xa |
mork:exactABoxMatch |
object | |
xi |
mork:inverseRBoxMatch |
object | |
xm |
mork:intransitiveExactMatch |
object |
Category match
| Code | Property | Kind | Literal type |
|---|---|---|---|
bt |
mork:broadTBoxCategoryMatch |
object | |
br |
mork:broadRBoxCategoryMatch |
object | |
ba |
mork:broadABoxCategoryMatch |
object | |
nt |
mork:narrowTBoxCategoryMatch |
object | |
nr |
mork:narrowRBoxCategoryMatch |
object | |
na |
mork:narrowABoxCategoryMatch |
object | |
bc |
mork:broadCategoryMatch |
object | |
nc |
mork:narrowCategoryMatch |
object |
Possible match
| Code | Property | Kind | Literal type |
|---|---|---|---|
px |
mork:possibleMatch |
object | |
lx |
mork:lexicalMatch |
object | |
sx |
mork:semanticMatch |
object | |
tx |
mork:structuralMatch |
object | |
pt |
mork:pathMatch |
object | |
rp |
mork:referenceDataPath |
object | |
pm |
mork:partialMatch |
object | equivalent to px |
mum |
mork:missingOrUnrelatedMatch |
object | deprecated |
dgm |
mork:digraphMatch |
object | deprecated |
dgo |
mork:digraphOf |
object | deprecated |
Mapping composition
| Code | Property | Kind | Literal type |
|---|---|---|---|
cn |
mork:compositeNarrowerMapping |
object | |
cb |
mork:compositeBroaderMapping |
object | |
cnt |
mork:compositeNarrowerTemplate |
object | |
cbt |
mork:compositeBroaderTemplate |
object | |
nrm |
mork:narrowerMapping |
object | |
brm |
mork:broaderMapping |
object | |
wn |
mork:weightedNarrower |
object | |
wb |
mork:weightedBroader |
object | |
ap |
mork:broaderApplicative |
object |
Mapping relation
| Code | Property | Kind | Literal type |
|---|---|---|---|
rm |
mork:relatedMapping |
object | |
cm |
mork:closeMapping |
object | |
em |
mork:exactMapping |
object | |
sg |
mork:siblingMapping |
object |
Deferral and templates
| Code | Property | Kind | Literal type |
|---|---|---|---|
df |
mork:deferredMapping |
object | |
dp |
mork:dependentMapping |
object | |
hy |
mork:hypothesisMapping |
object | |
tp |
mork:templateMapping |
object | |
it |
mork:identityTemplateMapping |
object | |
rd |
mork:referenceDataMapping |
object | |
tc |
mork:templateClassMapping |
object | |
y |
mork:yieldConcept |
object |
Placeholder
| Code | Property | Kind | Literal type |
|---|---|---|---|
ph |
mork:placeholderMapping |
object | |
cc |
mork:concatenation |
object | |
ic |
mork:interpolationComponent |
object |
Property assertion
| Code | Property | Kind | Literal type |
|---|---|---|---|
ad |
mork:assertsPropertyDomains |
object | |
ar |
mork:assertsPropertyRanges |
object | |
pma |
mork:propertyMappingAssertions |
object |
Concept and representation structure
| Code | Property | Kind | Literal type |
|---|---|---|---|
kn |
mork:compositeNarrower |
object | |
kb |
mork:compositeBroader |
object | |
an |
mork:associativeNarrower |
object | |
ab |
mork:associativeBroader |
object | |
brl |
mork:broadConceptRole |
object | |
nrl |
mork:narrowConceptRole |
object | |
bnr |
mork:broadNavigableConceptRole |
object | |
nnr |
mork:narrowNavigableConceptRole |
object | |
mp |
mork:memberProperty |
object | |
mo |
mork:memberOf |
object | |
et |
mork:elementType |
object | |
rl |
mork:relatedProperty |
object | |
ro |
mork:representationOf |
object | |
ra |
mork:representedAs |
object | |
hc |
mork:hasConcept |
object | |
fm |
mork:format |
object |
SKOS
| Code | Property | Kind | Literal type |
|---|---|---|---|
> |
skos:broader |
object | |
< |
skos:narrower |
object | |
~ |
skos:related |
object | |
s= |
skos:exactMatch |
object | |
s~ |
skos:closeMatch |
object | |
s> |
skos:broadMatch |
object | |
s< |
skos:narrowMatch |
object | |
s- |
skos:relatedMatch |
object | |
sm |
skos:member |
object |
Precedence (normally derived, not asserted)
| Code | Property | Kind | Literal type |
|---|---|---|---|
pr |
mork:precedes |
object | |
sp |
mork:softPrecedes |
object | |
rif |
mork:resolvesIdentityFor |
object | |
tb |
mork:templateBinding |
object |
Intent
| Code | Property | Kind | Literal type |
|---|---|---|---|
ri |
mork:refinesIntent |
object | |
im |
mork:intentMapping |
object | |
sv |
mork:scopeValue |
object | |
cu |
mork:constraintUnit |
object | |
cd |
mork:constraintDimension |
object | |
src |
mork:hasNaturalLanguageSource |
data | xsd:string |
cf |
mork:hasIntentConfidence |
data | xsd:decimal |
op |
mork:constraintOperator |
data | xsd:string |
cv |
mork:constraintValue |
data | xsd:decimal |
cuv |
mork:constraintUpperValue |
data | xsd:decimal |
sty |
mork:scopeType |
data | xsd:string |
sin |
mork:scopeInclusion |
data | xsd:string |
td |
mork:temporalDuration |
data | xsd:dayTimeDuration |
tu |
mork:temporalUnit |
data | xsd:string |
rid |
mork:referenceIdentifier |
data | xsd:string |
Template expression (normally produced by expression lines, §10.1)
| Code | Property | Kind | Literal type |
|---|---|---|---|
cl |
mork:concatLeft |
object | |
cr |
mork:concatRight |
object | |
hb |
mork:hasBinding |
object | |
ls |
mork:lookupSource |
object | |
lk |
mork:lookupScheme |
object | |
lp |
mork:lookupProperty |
object | |
pe |
mork:placeholderExpression |
object | |
ts |
mork:templateString |
data | xsd:string |
phn |
mork:placeholderName |
data | xsd:string |
Generative
| Code | Property | Kind | Literal type |
|---|---|---|---|
tg |
mork:hasTargetingSpec |
object | bare IRI value ⇒ [c IRI], §9.6 |
pb |
mork:hasParameterBinding / mork:paramBinding |
polymorphic | |
do |
mork:dependsOnMapping |
object | |
gs |
mork:generatesShapeDefinition |
object | string value ⇒ SHACL payload |
gr |
mork:generatesRuleDefinition |
object | string value ⇒ SWRL payload |
gt |
mork:generatesTransformDefinition |
object | string value ⇒ RML payload |
gc |
mork:generatesClassDefinition |
object | |
tl |
mork:hasShapeTemplate / mork:hasRuleTemplate |
polymorphic | |
st |
mork:hasShapeTemplate |
object | |
rt |
mork:hasRuleTemplate |
object | |
pv |
mork:hasConstraintProvenance / mork:hasRuleProvenance / mork:hasTransformProvenance / mork:hasProjectionProvenance |
polymorphic | |
pvc |
mork:hasConstraintProvenance |
object | |
pvr |
mork:hasRuleProvenance |
object | |
pvt |
mork:hasTransformProvenance |
object | |
pvp |
mork:hasProjectionProvenance |
object | |
se |
mork:hasSeverity |
object | |
aj |
mork:appliesInJurisdiction |
object | |
bq |
mork:bindsToCriteria |
object | |
ju |
mork:jurisdiction |
data | xsd:string |
ef |
mork:effectiveFrom |
data | xsd:dateTime |
eu |
mork:effectiveUntil |
data | xsd:dateTime |
kv |
mork:constraintVersion |
data | xsd:string |
mv |
mork:mappingVersion |
data | xsd:string |
dr |
mork:deprecationReason |
data | xsd:string |
pnm |
mork:paramName |
data | xsd:string |
pty |
mork:paramType |
data | xsd:string |
pvl |
mork:paramValue |
data | inferred |
ql |
mork:queryLanguage |
data | xsd:string |
qt |
mork:queryText |
data | xsd:string |
swt |
mork:swrlText |
data | xsd:string (deprecated) |
to |
mork:targetsOntology |
data | xsd:anyURI |
ih |
mork:inputHash |
data | xsd:string |
mid |
mork:llmModelId |
data | xsd:string |
lc |
mork:llmConfidence |
data | xsd:decimal |
rv |
mork:reviewStatus |
data | xsd:string |
pc |
mork:provenanceCreator |
data | xsd:string |
pd |
mork:provenanceCreated |
data | xsd:dateTime |
isc |
mork:impactScope |
data | xsd:string |
Versioning and Foundation
| Code | Property | Kind | Literal type |
|---|---|---|---|
ss |
mork:supersedes |
object | |
eg |
mork:egressProjection |
object | |
md |
mork:compilationMode |
object | |
gv |
fnd:hasGovernanceState |
object | |
fi |
fnd:hasIdentity |
object | |
sby |
fnd:supersededBy |
object |
Core data
| Code | Property | Kind | Literal type |
|---|---|---|---|
w |
mork:weighting |
data | xsd:integer |
n |
mork:mappingNote |
data | xsd:string |
c |
mork:conceptName |
data | xsd:string |
ci |
mork:conceptIRI |
data | xsd:anyURI |
cid |
mork:conceptId |
data | xsd:string |
i |
mork:individualName |
data | xsd:string |
ii |
mork:individualIRI |
data | xsd:anyURI |
id |
mork:identifier |
data | xsd:string |
p |
mork:path |
data | xsd:string |
rf |
mork:reference |
data | xsd:string |
r |
mork:dataRef |
data | xsd:string |
v |
mork:dataInline |
data | inferred |
dt |
mork:data |
data | inferred |
ix |
mork:mappingIndex |
data | xsd:integer |
mr |
mork:mappingRecommendation |
data | xsd:string |
nme |
mork:name |
data | xsd:string |
tt |
mork:template |
data | xsd:string |
ct |
mork:conceptNameTemplate |
data | xsd:string |
cft |
mork:conceptFQNameTemplate |
data | xsd:string |
cst |
mork:conceptShortNameTemplate |
data | xsd:string |
ip |
mork:interpolationTemplate |
data | xsd:string |
inc |
mork:incompleteMapping |
data | xsd:boolean |
icn |
mork:itemsConstrained |
data | xsd:boolean |
ord |
mork:orderedItems |
data | xsd:boolean |
unq |
mork:uniqueItems |
data | xsd:boolean |
iri |
mork:iri |
data | xsd:string |
ud |
mork:userDeclined |
data | xsd:string |
sw |
mork:swrlCompactSyntax |
data | xsd:string |
cev |
mork:collectionElementScalarValue |
data | inferred (internal) |
epv |
mork:externalPropertyValue |
data | inferred (internal) |
Annotation
| Code | Property | Kind | Literal type |
|---|---|---|---|
nn |
skos:note |
data | xsd:string |
sd |
skos:definition |
data | xsd:string |
lb |
rdfs:label |
data | xsd:string |
rc |
rdfs:comment |
data | xsd:string |
ex |
skos:example |
data | xsd:string |
sco |
skos:scopeNote |
data | xsd:string |
al |
skos:altLabel |
data | xsd:string |
en |
skos:editorialNote |
data | xsd:string |
hn |
skos:historyNote |
data | xsd:string |
sa |
rdfs:seeAlso |
object | |
db |
rdfs:isDefinedBy |
data | xsd:string |
sas |
owl:sameAs |
object | |
dep |
owl:deprecated |
data | xsd:boolean |
dcc |
dct:creator |
data | xsd:string |
dct |
dct:title |
data | xsd:string |
dcd |
dct:description |
data | xsd:string |
dco |
dct:contributor |
data | xsd:string |
SHACL targeting (on GT nodes; usually written positionally)
| Code | Property | Kind | Literal type |
|---|---|---|---|
tcl |
sh:targetClass |
object | |
tob |
sh:targetObjectsOf |
object | |
tsb |
sh:targetSubjectsOf |
object | |
tnd |
sh:targetNode |
object |
Resolution uses the subject’s asserted types at the point the code is decoded — including the block default type and any type implied by an earlier code on the same line. Generators SHOULD put the type token, or the payload code (gs/gr/gt/gc) that implies it, before any polymorphic code.
| Code | Subject type | Property |
|---|---|---|
pb |
mork:QueryTemplate |
mork:paramBinding |
pb |
anything else | mork:hasParameterBinding |
tl |
mork:ShapeMapping |
mork:hasShapeTemplate |
tl |
mork:RuleMapping |
mork:hasRuleTemplate |
pv |
mork:ShapeMapping |
mork:hasConstraintProvenance (inline node typed ConstraintProvenance) |
pv |
mork:RuleMapping |
mork:hasRuleProvenance (inline typed RuleProvenance) |
pv |
mork:TransformMapping |
mork:hasTransformProvenance (inline typed TransformProvenance) |
pv |
mork:ProjectionMapping |
mork:hasProjectionProvenance (inline typed ProjectionProvenance) |
An unresolvable polymorphic code is an error; the explicit codes (pvc, st, …) are always available.
Reserved tokens begin with . and are valid only in object position.
| Token | Resolves to |
|---|---|
.T |
owl:Thing |
.N |
owl:Nothing |
.top |
owl:topObjectProperty |
.dtop |
owl:topDataProperty |
.dci |
mork:DeferredConceptIRI |
.dcd |
mork:DeferredClassDefinition |
.dop |
mork:DeferredObjectPropertyDefinition |
.ddp |
mork:DeferredDataPropertyDefinition |
.did |
mork:DeferredIndividualDefinition |
.dab |
mork:DeferredABoxReference |
.mos |
mork:MorkOntologyScheme |
.bot |
mork:Bottom |
.nothing |
mork:OwlNothing |
.json |
mork:JSON |
.xml |
mork:XML |
.yaml |
mork:YAML |
.draft |
mork:DraftMode |
.review |
mork:ReviewMode |
.prod |
mork:ProductionMode |
.viol |
sh:Violation |
.warn |
sh:Warning |
.info |
sh:Info |
.gD |
fnd:Draft |
.gR |
fnd:Reviewed |
.gA |
fnd:Active |
.gS |
fnd:Superseded |
subject [types] (code value-list)*
An identifier per §3.4. Local ids are the norm; a CURIE or <iri> subject lets a document assert facts about external individuals (for example owl:sameAs on an OwlAxiom proxy).
The token immediately after the subject is a type list if it begins with +, or if every +-separated part is a type code or a CURIE whose local name starts with an uppercase letter. Otherwise it is the first property code and the block default type applies. This rule means a CURIE property with an uppercase local name cannot appear first on a line; put any code before it. In !I lines the : type list additionally accepts bare local ids without the + (§11).
The rest of the line is a sequence of code value-list pairs. Codes may repeat. Order is not significant except for polymorphic resolution (§8.3) and IRI minting of inline nodes (§12), both of which depend on left-to-right order.
A value is one of: a bare token, a quoted literal, an <iri>, or an inline node […]. Its interpretation depends on the code’s kind (§4).
code a,b,c asserts three triples with the same predicate. There are no spaces around commas. A quoted string may appear as a list member. A bare string value containing a comma must be quoted.
[ … ] in value position creates a node, links it as the object of the current code, and asserts the contents on the new node. Three forms exist; the first token inside the brackets selects the form:
| First token | Form | Meaning |
|---|---|---|
#id |
explicit identity | the node is the local id id (or CURIE/<iri>); following tokens are interpreted as one of the two forms below |
| a type list (§9.2) | typed | the node gets those types, then code value pairs as on a node line |
| anything else | positional | only permitted under codes with a positional table (below); the first k tokens fill the slots in order (_ skips a slot), after which code value pairs may follow |
Positional tables:
| Under code | Node type | Slots | Notes |
|---|---|---|---|
tg |
TargetingSpec |
mode target |
mode ∈ c (sh:targetClass), o (sh:targetObjectsOf), s (sh:targetSubjectsOf), n (sh:targetNode). A bare IRI directly as the value of tg is shorthand for [c IRI]. |
pb |
ParameterBinding |
name type value |
type ∈ Numeric, Concept, Path, String, Duration, or the full word; value is inferred-typed per §3.5 |
pv |
provenance class per §8.3 | creator model confidence status |
status is the reviewStatus string (DRAFT, APPROVED, DEPRECATED) |
hb |
TemplateBinding |
name expr |
expr is a template expression (§10.1) |
Inline nodes without #id receive minted IRIs (§12.2), not blank nodes, so that generated provenance and targeting records are addressable by SHACL reports and audit queries. Inline nodes nest to any depth.
A { … } immediately following a value (no intervening space) annotates that assertion. The braces contain code value pairs exactly as on a node line. The decoder emits the OWL 2 annotated-axiom pattern:
xt :Loan{n "Close lexical match on 'loan'" w 90}
decodes to
:m mork:exactTBoxMatch fin:Loan .
[] a owl:Axiom ; owl:annotatedSource :m ; owl:annotatedProperty mork:exactTBoxMatch ;
owl:annotatedTarget fin:Loan ; mork:mappingNote "Close lexical match on 'loan'" ; mork:weighting 90 .
In a list, the braces bind to the preceding member: cn a{n "x"},b. Annotations on annotations are not supported (OWL 2 does not permit them either).
The codes gs, gr, gt and the types SH, SW, TM accept a payload string: a quoted literal written in the corresponding sub-notation (§10.2–10.4). The decoder mints an artefact node (§12.2), parses the payload into structured RDF (a sh:NodeShape, swrl:Imp or rr:TriplesMap graph), and links it. The payload text itself is not stored, except that a gr payload is additionally asserted as mork:swrlCompactSyntax on the mapping, as Mork.ttl recommends.
Three ways to supply an artefact, from cheapest to most explicit:
r1 MR gr "Applicant(?a)^hasCreditScore(?a,?s)^swrlb:greaterThanOrEqual(?s,700)->Eligible(?a)"
r1 MR gr [#eligRule SW "Applicant(?a)^…->Eligible(?a)" lb "Eligibility rule"]
r1 MR gr eligRule
eligRule SW "Applicant(?a)^…->Eligible(?a)"
Whether the artefact is LLM-authored or compiler-generated is a governance question (ADR-A25), not a syntactic one; both are written the same way.
An expression line defines a mork:TemplateExpression tree (Foundations §3.9):
id = expr
Expression grammar (whitespace insignificant except inside quotes):
expr := term ( '+' term )* concatenation, left-associative
term := "'" chars "'" LiteralExpression (dataInline)
| '$' path RefExpression (dataRef; path is bare or "quoted")
| '%' '"' template '"' '(' bind (',' bind)* ')' InterpolationExpression
| '?' scheme '.' property '(' expr ')' LookupExpression (lookupScheme, lookupProperty, lookupSource)
| '(' expr ')'
| identifier reference to an existing expression node
bind := name '=' expr TemplateBinding (placeholderName, placeholderExpression)
Examples and their decoded structure:
loanIri = 'http://ex.org/loan/'+$loanId
→ loanIri a ConcatExpression; concatLeft loanIri_l (a LiteralExpression, dataInline "http://ex.org/loan/"); concatRight loanIri_r (a RefExpression, dataRef "loanId").
fullName = %"{l}, {f}"(l=$last,f=$first)
→ fullName a InterpolationExpression, templateString "{l}, {f}", two hasBinding nodes fullName_b1, fullName_b2 each a TemplateBinding with placeholderName and placeholderExpression (fullName_b1_e …).
lob = ?LobScheme.skos:notation($lineOfBusiness)
→ lob a LookupExpression with lookupScheme LobScheme, lookupProperty skos:notation, lookupSource lob_s (a RefExpression).
a+b+c decodes as (a+b)+c, i.e. a left-nested ConcatExpression chain, matching the monoid structure the ontology describes. The same expression syntax is accepted as the value of pe, ls, cl and cr, and inside the positional hb form.
The payload for gr and SW is the compact rule syntax already used by mork:swrlCompactSyntax:
rule := atoms '->' atoms
atoms := atom ( '^' atom )*
atom := Class '(' arg ')' swrl:ClassAtom
| Prop '(' arg ',' arg ')' swrl:IndividualPropertyAtom or swrl:DatavaluedPropertyAtom (chosen by the second argument: literal ⇒ datavalued)
| 'swrlb:' name '(' arg (',' arg)* ')' swrl:BuiltinAtom
| 'sameAs' '(' arg ',' arg ')' swrl:SameIndividualAtom
| 'differentFrom' '(' arg ',' arg ')' swrl:DifferentIndividualsAtom
| Datatype '(' arg ')' swrl:DataRangeAtom (when the name resolves to an xsd: datatype)
arg := '?' name variable
| number | '"' chars '"' literal (integer, decimal, or string; typed strings as "…"^xsd:t)
| identifier individual
Names resolve per §3.4, so Applicant is a local id, :Applicant uses the target prefix and ex:Applicant is a CURIE. Whitespace around ^ and -> is permitted and SHOULD be omitted.
Decoding produces the standard SWRL RDF vocabulary: swrl:body and swrl:head are swrl:AtomLists (RDF collections); variables are IRIs minted as <rule-iri>_v_<name> and typed swrl:Variable; atom nodes and list cells are blank nodes numbered in document order.
The payload for gs and SH is a subset of SHACL Core sufficient for the shapes a ShapeTemplate instantiates. It is deliberately not the W3C SHACL Compact Syntax, which spends tokens on prefixes and braces; anything outside this subset is written with !T lines against the minted shape IRI.
shape := targets ( ';' clause )*
targets := target ( ',' target )*
target := ('c' | 'o' | 's' | 'n') IRI sh:targetClass | sh:targetObjectsOf | sh:targetSubjectsOf | sh:targetNode
clause := propshape
| '!' severity sh:severity on the node shape (.viol .warn .info)
| 'msg' string sh:message on the node shape
| 'closed' sh:closed true
| 'ignore' IRI (',' IRI)* sh:ignoredProperties
| 'nd' IRI sh:node
propshape := path constraint* one sh:property with sh:path
path := elem ( '/' elem )* sequence path (rdf:List) when more than one elem
elem := IRI | '^' IRI inverse path
constraint:= 'dt' IRI sh:datatype
| 'cls' IRI sh:class
| 'nd' IRI sh:node
| 'nk' ('iri'|'bnode'|'lit'|'biri'|'blit'|'ilit') sh:nodeKind
| '[' n '..' m? ']' sh:minCount n, sh:maxCount m (m omitted ⇒ no max)
| '>=' num | '<=' num | '>' num | '<' num sh:minInclusive sh:maxInclusive sh:minExclusive sh:maxExclusive
| 'in' '{' value (',' value)* '}' sh:in
| 're' string sh:pattern
| 'len' '[' n '..' m ']' sh:minLength sh:maxLength
| '=' value sh:hasValue
| 'eq' IRI | 'lt' IRI | 'lte' IRI | 'disj' IRI sh:equals sh:lessThan sh:lessThanOrEquals sh:disjoint
| '!' severity | 'msg' string | 'name' string
Example:
cs MS tg :Applicant pb [min N 700] pv [MappingAgent claude-opus-5 0.93 DRAFT] gs "c :Applicant; :hasCreditScore dt xsd:integer >=700 <=850 [1..1] !.viol msg \"credit score out of range\""
decodes to a sh:NodeShape (cs_gs1) with sh:targetClass ex:Applicant and one sh:property (cs_gs1_p1) carrying sh:path, sh:datatype, sh:minInclusive, sh:maxInclusive, sh:minCount, sh:maxCount, sh:severity and sh:message. Property shapes are minted IRIs by default and blank nodes under @opt bnode-shapes.
The payload for gt and TM covers the triples-map structure mork2rml emits: one logical source, one subject map, any number of predicate-object maps including parent joins.
tm := 'src' string ( 'ref' ('json'|'csv'|'xml') )? ( 'it' string )? rml:logicalSource: rml:source, rml:referenceFormulation (default json ⇒ ql:JSONPath), rml:iterator
';' 'sm' template ( 'cls' IRI )* rr:subjectMap: rr:template, rr:class
( ';' pom )*
pom := IRI objmap ( 'dt' IRI | 'lang' tag )? rr:predicateObjectMap with rr:predicate
objmap := '=' string rr:objectMap [ rml:reference ]
| ':' value rr:objectMap [ rr:constant ] (IRI or literal)
| '@' template rr:objectMap [ rr:template ]
| '^' IRI ( '[' child '=' parent (',' child '=' parent)* ']' )? rr:objectMap [ rr:parentTriplesMap ; rr:joinCondition [ rr:child ; rr:parent ] ]
Example:
tm_loan MT gt "src loan.json it $ ; sm http://ex.org/loan/{loanId} cls fin:Loan ; fin:principal ^tm_principal_gt1[loanId=loanId]"
Blank nodes are used for logical source, subject map, predicate-object maps and object maps, exactly as R2RML/RML conventionally does; the triples map itself is a minted IRI.
Used by ! lines (§11). Precedence from loosest to tightest: | union, & intersection, ~ complement, restriction operators.
CE := IE ( '|' IE )* owl:unionOf
IE := AE ( '&' AE )* owl:intersectionOf
AE := '~' AE owl:complementOf
| '(' CE ')'
| '{' ind (',' ind)* '}' owl:oneOf
| PE '>' AE owl:someValuesFrom
| PE '<' AE owl:allValuesFrom
| PE '=' value owl:hasValue
| PE '@' owl:hasSelf
| PE '#' card ( '/' AE )? cardinality; '/' makes it qualified (owl:onClass / owl:onDataRange)
| identifier named class or datatype
PE := identifier | '^' identifier property or its inverse (owl:inverseOf blank node)
card := n | n '..' | '..' m | n '..' m exactly n | min n | max m | min n and max m
The qualifier separator is / rather than : because :Name already denotes the default target prefix (§3.4); p#1../:Loan is a qualified cardinality over fin:Loan when @t fin is in force.
Data ranges use the same grammar with datatype identifiers and literal enumerations {"a","b"}; facet restrictions are written as !T triples.
Examples from Mork.ttl, as they would be written in MCN:
!C mork:Datum = mork:deferredMapping>mork:DataMapping < mork:DataMapping
!C mork:Objectification = mork:broadConceptRole#1../skos:Concept & mork:narrowConceptRole#1../skos:Concept < mork:DataConcept
!C mork:Attribute = (^mork:memberProperty>mork:Association | ^mork:memberProperty>mork:Entity) & mork:memberProperty#0/mork:Representation < mork:IdentifiableElement
!G mork:broaderApplicative>mork:DataMapping & ~mork:exactRBoxMatch>.T < .N
The last line is GCI Axiom 2.14a verbatim: ∃broaderApplicative.DataMapping ⊓ ¬∃exactRBoxMatch.⊤ ⊑ ⊥.
Axiom lines carry OWL content that is not an individual’s property assertion. They make MCN complete for the T-Box of Mork.ttl itself and for the shadow declarations mapping data routinely needs (Rep_Falcon_Eggs a owl:DatatypeProperty).
| Line | Meaning |
|---|---|
!C id clause* |
owl:Class declaration. Clauses: = CE equivalentClass, < CE subClassOf, ! CE disjointWith, or any code value pair (annotations). |
!O pe clause* |
owl:ObjectProperty. pe is a PE (§10.5): normally a plain id, but ^id makes the subject ObjectInverseOf(id) (a minted, typed blank node) — used for Mork.ttl’s own precedence axioms, e.g. !O ^compositeBroaderMapping < precedes for Axiom P1 (SubObjectPropertyOf(ObjectInverseOf(:compositeBroaderMapping) :precedes)). Clauses: < PE subPropertyOf, = PE equivalentProperty, inv PE inverseOf, dom CE, rng CE, +F +IF +S +AS +T +R +IR characteristics (combinable: +AS+IR, but OWL 2 DL forbids +AS and +IR alongside +T, ADR-A97), chain PE,PE,… propertyChainAxiom, or code value pairs. |
!D id clause* |
owl:DatatypeProperty. Clauses as !O minus inv/chain, plus rng taking a data range. |
!A id clause* |
owl:AnnotationProperty. Clauses: < id, dom, rng, code value pairs. |
!I id (: types)? (code value)* |
owl:NamedIndividual outside any block: : type+type gives class assertions, where each type is a type code or any identifier (local ids allowed without +); the rest as a node line. |
!G CE < CE |
general class inclusion. |
!DC a,b,c |
owl:AllDisjointClasses. |
!DP a,b,c |
owl:AllDisjointProperties. |
!DI a,b,c |
owl:AllDifferent. |
!SA a,b,c |
pairwise owl:sameAs. |
!T s p o |
one raw triple. Each term is an identifier, _:label blank node, <iri> or quoted literal (with @lang/^dt). The escape hatch of last resort; a decoder MUST accept any triple this way. |
Ids in ! lines are identifiers per §3.4, so external terms are declared with CURIEs (!C fin:Loan) and local terms with bare ids.
A local id x under base B is the IRI B + x. The generator owns these names; the decoder never rewrites them.
Nodes the generator does not name receive deterministic IRIs derived from their parent and position:
| Node | IRI |
|---|---|
inline node, n-th under code k on subject S (counting per subject and code, in document order) |
S + _ + k + n — e.g. cs_shape_tg1, cs_shape_pb2 |
artefact payload under gs/gr/gt on S |
S_gs1, S_gr1, S_gt1 |
expression root from line id = … |
id |
concat operands of expression node E |
E_l, E_r |
interpolation binding n of E, and its expression |
E_bn, E_bn_e |
lookup source of E |
E_s |
SWRL variable ?x of rule R |
R_v_x |
SHACL property shape n of node shape N |
N_pn (unless @opt bnode-shapes) |
The separator _ and the code names cannot collide with generator-chosen ids in practice; a generator that wants to be certain avoids ids ending in _ + code + digits.
Only OWL class expressions, RDF lists, SWRL atoms, R2RML term maps, owl:Axiom reifications and !T blank nodes are blank. The decoder labels them _:b1, _:b2, … (_:a1, … for axiom reifications) in the order they are created during the single left-to-right pass. Two decoders conforming to this specification therefore produce byte-identical canonical N-Triples for the same input, which is what makes mork:inputHash over decoded output meaningful.
The canonical decoding of a document is: the N-Triples serialisation of the decoded graph, one triple per line, sorted by the byte order of (subject, predicate, object) after blank-node labelling as in §12.3, with no trailing whitespace and a final newline. Hashes, diffs and cache keys SHOULD be computed over the canonical decoding rather than over the MCN text, since two MCN texts can denote the same graph (different line order, quoted versus bare literals, M+MD versus MD).
The algorithm is presented as the decoder’s obligations, in order. A conforming decoder may be implemented in any way that produces the same graph.
D1. Line assembly. Split into physical lines; drop blank lines and comment lines; join ;-continuations. Number logical lines for error reporting.
D2. Directive pass is not separate. Directives take effect when reached. Initialise: prefix table = predeclared prefixes; base = none; target prefix = none; block = %X; options = {}.
D3. For each logical line, dispatch on its first character.
D4. Block header %K id …: resolve id; assert id a owl:NamedIndividual, id a <scheme class>; if %R and the next token is a reserved format token, assert mork:format; decode the remaining code value pairs with id as subject; set the current block context (membership property, scheme IRI, default type). %X clears the context.
D5. Node line.
S. Assert S a owl:NamedIndividual.=, this is an expression line: decode the expression (§10.1) rooted at S and stop.S a T for each.S <membership> <scheme>.SH, SW or TM and the next token is a quoted string, decode it as a payload (§10.2–10.4) onto S (no minting).code value-list pairs (D6).D6. Property pair k v1{a1},v2{a2},… on subject S:
k to a predicate P and kind (§4, §8.3). A CURIE is resolved directly.v:
[…] → create node N (§9.6, §12.2), assert its type(s), decode its contents with N as subject; object = N.k is tg and v is a bare identifier → as if [c v].k ∈ {gs,gr,gt} and v is quoted → mint the artefact node, decode the payload onto it; object = that node; if gr, also assert S mork:swrlCompactSyntax "v".S P object.k ∈ {gs,gr,gt,gc}, assert the implied type (§5).{…} followed v: create _:a, assert _:a a owl:Axiom, owl:annotatedSource S, owl:annotatedProperty P, owl:annotatedTarget object, then decode the braces’ pairs with _:a as subject.D7. Axiom line per §11, using the class-expression parser of §10.5. !T asserts exactly one triple.
D8. Options. If inv is set, after the pass add o P⁻ s for every asserted s P o where P has a known owl:inverseOf in Mork.ttl (the decoder ships this table; it is not read from the data). If strict is set, run the lint rules of §14.2 and fail on any hit.
D9. Output. The decoded graph. For OWL tooling, serialise as Turtle/RDF-XML or hand the graph to an OWL API; the graph conforms to the OWL 2 RDF mapping so long as the generator has not used !T to write something outside OWL 2 DL (a decoder does not police that; the DL checker does).
For readers who prefer to think in functional syntax, the constructs MCN emits map as follows:
| MCN | OWL 2 axiom |
|---|---|
node line subject, type T |
ClassAssertion(T s) (plus Declaration(NamedIndividual s)) |
| object-kind pair | ObjectPropertyAssertion(P s o) |
| data-kind pair | DataPropertyAssertion(P s "v"^^dt) — or AnnotationAssertion when P is an annotation property in Mork.ttl (n, nn, sd, lb, rc, ex, sco, al, en, hn, sa, db, dep, dcc, dct, dcd, dco, ud, sw) |
{…} on a pair |
the same axiom with an annotation set |
!C id = CE / < CE / ! CE |
EquivalentClasses, SubClassOf, DisjointClasses |
!O clauses |
SubObjectPropertyOf, EquivalentObjectProperties, InverseObjectProperties, ObjectPropertyDomain/Range, Functional…, SubObjectPropertyOf(ObjectPropertyChain …) |
!G |
SubClassOf between anonymous class expressions |
!DC / !DP / !DI |
DisjointClasses, DisjointObjectProperties, DifferentIndividuals |
@o, @i |
ontology IRI, Import |
Because MCN emits the RDF graph rather than structural axioms directly, the OWL 2 RDF-to-structural mapping performs this translation; the table only documents what to expect.
A decoder MUST reject the document, naming the logical line, on:
:Local token without @t, or a local id without @b;{…} not directly attached to a value;@i before @o; rebinding a predeclared prefix;! line with an unknown kind or clause.A decoder MUST NOT reject a document because a referenced IRI is not defined in it, because a GCI of Mork.ttl is violated, or because a mapping is incomplete. Those are graph-level judgements made after decoding.
Decoders SHOULD offer the following warnings (errors under @opt strict). Each mirrors an axiom in Mork.ttl and is checked on the decoded graph, so a model’s mistakes surface before an OWL reasoner or SHACL engine is involved. They are heuristics, not the source of truth — ontology/mork/shapes/constraints.ttl and the reasoner are.
| Lint | Mirrors |
|---|---|
MD node without df |
Datum ≡ ∃deferredMapping.DataMapping; Axiom 2.7a |
ap without xr or xi on the same subject |
GCI 2.14a |
ba together with cb but no xr/xi |
GCI 2.14b |
br together with cb but neither xa nor ba |
GCI 2.14d |
ss without ef |
GCI 5.10a |
MS/MR/MP without tg, without at least one pb, or without pv |
GenerativeMapping completeness GCIs (§5.6, §6.13.6) |
MT without pv |
TransformMapping completeness |
GT node with none of tcl/tob/tsb/tnd |
Axiom 5.6h-i |
provenance node without pc and pd |
Axiom 6.13.7c |
both gs and gr on one subject |
ShapeMapping/RuleMapping disjointness |
both cn and ap pointing at the same node |
the double-counting hazard documented in the loan example |
a node in %M with gv beyond .gD but no fi |
ADR-A22 production gate |
MU with neither hy nor mr |
UncertainMapping equivalence |
After decoding, the graph goes through exactly the existing pipeline: OWL 2 DL profile check, SHACL validation with ontology/mork/shapes/constraints.ttl (including the mode-conditional GenerativeMappingProductionGovernanceShape), the parity and conformance gate (ADR-A28) and compilation (ADR-A19). MCN adds nothing to and removes nothing from that pipeline.
Decoding is the required direction. Encoding is specified so that existing Turtle can be placed in an LLM context at MCN cost (a mapping scheme fed back for revision, a taxonomy handed to a mapping agent, the MORK T-Box itself as a reference). Encoding is lossless for any graph; it is compact for MORK-shaped graphs.
E1. Partition subjects. Every named subject with mork:mappingScheme, mork:conceptScheme, mork:representationScheme, mork:ontologicalScheme, mork:intentScheme or skos:inScheme pointing at a ConstraintScheme/RuleScheme is assigned to that scheme’s block (a subject with two memberships goes to the first in the order just given and carries the other explicitly). Everything else goes to %X.
E2. Header. Emit @b for the most common namespace among local subjects, @p for every other namespace used, @t for the most-referenced namespace in object position that is not the base, @o/@i from the owl:Ontology node.
E3. Per block, emit the header with the scheme’s own properties, then one node line per subject:
owl:NamedIndividual; drop the block default type if present; map the rest to type codes (unknown classes as CURIEs); omit a type implied by a payload code that will be emitted;^dt/@lang as needed;s P o and o P⁻ s are present, emit only the one whose subject is being written first and set @opt inv — or emit both (always correct, sometimes wasteful);owl:Axiom reifications whose source/property/target match an emitted pair become {…} on that value; unmatched reifications (the Zoo example’s defect) are emitted as !T lines with _: labels;TargetingSpec, ParameterBinding, provenance and TemplateBinding nodes referenced from exactly one subject and having only the positional properties collapse into positional inline nodes; other single-parent helper nodes become typed inline nodes; multi-parent nodes stay as their own lines;TemplateExpression trees whose root is referenced from elsewhere become expression lines; swrl:Imp, sh:NodeShape and rr:TriplesMap graphs that fit the payload sub-notations become payload strings, otherwise SW/SH/TM node lines with !T for the rest.E4. T-Box. owl:Class, property and GCI content is emitted as ! lines, using §10.5 for class expressions; anything unrepresentable falls back to !T.
E5. Verify. Decode the result and check graph isomorphism with the input. An encoder that cannot make this check pass MUST fall back to !T for the offending triples rather than emit an approximation.
MCN, 219 tokens, decoding to exactly the 40 triples of loan_mapping.ttl:
@b <http://example.org/mapping#>
@p fin=<http://example.org/fin#>
@p qnt=<https://www.nebularis.org/neuro-semantic/lattice/quantification#>
@t fin
!D qnt:numericValue
!O qnt:onSpace
!O qnt:inUnit
!C :Loan
!O :principal dom :Loan rng qnt:Quantity
!I :LoanPrincipalSpace : qnt:ValueSpace
!I :USD : qnt:Unit
%R Scheme_Loan_Source .json id loan.json
%X
Concept_Loan C cs MS rs Scheme_Loan_Source p $
%M MS
Map_Loan_Class xt :Loan
Map_Loan M+MD f Concept_Loan df Map_Loan_Class c Loan r loanId
Map_Principal ap Map_Loan cb Map_Loan xi :principal c LoanPrincipal r loanId cn Map_PrincipalValue
Map_PrincipalValue cb Map_Principal xr qnt:numericValue r principal
Reading the mapping block: Map_Loan_Class is a DataMapping (block default) whose T-Box match is fin:Loan. Map_Loan is a Datum for Concept_Loan, deferred on the class mapping, individuated as Loan keyed by loanId. Map_Principal applies in the context of Map_Loan (ap), composes into it (cb), and reaches the quantity through the inverse of fin:principal (xi); its component Map_PrincipalValue asserts qnt:numericValue from the principal field. M+MD reproduces the original’s redundant double typing; MD alone would be the normal spelling.
From UncertainMappings.ttl, showing annotated matches, multi-valued matches to deferred definitions, DeferredContext yields and a shadow property declaration:
@b <http://www.nebularis.org/ontologies/UncertainMappings#>
@o <http://www.nebularis.org/ontologies/UncertainMappings>
@i <http://www.nebularis.org/ontologies/Mork>,<http://www.w3.org/2004/02/skos/core>,<http://www.nebularis.org/ontologies/Zoo>,<http://www.nebularis.org/ontologies/Petstore>
@p zoo=<http://www.nebularis.org/ontologies/Zoo#>
@p pets=<http://www.nebularis.org/ontologies/Petstore#>
@t zoo
!C OviparousBase = :laysEggs=t
!D Map_EggSize_Hypothesis rng xsd:string
%M Mappings
Map_EggSize MU hy Map_EggSize_Hypothesis lx :laysEggs{n "Close lexical match on conceptName 'EggSize' and DataProperty name 'eggSize'."} mr "Create a new datatype property in the output ontology to represent the size of Eggs." w 40
Map_EggSize_Hypothesis ad Map_Yield_Oviparous br .top f EggSize c #eggSize n "Mapping the concept 'EggSize' to a new data property 'eggSize' in the target ontology."
Map_Falcon_Hypothesis MD cn Map_Lays_Eggs df Map_Gen_Bird_Class{n "Falcon is a bird, birds lay eggs, however birds are not reptiles."} xt .dcd,Map_Yield_Oviparous,Map_Yield_Pet i #Falcon n "Hypothesis mapping for 'Falcon' is Pet, Bird."
Map_Lays_Eggs MD cn Map_Eggs xr :laysEggs v t n "Asserts the property `Falcon laysEggs true`"
Map_Oviparity MX bt .T tc OviparousBase c #Oviparous mr "Recommend introducing a notional construct to model reproductive modes, with 'Oviparity' as a member/individual."
Map_Yield_Oviparous MX y .dci c #Oviparous n "Yields the axiom <BaseIRI>#Oviparous at processing time."
The full encoding (1366 tokens against 2674 for the Turtle) decodes to a graph differing from the original in exactly three owl:annotatedTarget triples, where the original’s reified annotations reference mork:Map_Falcon_Hypothesis, mork:Map_Gen_Bird_Class and mork:Map_Platypus_Hypothesis — IRIs that do not exist — instead of the local individuals. In MCN the {…} form cannot express that mistake.
@u lattice-insure-o
%I Intents ju US ef 2026-01-01T00:00:00Z
i1 IN src "credit score at least 700" cf 0.93 op GreaterThanOrEqual cv 700 cd :creditScore
i2 IS src "in US states" sty State sin Include sv :US ri i1
%M MS md .prod mv 1.2
cs_shape MS im i1 tg :Applicant pb [min N 700] pv [MappingAgent claude-opus-5 0.93 DRAFT pd 2026-09-19T10:00:00Z] tl :RangeTemplate se .viol gv .gA fi [PI] gs "c :Applicant; :hasCreditScore dt xsd:integer >=700 <=850 [1..1]"
cs_rule MR im i1 tg [o :hasCreditScore] aj :US pb [threshold N 700] pv [MappingAgent claude-opus-5 0.9 DRAFT pd 2026-09-19T10:00:00Z] gr "Applicant(?a)^hasCreditScore(?a,?s)^swrlb:greaterThanOrEqual(?s,700)->Eligible(?a)"
v2 MS ss cs_shape ef 2026-10-01T00:00:00Z fi cs_shape_fi1 gv .gA tg :Applicant pb [min N 720] pv [reviewer _ _ APPROVED pd 2026-10-01T00:00:00Z] gs "c :Applicant; :hasCreditScore >=720"
loanIri = 'http://ex.org/loan/'+$loanId
Decoded, cs_shape is a ShapeMapping with hasTargetingSpec cs_shape_tg1 (sh:targetClass ex:Applicant), hasParameterBinding cs_shape_pb1 (paramName "min", paramType "Numeric", paramValue 700), hasConstraintProvenance cs_shape_pv1 (creator, model id, confidence, review status, created), hasShapeTemplate, hasSeverity sh:Violation, fnd:hasGovernanceState fnd:Active, fnd:hasIdentity cs_shape_fi1 (a PersistentIdentity), and generatesShapeDefinition cs_shape_gs1 (a structured sh:NodeShape). cs_rule is a RuleMapping with hasTargetingSpec cs_rule_tg1 (sh:targetObjectsOf :hasCreditScore), appliesInJurisdiction :US asserted directly on cs_rule itself (mork:appliesInJurisdiction has rdfs:domain :GenerativeMapping in Mork.ttl, not TargetingSpec, so it is written as a top-level pair rather than nested inside tg [...]), and whose generatesRuleDefinition is a structured swrl:Imp with body and head atom lists and minted variables cs_rule_gr1_v_a, cs_rule_gr1_v_s; the compact text is retained as swrlCompactSyntax. v2 supersedes cs_shape, shares its persistent identity and carries the effectiveFrom GCI 5.10a requires.
Mork.ttl examples)@b <http://www.nebularis.org/hyperthunk/Mapping#>
%R Scheme_Rep_Contracts .json
JS_Array_ContractingParties RY et JS_Elem_ContractingParties id contractingParties p contract.signatories.contractingParties ro DC_ContractingParties
JS_Elem_ContractingParties RN id "" mp JS_Attr_FirstName,JS_Attr_LastName p "contract.signatories.contractingParties[n]" ro DC_ContractingParties_Party
JS_Attr_FirstName RA id firstName
JS_Attr_LastName RA id lastName
%T Taxa_Contracts
DC_ContractingParties cid ContractingParties kn DC_ContractingParties_Party
DC_ContractingParties_Party cid Party
Concept_Contract_Signatories CO brl Rep_Contract nrl Rep_Signatories nn "Models the association between contract and signatories"
id "" is the empty identifier that marks an AnonymousElement; the empty string must be quoted, as must the path containing [n], since [ is a delimiter (§3.3).
MCN can carry Mork.ttl itself, which is useful for placing the vocabulary’s formal structure (though not its prose) in a model’s context. Representative lines:
@b <http://www.nebularis.org/ontologies/Mork#>
@o <http://www.nebularis.org/ontologies/Mork>
@i <http://www.w3.org/2004/02/skos/core>,<http://www.w3.org/ns/shacl>,<http://www.w3.org/2003/11/swrl>,<https://www.nebularis.org/neuro-semantic/lattice/foundation/0.2.0>
!A mappingNote < skos:note lb "mapping note"
!O compositeNarrowerMapping < narrowerMapping lb "has composite narrower mapping"
!O compositeBroaderMapping < broaderMapping inv compositeNarrowerMapping
!O broaderApplicative < broaderMapping +AS
!O precedes +T dom DataMapping rng DataMapping
!O ^compositeBroaderMapping < precedes
!D weighting rng xsd:integer sd "confidence score out of 100"
!C DataMapping < skos:Concept & mappingScheme>MappingScheme
!C Datum = deferredMapping>DataMapping < DataMapping
!C UncertainMapping = hypothesisMapping>DataMapping | mappingRecommendation>xsd:string < DataMapping
!C ShapeMapping = DataMapping & generatesShapeDefinition>sh:Shape < GenerativeMapping
!C ShapeMapping < hasTargetingSpec>TargetingSpec & hasParameterBinding#1.. & hasConstraintProvenance>ConstraintProvenance
!DC OrderedCollection,SetListing,UnorderedCollection
!DP broaderMapping,closeMapping,deferredMapping,hypothesisMapping,narrowerMapping
!G broaderApplicative>DataMapping & ~exactRBoxMatch>.T < .N
!G broadABoxCategoryMatch>.T & compositeBroaderMapping>DataMapping & ~exactRBoxMatch>.T < .N
!I DraftMode : CompilationMode lb "draft mode"
!O ^compositeBroaderMapping < precedes is Axiom P1 (SubObjectPropertyOf(ObjectInverseOf(compositeBroaderMapping) precedes)), written with the inverse-property form of §10.5.
These are recommendations for the system prompt of a mapping agent that emits MCN; they are not part of the notation.
Mork.ttl is ~45,000 tokens; its definitions and scope notes belong in a retrieval store the agent consults when it needs the meaning of a term, not in every prompt.mappingNote that restates what lx :laysEggs already says wastes tokens twice (in generation and in every later read). Notes should carry the evidence — the reasoning, the source excerpt, the alternative rejected.Map_Loan costs a few more tokens than m3 but is far less likely to be mis-referenced three hundred tokens later.MD, not M+MD; write gs … and let MS be implied unless a polymorphic code precedes it.@u) and full headers when the MCN will be stored.cn from the parent or cb from the child, not both (§5).| Level | Requirements |
|---|---|
| MCN-Core | §3, §5, §6 (except @u), §7, §8, §9 (except payload strings), §11 !I and !T, §12, §13 (except payloads), §14.1 |
| MCN-Full | Core plus §10.1 (expressions), §10.5 and all of §11 (axiom lines), @u profiles, @opt inv |
| MCN-Artefacts | Full plus §10.2 (SWRL), §10.3 (SHACL), §10.4 (RML) payloads |
A decoder states which level it implements. A generator targets a level and MUST NOT emit constructs above it. This document’s examples in §16.1, §16.2 and §16.4 are Core; §16.3 and §16.5 are Artefacts.
Versioning of the notation follows the codebook: adding codes is a minor version; changing or removing the meaning of a code is a major version, and @v lets a decoder refuse a document written against a codebook it does not have.
EBNF for the line-level syntax. Sub-notation grammars are in §10.
document := line*
line := directive | block | axiom | exprline | nodeline | comment | empty
comment := '#' any*
directive := '@b' IRI | '@o' IRI IRI? | '@i' IRI (',' IRI)* | '@p' NAME '=' IRI
| '@t' NAME | '@u' NAME | '@opt' NAME+ | '@v' VERSION
block := ('%M' | '%T' | '%R' | '%O' | '%I' | '%K' | '%U') ident (RESERVED)? pair*
| '%X'
nodeline := ident typelist? pair*
exprline := ident '=' expr
typelist := typetok ('+' typetok)*
typetok := TYPECODE | CURIE | '+' ident
pair := code valuelist
code := PROPCODE | CURIE
valuelist := value annotation? (',' value annotation?)*
value := BARE | QUOTED ('@' LANG | '^' CURIE)? | IRI | inline
inline := '[' ( '#' ident )? ( typelist pair* | positional ) ']'
positional := slot+ pair* ; slot count fixed by the enclosing code
slot := value | '_'
annotation := '{' pair* '}' ; no whitespace before '{'
axiom := '!C' ident cclause* | '!O' PE oclause* | '!D' ident dclause* | '!A' ident aclause*
| '!I' ident (':' typelist)? pair* | '!G' CE '<' CE
| '!DC' ident (',' ident)* | '!DP' ident (',' ident)* | '!DI' ident (',' ident)* | '!SA' ident (',' ident)*
| '!T' term term term
cclause := '=' CE | '<' CE | '!' CE | pair
oclause := '<' PE | '=' PE | 'inv' PE | 'dom' CE | 'rng' CE | CHARS | 'chain' PE (',' PE)* | pair
dclause := '<' ident | '=' ident | 'dom' CE | 'rng' CE | '+F' | pair
aclause := '<' ident | 'dom' ident | 'rng' ident | pair
term := ident | BNODE | IRI | QUOTED ('@' LANG | '^' CURIE)?
ident := IRI | RESERVED | ':' NAME | CURIE | LOCALID
continuation: a physical line beginning with ';' is appended to the previous logical line
TYPECODE := [A-Z][A-Z]?
PROPCODE := [a-z][a-z]{0,2} | '>' | '<' | '~' | 's' [=~><-]
CHARS := '+' ('F'|'IF'|'S'|'AS'|'T'|'R'|'IR') ('+' ('F'|'IF'|'S'|'AS'|'T'|'R'|'IR'))*
LOCALID := [A-Za-z_][A-Za-z0-9_.-]*
CURIE := NAME ':' [^ \t,{}\[\]]+
RESERVED := '.' [A-Za-z]+
BNODE := '_:' [A-Za-z0-9]+
IRI := '<' [^>]* '>'
QUOTED := '"' ( [^"\\] | '\\' ["\\ntu] )* '"'
BARE := [^ \t,{}\[\]"<]+ [^ \t,{}\[\]]*
mork_schemas.py already exists for that path. An MCN document can be carried as a single string field in such a schema, which keeps token cost low while retaining the schema-level validation of the envelope; whether to retire the per-field Pydantic schema in favour of {"mcn": "…"} should be decided by measuring error rates, not assumed.@opt autoid that lets a bare integer subject mint B + "n" + digits would shave tokens further; §17 argues against it on reliability grounds, but the measurement has not been made.Mork.ttl. tools/mork/src/mcn_codebook.py is that machine-readable file (the tables in §8 were generated from it), and TestCodebookCoverage in test_mcn_decoder.py fails whenever a class, property or named individual is added to Mork.ttl without a corresponding code. Regenerating §8’s Markdown tables from the module on each change, rather than maintaining them by hand, remains open.