Cell-Level Legislative Grounding¶
Linking legislation to decision logic at decision-table cell granularity —
finer than DMN's native decision-level knowledgeSource, and finer than
CPRMV's rule-level extends/ruleType/confidence.
Shipped in v2026.08.0. See the Changelog for the release summary and Namespace & Property Reference for the property definitions.
Why cell granularity¶
A decision table rule is a row; each of its cells is a separate condition or outcome, and in practice each cell can rest on a different article. Take a rule that qualifies an applicant: one cell tests residence, another tests household composition, a third sets the resulting entitlement. Those three conditions routinely come from three different provisions.
The existing linking levels cannot express that:
| Level | Mechanism | Granularity |
|---|---|---|
| DMN native | <knowledgeSource> + authorityRequirement |
Whole decision |
| CPRMV | cprmv:isBasedOn on a cprmv:Rule |
Whole rule (row) |
| This | cprmv:hasPart → per-cell cprmv:Rule |
One cell |
Cell-level grounding adds the third row without replacing either of the first
two — a DMN keeps its knowledgeSource links, and rule-level isBasedOn
still emits as before.
The implementation spans three layers: attributes carried in the DMN file, the reader that extracts them, and the TTL emitter that publishes them.
Layer 1 — DMN attributes¶
Grounding travels inside the DMN file, on the <inputEntry> and
<outputEntry> elements themselves, so it survives any tool that round-trips
the XML. Two namespaces are declared on <dmn:definitions>:
<dmn:definitions
xmlns:cprmv="https://standaarden.open-regels.nl/standards/cprmv/0.4.1#"
xmlns:dct="http://purl.org/dc/terms/">
Each grounded cell then carries up to three attributes:
| Attribute | Meaning |
|---|---|
dct:source |
The annotation or concept id this cell was derived from |
cprmv:sourceQuote |
The quoted text fragment the grounding rests on |
cprmv:isBasedOn |
The citation — a full URL, or a bare JuriConnect (JCI) reference |
<inputEntry id="ie1a"
dct:source="apt-1"
cprmv:sourceQuote="Woonadres"
cprmv:isBasedOn="https://lokaleregelgeving.overheid.nl/CVDR1/1">
<dmn:text>"Amsterdam"</dmn:text>
</inputEntry>
Cells carrying none of the three are ungrounded, which is the normal case — wildcards and cross-decision references have nothing to cite.
Compound cells¶
A single cell can be a compound FEEL expression whose conjuncts each need their own citation. Two encodings were tested directly against Operaton: repeatable child elements were rejected outright (the DMN schema's unary-tests content model has no extension point for them), while a numbered attribute family deploys cleanly with no upper bound. That is what shipped:
<inputEntry id="ie2b"
dct:source1="concept-A"
cprmv:isBasedOn1="jci1.3:c:BWBR0002&artikel=9"
dct:source2="concept-B"
cprmv:sourceQuote2="tweede grondslag">
<dmn:text>>= 18 and < 67</dmn:text>
</inputEntry>
The reader scans dct:source1/cprmv:sourceQuote1/cprmv:isBasedOn1, then
…2, and so on, stopping at the first index where none of the three is
present.
Grounding attributes are inert to evaluation
They are extension attributes in foreign namespaces, so Operaton ignores them. This was confirmed empirically rather than assumed: after grounding was applied, the 100-case MC/DC suite still passed 100/100 against the redeployed model.
Layer 2 — Reading the DMN¶
src/utils/dmnHelpers.js extracts groundings while it parses the decision
table. extractCellGroundings(entryEl) returns an array of
{ source, sourceQuote, isBasedOn } — empty for an ungrounded cell, one entry
for the shorthand form, N for the numbered form. extractCell(entryEl) wraps
that together with the cell's own id and its FEEL text, and
extractRulesFromDMN attaches the results to each rule as inputEntries and
outputEntries.
The cell's id matters: it is the stable key the published cell URI is built
from. The iKnow re-export in this release gives every <inputEntry> and
<outputEntry> its own id for exactly this reason — keying off the id rather
than column position keeps URIs stable when columns are reordered. A
positional fallback covers sources that emit no per-cell ids.
The namespace-agnostic lookup¶
Building this layer surfaced a defect that predates cell grounding
entirely. Every selector-based DMN lookup in the codebase silently matched
nothing against a real, dmn:-prefixed file:
// Matches only elements in the null namespace — i.e. an unprefixed DMN.
xmlDoc.querySelectorAll('decision');
CSS type selectors match on the pair (namespace, local name), and an
unprefixed selector implies the null namespace. Against a real DMN — where
every element is dmn:decision — the selector returns nothing. Cell-level
grounding could never have worked, and neither did the pre-existing
rule-level export.
The fix is a pair of helpers that match on local name regardless of prefix:
function queryAllLocal(root, localName) { /* … */ }
function queryLocal(root, localName) { /* … */ }
Every DMN lookup now goes through them.
Layer 3 — Emitting the TTL¶
generateDmnSection() in src/utils/ttlGenerator.js emits a
cprmv:hasPart list of per-cell resources on each cprmv:DecisionRule:
<…/rules/DecisionRule_1> a cpsv:Rule, cprmv:DecisionRule ;
dct:identifier "DecisionRule_1" ;
cprmv:hasPart ( <…/rules/DecisionRule_1/cell/ie1a>
<…/rules/DecisionRule_1/cell/ie1b> ) .
URI scheme¶
| Resource | URI | cprmv:id |
|---|---|---|
| Cell | {ruleUri}/cell/{cellId} |
{ruleId}-cell-{cellId} |
| Compound sub-grounding | {cellUri}/grounding/{n} |
{ruleId}-cell-{cellId}-grounding-{n} |
| Minted concept | {serviceUri}/concepts/{sourceId} |
{sourceId} |
Two kinds of grounding¶
A cell with a sourceQuote grounds directly — the quote and its citation
are emitted on the cell resource:
<…/rules/DecisionRule_1/cell/ie1a> a cprmv:Rule ;
cprmv:id "DecisionRule_1-cell-ie1a" ;
dct:source <https://hva.pna-web.com/hva/?type=APT&id=apt-1> ;
cprmv:sourceQuote "Woonadres" ;
cprmv:isBasedOn <https://lokaleregelgeving.overheid.nl/CVDR1/1> .
A cell with only a dct:source points instead at a shared concept
resource, minted once and reused by URI wherever it recurs:
<…/rules/DecisionRule_1/cell/ie1b> a cprmv:Rule ;
cprmv:id "DecisionRule_1-cell-ie1b" ;
cprmv:isBasedOn <…/concepts/cpt-shared> .
<…/concepts/cpt-shared> a cprmv:Rule ;
cprmv:id "cpt-shared" ;
dct:source <https://hva.pna-web.com/hva/?type=CPT&id=cpt-shared> .
Deduplication is not cosmetic — a decision's own authorityRequirement and a
rule's output cell were verified to resolve to the same underlying concept, so
minting once and referencing by URI is what keeps the graph joinable.
A compound cell composes the two forms with a nested list:
<…/cell/ie2b> a cprmv:Rule ;
cprmv:id "DecisionRule_2-cell-ie2b" ;
cprmv:hasPart ( <…/cell/ie2b/grounding/1> <…/cell/ie2b/grounding/2> ) .
Citation resolution¶
cprmv:isBasedOn values are passed through when they are already full URLs,
and resolved against https://wetten.overheid.nl/ when they are bare
JuriConnect references — the same construction the rule-level emission already
used.
SHACL conformance¶
Live validation against a real published .ttl surfaced two RuleShape
violations. Both are fixed, and both are worth knowing about before extending
the emitter:
cprmv:id is mandatory on every cprmv:Rule (sh:minCount 1). Cell
resources originally emitted dct:identifier — a property RuleShape does
not check at all, so the constraint failed while the data looked fine — and
concept resources carried no identifier whatsoever. Every resource this layer
mints now emits cprmv:id, sub-groundings included.
cprmv:isBasedOn has sh:class cprmv:Rule, so its object must itself be
typed cprmv:Rule. Concept targets already were. A direct external citation
URI — CVDR, wetten.overheid.nl — was not, so every such reference failed the
shape. Each distinct citation URI now gets a minted, deduplicated stub:
<https://lokaleregelgeving.overheid.nl/CVDR1/1> a cprmv:Rule ;
cprmv:id "https://lokaleregelgeving.overheid.nl/CVDR1/1" .
The cprmv:id falls back to the URI itself — there is no other identifier
available for an external resource this graph does not otherwise describe.
No new SHACL shapes were needed: cell resources are ordinary cprmv:Rule
instances composed with the existing cprmv:hasPart, so the recursion is
covered by shapes that already exist.
Verification¶
- 12 tests in
src/utils/ttlGenerator.cellGrounding.test.jscover emission, concept dedup, compound nesting and the conformance rules; further tests indmnHelpers.test.jscover the reader. See Testing. - End-to-end against the real Amsterdam HvA model: grounding applied, deployed, evaluated, and published live through the editor with zero DMN evaluation issues and zero SHACL validation issues.
- The 100-case MC/DC suite passes unchanged after grounding, confirming the attributes stay inert to FEEL evaluation.
Open questions¶
- Cross-export id stability. Cell URIs key off
<inputEntry>/<outputEntry>ids. Whether those ids survive a fresh export from the authoring tool is not yet established — if they do not, republishing after a re-export would mint new cell URIs for unchanged logic. ruleType/rulesetTypeon cell resources. Flagged by the CPRMV spec owner as still under consideration; cell resources currently carry neither.- CPRMV
ReferenceMethod. CPRMV 0.4.2 introducesReferenceMethodas a pluggable concept with JCI and ELI as known methods, rather than mandating one citation grammar. The emission here predates it and resolves JCI directly; aligning withReferenceMethodis a future change. See External Standards.