- Rust 50.3%
- JavaScript 23.6%
- Vue 13.8%
- Gherkin 7.2%
- Python 2%
- Other 3.1%
* feat(demo): opgenomen rondleiding met stem, dia's en veelgestelde vragen
Een rondleiding door de demo voor wie zonder presentator kijkt. De demo
staat er als video in, met de stem van de presentator; de dia's links
blijven echte tekst en zijn na de opname nog aan te passen. Optioneel de
presentator zelf in een rondje, ondertitels, hoofdstukken, uitgeschreven
tekst, en veelgestelde vragen die op het juiste moment verschijnen en een
eigen opgenomen antwoord hebben.
- Opnemen: `just walkthrough-record` opent de demo met een opnamepaneel.
Het neemt het tabblad op (bijgesneden tot de werkruimte), de microfoon,
de webcam en een log van dia's, klikken en typen. Alleen in de
dev-server, alleen vanaf deze machine; niet in de productiebundel.
- Nabewerken: `just walkthrough prepare|build|export` (Python via uv):
ruis eruit, transcriptie met woordtijden, voorstel voor knippen,
knippen op hele frames, ondertitels, en een deelbare MP4.
- Afspelen: `/rondleiding` (`/en/tour`), met bediening uit het design
system en "Probeer het zelf" naar de live demo.
- De video's staan niet in git maar in een GitHub-release; de
Docker-build haalt ze op en controleert de checksum.
Er staat nog geen opname in; zonder timeline.json toont de demo geen knop.
* fix(demo): minder knoppen in de bediening van de rondleiding
Speelsnelheid, ondertitels, de uitgeschreven tekst, de presentator in beeld en terug naar de demo staan onder één menu. De knoppen voor tien seconden terug en vooruit zijn weg (j en l blijven werken), en 'Naar de demo' valt samen met 'Probeer het zelf'. Onder de bediening staat nog één knop, afhankelijk van waar de kijker is: terug uit een vraag, of de live demo in.
* fix(demo): rondleiding neemt het hele venster op, in zaalmodus
Een uitsnede van de werkruimte naast de rail liet zijpanelen en dialogen buiten beeld: die staan tegen de rand van het venster, en de wettenlijst opende over de rail. De opname is nu het hele tabblad, in zaalmodus zoals de demo in een zaal gegeven wordt; de speler zet de diatekst er weer naast. Het opnamepaneel verdwijnt tijdens de opname, de tabtitel toont ● REC. Op een dia over het hele scherm krijgt de bediening dezelfde marge als de voettekst van het dek.
* feat(demo): rondleiding speelt zich af in de echte demo, niet als video
De rondleiding is geen video van de demo meer. Je hoort de stem van de
presentator, en ondertussen doet de demo zelf wat de presentator deed:
het dek wisselt van dia en opent tabbladen, knoppen worden aangeklikt en
tekst verschijnt letter voor letter, in de live app. Een cursor beweegt
mee. Pauzeren geeft de demo aan de kijker; verder afspelen zet de
opgenomen toestand terug.
- Opnemen legt per actie vast welk element het was (tekst, label, link,
door de shadow roots van het ontwerpsysteem heen), niet waar op het
scherm. Een opname op de ene vensterbreedte speelt af op een andere;
nagemeten van 1100 tot 1920 pixels, met de rail ernaast.
- Afspelen wacht op een element dat nog laadt, en de stem wacht mee. De
klok staat op het moment van opnemen, zodat bedragen en termijnen
kloppen met wat er gezegd wordt.
- Springen zet de toestand van het hoofdstuk terug en speelt de acties
tot het doel snel af. De eigen sessie van de kijker wordt niet
overschreven.
- De nabewerking levert de stem los, de acties (een actie in een knip
verhuist naar de knip in plaats van te verdwijnen) en een toestand per
hoofdstuk. Een telefoon krijgt de opname van het venster als video.
* fix(demo): wettenlijst naast de rail en de bevindingen uit de review
Een paneel dat links opent (de wettenlijst op het tabblad Wetten) stond
tegen de vensterrand, en met het dek als rail dus over de dia's, ook in
zelfstandig-modus. Het ontwerpsysteem heeft daar geen haak voor; tot die
er is zet de demo een regel in de shadow root van die componenten die het
paneel, en het inschuiven, naar de rand van de demo verplaatst. Een test
faalt zodra de interne klassenamen veranderen.
Uit de review van de afspeelmotor:
- een actie die nog wachtte, liep na een sprong, na verlaten of na
overnemen door de kijker niet meer door in de demo;
- verlaten in het eerste hoofdstuk startte de rondleiding opnieuw;
- de ruimere zoektocht naar een element klikt geen willekeurige knop meer,
en pas na wachten;
- springen speelt geen acties meer af die al in de toestand zaten;
- een lopende presentatie geeft haar toetsen af, de datum op de titeldia
volgt de klok weer, en een taalwissel vervangt de opgenomen dia's niet;
- geen sneltoetsen van één letter meer (WCAG 2.1.4); spatie, pijltjes en
Escape horen tijdens het afspelen bij de speler, ook als de focus nog in
een veld staat waar de rondleiding in typte.
* feat(demo): hoofdstukken van de rondleiding uit een script, met gegenereerde stem
Een hoofdstuk kan nu ook uit tekst komen in plaats van uit een opname. Het
script is tekst met markeringen ("Ik open [1] de lijst met wetten"), naast
een stille opname met de klikken en het typen. De stem wordt per regel
gegenereerd (ElevenLabs, met een kloon van de eigen stem; om te proberen de
eigen stem van de Mac), met de tijd van elk teken, en elke handeling valt
op het woord na haar markering. Duurt een handeling langer dan de woorden,
dan wacht de volgende regel.
Opgenomen en gegenereerde stukken staan op één tijdlijn: een opgenomen
opening met de presentator in beeld, daarna de hoofdstukken uit het
script. Het rondje verdwijnt waar de gegenereerde stem begint, en naast de
bediening staat dat die stem met AI is gemaakt (AI-verordening, art. 50).
Een ondertitel breekt waar een opgenomen stuk overgaat in een gegenereerd.
Daarbij een fout uit de bestaande code: met meer dan één opname kwam de
stem uit het verkeerde bestand.
* feat(demo): twee soorten antwoord op een veelgestelde vraag
Een oordeel neemt de presentator in beeld op; zonder demo erbij staat het gezicht dan groot naast de vraag. Een laat-maar-zien-antwoord komt uit een script met de gegenereerde stem en de demo die meeklikt. Tijdens elk antwoord staat de vraag zelf als titel op de dia's, in plaats van de dia waarop het antwoord is opgenomen.
* feat(demo): van opname naar script, en publiceren van de rondleiding
walkthrough script maakt uit een opname een concept-script per dia: de zinnen die gezegd zijn, met een markering waar elke handeling begon. Wie een hoofdstuk opneemt zoals in een presentatie, praten en klikken tegelijk, krijgt zo zijn eigen woorden terug om door de gekloonde stem te laten voorlezen, met de klikken waar ze waren. Een script kan een stuk van een langere opname beslaan.
walkthrough voices toont de stemmen op het ElevenLabs-account, walkthrough publish zet de media in een GitHub-release (en vraagt dat eerst) en schrijft de tag in timeline.json. De docs hebben het draaiboek van opname tot release.
* feat(demo): de rondleiding neemt zoomen en slepen in de graaf op
Slepen en zoomen in de graaf laten geen aangeklikt element achter, dus het actielog zag ze niet. De graaf meldt nu waar hij naar kijkt (het midden van het vlak in graafcoördinaten plus de zoom), tien keer per seconde tijdens het bewegen en nog eens in rust; de rondleiding stuurt hem daar met een korte glijbeweging heen. Omdat het een punt in de graaf is en geen pixelpositie, landt het ook op een ander schermformaat goed.
Daarnaast: alles van de rondleiding gaat via één recept (just walkthrough record, prepare, check, script, build, publish, status, test), en just walkthrough check zegt per opname hoe hij klinkt: luidheid, oversturing, ruis, spreektempo en de gelogde handelingen, met een oordeel per regel.
* chore(dev): de rondleiding via één just-recept
just walkthrough record en just walkthrough test in plaats van losse recepten; de rest gaat door naar de pipeline. Hoort bij de vorige commit (het pad heet Justfile, met hoofdletter).
* feat(demo): microfoontest in het opnamepaneel
Voor een opname kies je de microfoon en zie je het spraakniveau live, met een oordeel: geen geluid, te zacht, goed of te hard. Gemeten als spraakniveau (RMS) en niet als piek: de eerste echte testopname piekte op -15 dB terwijl de stem rond -40 zat, veel te zacht. Een opname gebruikt de gekozen microfoon.
* feat(demo): rondleiding houdt bij wat hij niet terugvindt
Een handeling waarvan de replay het element niet vindt, komt in een lijst (en in de console tijdens ontwikkelen): dat is wat er na een wijziging in de demo opnieuw opgenomen moet worden. Een opname bewaart ook de naam van de microfoon, zodat een te zachte opname te herleiden is.
* fix(demo): menu's van de rondleiding openen naar boven
De bediening staat onderaan het scherm. Floating UI krimpt een menu dat onder te weinig ruimte heeft in plaats van het altijd om te klappen, waardoor het menu onder '...' de tweede keer afgesneden werd. Beide menu's openen nu naar boven.
* feat(demo): taalmodel kijkt het transcript van een opname na
Whisper verstaat beleidstaal verkeerd. Na het transcriberen krijgt een
taalmodel de tekst met de woordenlijst en de diateksten erbij, en geeft
dezelfde spraak terug met de verhaspelingen verbeterd. De verbeterde tekst
wordt letter voor letter teruggelegd op de tijden van Whisper, zodat
ondertitels en acties op hun plek blijven.
* feat(demo): nakijken van het transcript ziet ook wat er op het scherm stond
Het taalmodel krijgt schermbeelden uit de opname (bij elke routewissel,
elke klik en om de zes seconden) en de labels van waarop geklikt is. Een
woord dat in beeld staat, zoals bsn, wint dan van wat Whisper meende te
horen.
* feat(demo): cameratest in het opnamepaneel
Een ronde voorvertoning zoals de kijker het bolletje ziet, een keuze van
de camera en een oordeel over het licht, zodat achtergrond en licht voor
de opname goed staan. De opname gebruikt de gekozen camera.
* fix(demo): rondleiding houdt de taal van de kijker en loopt niet vast
- De knoppen van de speler blijven in de taal van de kijker; alleen de
demo eronder draait in het Nederlands, zoals de stem.
- De pagina startte de rondleiding twee keer (mount en activate), waardoor
de taal van de kijker verloren ging en er twee replays liepen.
- Een sprong is meteen eigenaar van de events: afspelen tijdens een sprong
past geen events van de oude positie meer toe.
- De klok loopt door op wandtijd zolang de stem stilstaat, zodat ids op
Date.now() niet botsen.
- Ontbrekende media geven de pagina 'nog geen rondleiding' in plaats van
een eeuwige spinner, zonder herstart-lus.
* fix(demo): dia's en formaat uit de openingsopname; .walkthrough/.env in de docs-check
De rondleiding nam de dia's en de beeldgrootte van de alfabetisch eerste
opname, niet van het eerste segment. De docs noemen het lokale
sleutelbestand, dat niet in git staat; de padcontrole kent het nu.
* feat(demo): voorpagina en presentatietab zeggen wat elke ingang is
De voorpagina had drie gelijke knoppen zonder uitleg; nu zijn het drie
kaarten (rondleiding, zelf rondkijken, presenteren) met in één regel wat
je krijgt. De presentatietab scheidt zelf presenteren (modus en toetsen)
van kijken zonder presentator, met de dia's eronder. De uitleg van de
zaalmodus is ingekort tot twee zinnen.
* fix(demo): rondleiding vindt velden met een zelfgemaakte id en toont het gezicht groot in de opening
- Een id die een component bij elk renderen verzint (nldd-field-input-<uuid>)
zegt niets over het veld; de locator laat hem weg, ook in opnames die hem
nog dragen. De standaardpremie in de simulatie speelt nu terug.
- Op een volle dia met camera (de opening) staat het gezicht groot naast de
dia, zoals al bij de vragen; zodra de demo opent krimpt het.
- Afspelen of springen terwijl de rondleiding nog laadt, wordt niet meer
ongedaan gemaakt door de start.
* feat(demo): eerste opname van de rondleiding, opening tot en met de simulatie
Opname van 3 oktober, 12 minuten, met camera. Geknipt: stiltes, een
verspreking, het zoeken naar de vergelijking in de simulatie en het einde
na het doorrekenen van de standaardpremie. De media staan in de release
walkthrough-2026-10-03. Mijn overheid, het zaaksysteem en de afsluiting
volgen in een volgende opname.
* fix(demo): ondertitels leesbaar in dark mode
De tekstkleur kwam uit een primitief token van het ontwerpsysteem, en die
zijn light-dark()-paren: wit werd zwart in dark mode, op een vast zwart
vlak. Ondertitel, rand van het bolletje en cursor hebben nu vaste kleuren.
* fix(demo): rondleiding scrolt naar dezelfde regel, op elk scherm
Een scrollpositie in pixels klopt alleen in de layout van de opname; waar
de wettekst of de trace anders afbreekt, landt hij ergens anders. De
recorder legt nu vast welke regel er midden in beeld stond, en de replay
zet die regel terug op dezelfde plek. Scrollvlakken binnen componenten van
het ontwerpsysteem (shadow DOM) worden nu ook opgenomen, en de rustpositie
van een scrollbeweging altijd. Voor oudere opnames meet `walkthrough
anchors` de ankers achteraf; de opname van 3 oktober heeft ze nu.
* fix(demo): terug naar een opname na een hertake begint in de staat van dat moment
Een stuk dat na een opnieuw opgenomen hoofdstuk terugkeert naar de eerste
opname, zette de demo terug naar het begin van die opname. Nu is het de
momentopname van de dia waar het stuk begint.
* fix(demo): terugkeer naar een opname speelt eerdere handelingen niet opnieuw af
Na een hertake begon het stuk uit de eerste opname met alles wat daar
vóór het teruggezette moment gebeurde. Nu alleen wat na de dia van de
momentopname kwam.
* feat(demo): rondleiding met de opnieuw opgenomen wet en kortere diateksten
De wet (dia 6) komt uit een nieuwe opname waarin het scrollen door de
YAML is vastgelegd; daaromheen de opname van 3 oktober. De diateksten
van de rondleiding volgen kort wat er gezegd wordt; de zaal-deck blijft
zoals hij is.
* feat(demo): skill voor de rondleiding en walkthrough verify
De walkthrough-skill zegt hoe je na een opname te werk gaat: verwerken,
beoordelen, knippen op zinsgrenzen, een hoofdstuk opnieuw opnemen en
aansluiten, wat te doen als de replay iets niet nadoet, en publiceren.
`walkthrough verify` speelt elk hoofdstuk headless terug in twee
vensterformaten en noemt elke handeling die niet gevonden werd.
* ci: rondleiding speelt terug als controle bij elke wijziging aan de demo
Een wijziging in de demo kan een opgenomen klik of scroll zonder doel
laten, en dan laat de rondleiding iets anders zien dan de stem zegt. De
job 'Rondleiding speelt terug' bouwt de engine, start de dev-server en
speelt elk hoofdstuk headless terug in twee vensterformaten; een handeling
die haar element niet vindt laat hem falen, met het hoofdstuk erbij. Een
handeling die alleen losser nog iets vindt is een waarschuwing. Hij draait
bij wijzigingen aan frontend-demo/, corpus/demo/, de engine of het schema
en hangt aan de Test-poort. Lokaal is het hetzelfde commando: just
walkthrough verify (nu in Node, de Python-variant is weg). De installatie
van wasm-bindgen staat in een gedeeld script voor deze job en e2e.
* ci: job 'Rondleiding speelt terug' en het gedeelde wasm-bindgen-script
Hoort bij de vorige commit; de wijziging aan ci.yml was daar niet in
meegekomen. De job draait bij wijzigingen aan de demo, het demo-corpus,
de engine of het schema en hangt aan de Test-poort; e2e installeert
wasm-bindgen nu via script/install-wasm-bindgen.sh.
* feat(demo): korte ondertitels in plaats van letterlijke spreektaal
Spreektaal maakt lange ondertitels. `walkthrough subtitles` laat een
taalmodel per ondertitel een korte versie schrijven (zonder vulwoorden en
herhalingen, met begrippen en getallen zoals gezegd) in subtitles.yaml,
een lijst van wat gezegd is en wat er in beeld komt, met de hand te
corrigeren. De build toont ze, ook in de uitgeschreven tekst. 125 van de
207 ondertitels van de rondleiding zijn nu korter.
* fix(demo): padcontroles kennen de map voor gegenereerde hoofdstukken
corpus/demo/walkthrough/script/ ontstaat pas als een hoofdstuk met een
gegenereerde stem wordt gemaakt; de docs en de walkthrough-skill noemen
hem al. Beide controles kennen hem nu, met de reden erbij.
* feat(demo): rondleiding pauzeert om de kijker zelf te laten rondkijken
Zegt de presentator "kijk gerust even zelf rond" of "ga je gang", dan
stopt de speler aan het eind van die zin, met een coach mark
(nldd-just-in-time-education) bij de afspeelknop, die dan Verder heet. De
build vindt die zinnen in het nagekeken transcript met een vaste lijst
uitdrukkingen en legt de pauzes door de knippen heen; walkthrough.yaml kan
er een toevoegen of weghalen, of de herkenning per opname uitzetten.
Gaat de kijker tijdens het afspelen zelf klikken, dan wacht de speler met
dezelfde coach mark ("Ga je gang"), en zegt hij desgewenst een korte zin
in de eigen stem (bumpers.wait); bij Verder een tweede (bumpers.resume)
terwijl de demo wordt teruggezet. Die zinnen zijn stukjes uit een opname,
eigen audiobestanden die meegaan in de release.
* fix(demo): rondleiding vindt elementen terug na de update van het ontwerpsysteem
Na 0.8.94 en de nieuwe demowetten misten twee opgenomen klikken in de
graaf. Een verzonnen name (nldd-segmented-2) telt nu net als een verzonnen
id niet mee; een klik op een onderdeel dat het component niet meer rendert
gaat bij een lossere match naar het component zelf; en de hele tekst van
een pagina laat een lossere match vallen. Beide tellen als waarschuwing
in walkthrough verify.
* fix(demo): zorgtoeslag en awb openen in de stand waarin ze verteld worden
De leeftijdstoets in de zorgtoeslag begint dichtgeklapt als 'subject: AGE',
zodat de twee voorwaarden als twee korte blokken te lezen zijn. Bij awb/bezwaar
staan de twee lijsten onder definitions uitgeschreven en beginnen input en
output dicht, als aantal.
De opgenomen rondleiding klapte reference_date met de hand dicht; die klik
vervalt, want de knoop staat nu al dicht.
* fix(demo): escape in een open paneel stopt de presentatie niet meer
Escape sloot het tracepaneel en beeindigde in dezelfde toetsaanslag de
presentatie. In een opname verdwenen daardoor alle dia's daarna. Een open
dialog of popover houdt nu zijn eigen Escape, en tijdens een opname stopt
Escape het dek helemaal niet; stoppen gaat daar met Shift+R.
* feat(demo): rondleiding verdraagt een opname met het dek uit
Een presentator die halverwege het dek uitzet en via de tabbladen verder
loopt, leverde een opname op die niet terugspeelde. Wat er nu kan:
- hoofdstukken achteraf: `chapters:` onder een opname in walkthrough.yaml
geeft een stuk zonder dia's zijn eigen dia's en hoofdstukken;
`just walkthrough states` legt de stand van de demo bij elk daarvan vast
door de rondleiding een keer in echte tijd af te spelen
- `actions:` onder een opname zet handelingen erbij die al gedaan waren
toen de opname begon
- de speler zoekt een element niet meer op een id dat bij elke run anders
is (data-run, een zaak-id) of op een toestand van het moment (data-focused)
- een klik op een tabblad dat naast het dek verborgen is, volgt de link
- een opgenomen 'Demo resetten' haalt het dek niet meer weg
- `off: true` bij een pauze werkte niet: YAML leest `off` als false
- versneld naspelen wacht op een knop die nog uitgeschakeld is, en de
terugspeelcheck speelt een hoofdstuk met een gemiste handeling nog een
keer op opnametempo voor het telt, zodat een trage machine niet rood geeft
- ondertitels worden in genummerde blokken ingekort, zodat een samengevoegde
regel niet alles daarna verschuift
Op een klein scherm speelde de rondleiding als video van het opgenomen
venster. Die toont de dia's van de opnamedag en niets waar het dek uit
stond. De video wordt niet meer gemaakt; een klein scherm krijgt uitleg en
een verwijzing naar een groot scherm.
* feat(demo): rondleiding opnieuw opgenomen, met vier vragen en pauzes
De doorloop van 11 oktober in een keer: 20:41, vijftien hoofdstukken. De
laatste zes zijn achteraf afgeleid, omdat het dek vanaf de simulatie uit
stond. Drie pauzes waar de kijker wordt uitgenodigd zelf rond te kijken,
vier vragen met een opgenomen antwoord (open normen, juridisch bindend,
de volgorde van artikelen, of de vertaling klopt) en de tussenzinnen voor
als de kijker zelf gaat klikken. Ondertitels ingekort.
* fix(demo): rondleiding scrolt naar het eind waar de presentator dat deed
Een scroll helemaal naar beneden werd teruggespeeld op het anker in het
midden van het beeld. Bij een lange wettekst is dat de alinea zelf, en waar
die anders afbreekt belandde de kijker halverwege in plaats van bij de
parameters eronder. Een scroll tot het eind (of het begin) landt nu daar.
* feat(demo): rondleiding toont de demo op de breedte van de opname
In een venster smaller dan dat van de opname braken regels anders af en
schoof wat de presentator aanwees uit beeld. De demo naast het dek wordt nu
verkleind tot de opgenomen breedte, met een ondergrens van 70 procent; het
dek houdt zijn grootte.
Een opgenomen Escape sluit het paneel waarin hij gedrukt werd. Een nagemaakte
toetsaanslag zet het sluiten door de browser niet in gang, waardoor de trace
open bleef staan.
|
||
|---|---|---|
| .claude | ||
| .github | ||
| bdd | ||
| conformance | ||
| corpus | ||
| corpus-poc | ||
| deploy/nginx | ||
| dev | ||
| docs | ||
| frontend | ||
| frontend-demo | ||
| frontend-lawmaking | ||
| frontend-poc-napp | ||
| frontend-poc-nieuwkomersbekostiging | ||
| frontend-poc-portal | ||
| frontend-poc-terugbetaalregimes | ||
| LICENSES | ||
| packages | ||
| pocs | ||
| schema | ||
| script | ||
| tests/fixtures/federation | ||
| .dockerignore | ||
| .env.example | ||
| .env.poc.example | ||
| .env.sso-local.example | ||
| .gitignore | ||
| .npmrc | ||
| .pre-commit-config.yaml | ||
| .yamllint | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| CONTRIBUTING.md | ||
| corpus-registry.yaml | ||
| deny.toml | ||
| docker-compose.dev.yml | ||
| Justfile | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| publiccode.yml | ||
| README.md | ||
| REUSE.toml | ||
| REVIEW.md | ||
| rust-toolchain.toml | ||
regelrecht
Machine-readable Dutch law execution. regelrecht takes legal texts, encodes them as structured YAML, and runs them as deterministic decision logic.
What does it do
- The engine takes a regulation and a set of inputs, evaluates the decision logic, and returns a result with a full explanation trail
- Laws are tested against real-world scenarios using BDD (Gherkin) tests, many derived from legislative explanatory memoranda. The vocabulary those scenarios speak lives in
bdd/grammar.yamland every engine's step bindings are generated from it - A harvester downloads and tracks Dutch legislation from the official BWB repository
- Regulations can be edited through a web UI with live execution preview
Components
Rust packages
| Package | Description |
|---|---|
| packages/engine/ | Law execution engine (also compiles to WASM) |
| packages/law-model/ | The Rust representation of the law format, conforming to schema/ |
| packages/harvester/ | Downloads Dutch legislation from BWB |
| packages/pipeline/ | PostgreSQL job queue for law processing |
| packages/admin/ | Harvester-admin API; its dashboard UI lives in the editor |
| packages/editor-api/ | Backend API for the law editor |
| packages/corpus/ | Git integration for the regulation corpus |
| packages/auth/ | Shared OIDC/SSO authentication |
| packages/github/ | Shared GitHub REST client |
| packages/shared/ | Shared domain types across crates |
| packages/arch-extract/ | Derives the architecture model from the code (just arch-explore) |
| packages/tui/ | Terminal dashboard (Ratatui) |
Frontends and sites
| Directory | Description |
|---|---|
| frontend/ | Law editor UI (Vue 3 + Vite) |
| frontend-lawmaking/ | Law-making process visualization (Vue 3 + Vite) |
| packages/frontend-shared/ | Shared frontend primitives (auth, colour scheme, API fetch) |
| docs/ | Astro site: landing page + documentation |
Data and testing
| Directory | Description |
|---|---|
| corpus/regulation/ | A working set of ruleworks, some of them synthetic (test_*) |
| schema/ | Versioned JSON schema for the law format (schema/latest points at the current version) |
| bdd/ | The Gherkin vocabulary (grammar.yaml) and the engine-conformance suite |
corpus/regulation/**/scenarios/ |
Law-validation scenarios, next to the law they test |
| packages/grafana/ | Grafana monitoring dashboards (provisioning, not a crate) |
The full corpus lives elsewhere
corpus/regulation/ in this repo holds a couple of dozen laws: the ones the
tests and BDD scenarios run against, plus synthetic test_* laws for the
conformance suite. The harvested body of Dutch legislation is far too large to
carry here and lives in its own repository:
MinBZK/regelrecht-corpus — currently some 22,000 YAML files under regulation/nl.
Which branch you want depends on what you are after:
| Branch | What is on it |
|---|---|
development |
The working branch, and what corpus-registry.yaml points this repo at. Harvested laws land here first |
main |
Only moves when development is merged into it, so it lags |
enrich/* |
Output of the enrichment runs, one branch per enricher (enrich/claude, enrich/opencode) |
Short-lived prN and editor/* branches belong to preview deployments and
editor sessions; they are not meant to be read directly.
Deployed services
| Service | URL |
|---|---|
| Editor | https://editor.regelrecht.rijks.app |
| Landing page | https://regelrecht.rijks.app |
| Documentation | https://docs.regelrecht.rijks.app |
| Law-making | https://lawmaking.regelrecht.rijks.app |
| Harvester admin | https://harvester-admin.regelrecht.rijks.app |
| Grafana | https://grafana.regelrecht.rijks.app |
The pipeline API and the harvester and enrich workers deploy alongside these but have no web UI of their own.
A PR gets a preview environment once it carries the deploy:preview label; taking the label off, or closing the PR, cleans it up again.
Getting started
Prerequisites: Rust (rustup installs the version pinned in
rust-toolchain.toml) and just. The dev stacks
below also need Docker and Node.js, and on x86_64 Linux mold.
just check # everything CI runs (format, lint, build, validate, tests)
just test # every Rust test in the workspace
just test-no-docker # the same, minus the container-backed suites
just bdd # the BDD suites: law scenarios and engine conformance
just test and just check start PostgreSQL containers for the crates that
need one, so they want a Docker daemon. Without one, just test-no-docker
covers everything else. just bdd runs separately: it executes against the real
corpus and is deliberately not part of just test.
Faster builds (recommended)
Run just dev-setup once. It does three things:
- Points every worktree at one shared target dir — a new worktree reuses the already-built dependency graph instead of cold-building ~600 crates.
- Puts that target on fast local storage when the repo lives on a slow mount.
A Rust build writes tens of thousands of small files; on a 9p/NFS/SMB mount
(e.g. a WSL2 or Docker-Desktop dev container where the repo is a Windows
drive) that I/O dominates build time — often more than everything else
combined.
dev-setupdetects this and relocates the target to~/.cache/regelrecht/(local disk). This is usually the single biggest win. - Installs mold +
sccache. mold (wired intopackages/.cargo/config.toml) speeds up linking and is a hard requirement for the dev recipes;sccacheis installed but left off locally.
sccache is disabled locally because it requires CARGO_INCREMENTAL=0 and so
disables incremental compilation, which slows every rebuild after an edit.
Enable it only for cold or flag-varying builds:
export RUSTC_WRAPPER=sccache CARGO_INCREMENTAL=0
CI uses both mold and sccache (see .github/workflows/ci.yml).
Running a dev stack
just dev # editor (7300) + lawmaking (7500), editor-api on 8000, admin API on 8001
just dev editor # just the editor (editor-api + editor UI + DB)
just dev admin # just the admin API + DB
just dev lawmaking # just the lawmaking UI (no backend)
just dev-down # stop it
just dev starts only what the chosen app needs: no Grafana, Prometheus or
workers. The editor runs with real SSO against the central Keycloak, so it
needs .env.sso-local (copy .env.sso-local.example).
Vite ports default to 7300/7500 (overridable via EDITOR_PORT /
LAWMAKING_PORT). When a native backend can't reach Postgres
on localhost (e.g. a WSL2/Docker-Desktop dev container, where Postgres is
published on the Docker host), point it at host.docker.internal: for the
admin API set DB_HOST=host.docker.internal in .env; for the
editor that host comes from DATABASE_URL in .env.sso-local (the
.env.sso-local.example already uses host.docker.internal).
See the docs site for full development instructions.