Context
Problem. Version selection filters on the lower bound only (valid_from <= calculation_date,
newest wins - RFC-001 §5, RFC-003). There is no end date. So:
- The most recent version of any law is treated as valid forever. A law can only “end” by being
superseded - never by expiring or being repealed.
- Real laws do end without a successor, in two ways:
- intrinsic - the law’s own horizonbepaling: CEK23 “vervalt met ingang van 1 januari 2027”
(art. 8.1 lid 2);
- extrinsic - another instrument ends it: the Wet op de openluchtrecreatie was gefaseerd
ingetrokken per 1 januari 2008; the Twm covid-19 measures vervielen per 20 mei 2022 after the
Eerste Kamer rejected the goedkeuringswet for the fifth verlenging (17 mei 2022, dossier 36.042).
- Dependents break silently (“verweesde regelingen”). A regulation whose delegation basis
disappears can no longer have effect (old Aanwijzing 243: van rechtswege vervallen; since 2018
Aanwijzing 6.24 prescribes explicit mede-intrekking instead - Stb. 2018, 362 executes it to the
letter). Example: the Tijdelijke regeling maatregelen covid-19 was vervallen per the same date as
the Twm. In regelrecht’s
source.regulation model this is a dangling reference: the engine keeps
computing on no-longer-valid law - a silent wrong answer.
Scope:
valid_from/valid_to describe instrument validity (is the text in force?) - not the
substantive scope a norm governs (e.g. CEK23 caps calendar-2023 deliveries), which stays in
the rule’s own conditions and temporal metadata (RFC-001 §8).
- Article repeal is out of scope: repealing a single article is an ordinary amendment, already
expressed by version succession (new consolidation → new version of the same
$id, newest-wins).
See Alternatives.
- Resolving an ended/changed law on purpose (eerbiedigende werking, statische verwijzing,
ex tunc) is RFC-020 (date-aware resolution,
as_of). RFC-019 covers only the global
calculation_date.
Decision
Add a law-level valid_to, give version selection an inclusive upper bound, and report expired
references honestly.
Grounding - the legislation this RFC is built on
| Instrument | Vindplaats | Role in this RFC |
|---|
| Subsidieregeling bekostiging plafond energietarieven kleinverbruikers 2023 (CEK23) | BWBR0047628 | intrinsic end (art. 8.1 lid 2); worked example A; real corpus law |
| Wet op de openluchtrecreatie | BWBR0006548 · EK 29.829 | extrinsic end: whole wet ingetrokken per 1-1-2008 |
| Tijdelijke wet maatregelen covid-19 + Tijdelijke regeling | BWBR0044337 · BWBR0044416 | the limits case (example B): KB-determined, per-provision end; verweesde regeling |
| Besluit Stb. 2018, 362 (Fokkerijbesluit/-regeling) | stb-2018-362 | Aanwijzing 6.24 in practice: grondslag vervalt (art. 2) + explicit mede-intrekking (art. 3) |
| Wet open overheid art. 10.2f | BWBR0045754 | open end date (“Hoofdstuk 6 vervalt bij koninklijk besluit”) - beyond the static boundary |
| Aanwijzingen voor de regelgeving 6.24/6.25 (old 243) | BWBR0005730 | drafting conventions for vervallen/intrekking (besluit van de Minister-President, not substantive law) |
| BWB consolidation metadata | einddatum per toestand | harvester source for valid_to |
1. valid_to: instrument end date
$id: subsidieregeling_bekostiging_plafond_energietarieven_kleinverbruikers_2023
bwb_id: BWBR0047628
valid_from: '2022-12-15'
valid_to: '2026-12-31' # art. 8.1 lid 2: "vervalt met ingang van 1 januari 2027" → laatst van kracht 31 dec 2026
- Optional law-level field, mirroring
valid_from (RFC-001 §5).
- Inclusive: the last calendar day the law is in force, matching BWB’s
einddatum
(“geldend t/m”). A statutory “vervalt met ingang van D” is exclusive → valid_to = D − 1.
- Only for genuine termination (the law vervalt or is ingetrokken):
- not for ordinary succession - the next version’s
valid_from already ends the old one;
- a law repealed and replaced by a different
$id still gets valid_to (newest-wins cannot
end it).
- Two provenance modes for the date:
- intrinsic - the law’s own vervalbepaling (CEK23 art. 8.1 lid 2). The only mode RFC-019
implements.
- extrinsic - the date lives in another instrument (intrekkingswet, verlengings-KB, or open:
Woo art. 10.2f lid 1). A cross-law “terminates” mechanism that derives the date from that
instrument is future work; until then the harvested literal
valid_to is a denormalised value
with the terminating instrument as source of record.
- Boundary: validity is static metadata. This RFC never executes a law to decide whether
another law is in force:
- selection runs before execution, so derived validity has an ordering problem (you would have
to execute a law to know which version is selectable);
- a terminates-effect therefore binds early: at harvest time today (BWB already processed the
terminating instrument into the final consolidation’s
einddatum), at load/index time in
the future mechanism - never at execution time (unlike overrides/implements, which resolve
values, not validity);
- load-time derivation matters for moving end dates: the Twm verlengings-KBs shifted the verval
repeatedly; harvest-time literals would need a corpus rewrite on every change.
- Harvester rule: write
valid_to from BWB einddatum only when the consolidation is the
final one and the einddatum is finite (≠ the 9999-12-31 “still in force” sentinel). An
intermediate consolidation’s einddatum is mere succession.
2. Version selection gains an upper bound
- Among versions with
valid_from <= calculation_date, select the newest (unchanged).
- If that version has a
valid_to and calculation_date > valid_to: the law is not in force
→ return nothing. No fall-through to an older version - a repealed law does not resurrect a
prior version.
In force iff valid_from <= calculation_date <= valid_to (inclusive). Additive: laws without
valid_to behave exactly as today.
3. Expired references: honest diagnostics
A reference whose target is not in force resolves to nothing - and the engine reports only what
the data proves (RFC-015):
| Cause | The engine states | Must NOT state |
|---|
$id absent from corpus | ”regeling <id> niet gevonden" | "geen grondslag” |
all versions valid_from > calculation_date | ”geen versie van <id> van kracht op <datum> (nog niet in werking)“ | - |
selected version valid_to < calculation_date | ”geen versie van <id> van kracht op <datum>; laatst geldig t/m <valid_to>" | "vervallen zonder opvolger” |
- Never “geen grondslag / zonder opvolger”: eerbiedigende werking, a statische verwijzing or an
alternative grondslag may keep the law alive (→ RFC-020).
- The resolver exposes a typed reason (
NotFound / NotYetInForce / EndedOn(date)); execution
maps it to typed errors (LawNotFound / LawNotYetInForce / LawEnded with reference date and
valid_to).
- Fatal vs. continue: a required reference that fails is an error (fail-fast,
RFC-012); an optional one (
required: false, RFC-003) yields a
null/taint that propagates like UNTRANSLATABLE, recorded on the CrossLawReference trace node
(RFC-007, RFC-013).
- The receipt’s
loaded_regulations (RFC-013) gains valid_to next to
valid_from: the full validity window.
- A
valid_to filtering case joins the Temporal conformance level (RFC-014).
Worked examples
A. Intrinsic end - Prijsplafond energie 2023 (CEK23, BWBR0047628)
Real corpus law, harvested from BWB:
corpus/regulation/nl/ministeriele_regeling/subsidieregeling_bekostiging_plafond_energietarieven_kleinverbruikers_2023/2022-12-15.yaml.
Modeled is what the articles literally vaststellen: plafondtarieven (art. 2.2), volumeplafonds
(art. 2.3) and the vervaldatum (art. 8.1).
$id: subsidieregeling_bekostiging_plafond_energietarieven_kleinverbruikers_2023
regulatory_layer: MINISTERIELE_REGELING
bwb_id: BWBR0047628
valid_from: '2022-12-15'
valid_to: '2026-12-31' # art. 8.1 lid 2: "vervalt met ingang van 1 januari 2027"
# (exclusive) → laatst van kracht 31 december 2026 (inclusive)
articles:
- number: '2.2' # plafondtarief - "wordt vastgesteld op: € 0,24755 per kWh" (lid 1) etc.
machine_readable:
execution:
# euro-per-unit rates (EUR/kWh, EUR/m3(n), EUR/GJ), typed `number`: the corpus
# `amount` convention is whole eurocents and type_spec has no tariff units yet
output:
- { name: plafondtarief_elektriciteit, type: number }
- { name: plafondtarief_gas, type: number }
- { name: plafondtarief_warmte, type: number }
actions:
- { output: plafondtarief_elektriciteit, value: 0.24755 }
- { output: plafondtarief_gas, value: 0.85734 }
- { output: plafondtarief_warmte, value: 47.38 }
- number: '2.3' # volumeplafond - "wordt vastgesteld op: 2.900 kWh ... per jaar" etc.
machine_readable:
execution:
output:
- { name: volumeplafond_elektriciteit, type: number }
# ... gas (1200), warmte (37.0)
- number: '8.1' # slotbepaling
machine_readable:
execution:
output:
- { name: vervaldatum, type: date } # lid 2: "vervalt met ingang van 1 januari 2027"
actions:
- { output: vervaldatum, value: '2027-01-01' }
- End date grounded in art. 8.1 lid 2, full text: “Deze regeling vervalt met ingang van
1 januari 2027, met dien verstande dat zij van toepassing blijft op subsidies die voor die datum
zijn verstrekt.” Art. 8.1 outputs the vervaldatum verbatim (the grounded source); the top-level
valid_to is the denormalised inclusive last day (vervaldatum − 1). Deriving it automatically
from the article output is the static-metadata boundary of §1.
- The staartzin is eerbiedigende werking - the RFC-020 bridge case: the regeling stays
applicable to subsidies granted before 2027 even after its instrument end.
valid_to correctly
records the instrument end; §3 ensures the engine states only “no version in force on
<datum>”, never “no longer applicable”. Resolving this regeling for a pre-2027 subsidie at a
post-2026 date is exactly RFC-020’s as_of case.
- Verified with the
evaluate binary:
date: 2023-06-01 → plafondtarief_elektriciteit: 0.24755, volumeplafond_elektriciteit: 2900, vervaldatum: 2027-01-01;
date: 2027-06-01 → fails with the data fact: no version in force on 2027-06-01, last in force
until 2026-12-31 (§2, §3).
- Faithfulness: the regeling is a subsidie aan leveranciers (art. 2.1 lid 1); the supplier
subsidy formulas of art. 3.1-3.5 (volume-capped verbruik × (contracttarief − plafondtarief), plus
TUK, minus brutomarge-correctie) are not modeled - they need bijlage data and supplier-level
aggregates. No article computes a consumer-facing “korting”. The cap’s substantive scope
(calendar 2023) is separate from the instrument validity (text in force until 2027).
B. The limits case - dynamic, KB-determined, per-provision end (Twm)
The Tijdelijke wet maatregelen covid-19 shows what a static law-level valid_to deliberately does
not cover (§1 boundary):
- The end date is not in the law. Art. VIII lid 1: the inserted Hoofdstuk Va Wpg vervalt
three months after entry into force; lid 3 lets a KB extend it - or designated parts - by at most
three months at a time (“steeds ten hoogste drie maanden”). After repeated verlengings-KBs the
measures lapsed per 20 mei 2022, when the goedkeuringswet for the fifth verlenging was rejected.
Authoring
valid_to: '2022-05-19' would be invented - not grounded in the Twm’s own text.
- The verval is per-provision. Parts not extended lapsed separately (e.g. the
avondklok-grondslag earlier); a single law-level date is too coarse.
- The clean, grounded intrinsic case is CEK23 (example A): one literal vervaldatum for the whole
regeling.
C. Expired dependent - orphaned reference
- Regulation R reads an output of law B (
source: { regulation: B, output: … }); B carries
valid_to.
- At a
calculation_date after B’s end: B resolves to nothing, R yields no outcome, and execution
reports “geen versie van B van kracht op <datum>; laatst geldig t/m <valid_to>” - instead of
computing on the repealed B.
- Tested end-to-end in
features/einddatum.feature (test laws test_einddatum /
test_einddatum_afnemer).
Testing
New cucumber-rs scenarios in features/einddatum.feature (test laws mirroring
test_untranslatables):
- valid_to upper bound: resolves before and on the (inclusive) end date; fails with the data
fact after it (no fall-through).
- expired cross-reference: dependent R references ended law B; after B’s
valid_to, R yields no
outcome and the error states the fact (§3).
Plus unit tests on the selection rule (SelectionReason cases) and, as follow-up, a valid_to
case in the Temporal conformance level (RFC-014).
Why
Benefits
- Expresses laws that end. Twm, CEK23 and WOR become representable; the latest version is no
longer eternal.
- No silent wrong answers. A reference to an ended law surfaces honestly.
- Small and additive. One field + an upper bound + an honest diagnostic; laws without
valid_to are unchanged.
Tradeoffs
- Authoring discipline:
valid_to only for genuine ends (mirroring RFC-003’s explicit
valid_from convention for implementing regulations).
- Receipt/conformance churn: one new field, one new case.
Alternatives Considered
valid_to on every version (mirroring the next version’s valid_from). Rejected: on
ordinary succession the law text contains no end date - it would be invented, violating
groundedness. Newest-wins already ends the old version implicitly.
- Article-level
valid_from/valid_to. Rejected: repealing a single article is an ordinary
amendment, already expressed by version succession. Stb. 2018, 362 art. 2 (“Artikel 76 van de
Gezondheids- en welzijnswet voor dieren vervalt met ingang van 1 november 2018”) materialises as
the Gwwd’s 2018-11-01 consolidation with art. 76 as a “Vervallen” placeholder; a post-repeal
lookup fails with the OutputNotFound data fact, while historical dates still resolve the older
version.
- Engine infers “repealed/no successor” and reports it. Rejected: violates
RFC-015 (zero domain knowledge) and is unprovable - eerbiedigende werking or an
alternative grondslag may keep the law alive.
- Fall through to an older version once the latest has ended. Rejected: repeal does not
resurrect a prior version; if an earlier version still applies, that is RFC-020’s explicit
date-aware case, not an implicit fallback.
Implementation Notes
- Schema: additive law-level
valid_to; new minor schema version. Literal dates only (no
#-references): the engine reads valid_to as static metadata, so a reference would be silently
ignored.
- Engine:
ArticleBasedLaw.valid_to; inclusive upper bound + no-fall-through in
select_version_for_date; typed SelectionReason mapped to the §3 errors; malformed calculation
dates rejected up front (no selection bypass); receipt records valid_to.
- Harvester:
valid_to from BWB einddatum, only for a final consolidation with a finite
einddatum.
References
- RFC-001 YAML Schema · RFC-003 IoC/temporal filter ·
RFC-007 Cross-Law Execution · RFC-012 Untranslatables ·
RFC-013 Execution Provenance · RFC-014 Conformance ·
RFC-015 Engine Policy
- RFC-020: date-aware reference resolution (
as_of) - eerbiedigende werking, statische
verwijzing, ex nunc/ex tunc, lex mitior
- Legislation: see the Grounding table above. Aanwijzingen voor de regelgeving: official text
BWBR0005730, deep links via
kcbr.nl - 6.24 (vervallen uitvoeringsregelingen; explicit mede-intrekking
since 2018), 6.25 (uitgewerkte regelingen), old 243 (pre-2018: van rechtswege vervallen)
- Glossary of Dutch Legal Terms