# Architectuur Notary.AI

Notarisassistent voor het kantoor te Tervuren: berekeningsmodules (schenk- en
erfbelasting, ereloon/aktekosten), opzoekingsmatrix vastgoed en regelgevingbeheer.
Next.js App Router; alle modulepagina's zijn client components.

## Einddoel

> **Ruim einddoel (2026-07-04):** Notary.AI voert autonoom de volledige
> dossierketen uit — van intake tot en met de formaliteiten ná de akte — tot aan
> de wettelijke **ambtelijke-voorbehoudgrens** (verlijden/wilscontrole blijft de
> notaris). De capaciteitslagen-architectuur, het gefaseerde stappenplan en de
> werven per Claude-model staan in **`AUTONOMIE-ROADMAP.md`**. De hieronder
> beschreven architectuur is **laag 4** (generatie/berekening) van dat kader —
> de rijpste laag en de motor waar de gouden-paden-backlog op werkt.

Notary.AI evolueert naar een autonome, ervaren notariële medewerker die — op
basis van de door de notaris aangeleverde (bron)documenten — zelfstandig de
ontwerpdocumenten opstelt (authentieke akte of onderhands document), de
bijbehorende modelmails klaarmaakt en de afrekeningen berekent (aktekosten/
ereloon én schenk-/erfbelasting), telkens als werkdocument dat de notaris
naleest en valideert.

Het governend principe verzoent validatie en flexibiliteit via **twee
regimes**: *genereren/berekenen/opzoeken* levert enkel werkdocumenten en mag
volledig vrij en autonoom; *muteren van de gedeelde kantoorbibliotheek* loopt
altijd via een wijzigingsvoorstel ('Te valideren'), nooit rechtstreeks (zie
`AGENTS.md`).

De **MCP-/API-laag** (`app/api/*` → `lib/mcp/*`, gegenereerd uit
`lib/openapi.ts`) is de manier waarop externe AI-agenten (Claude, M365 Copilot)
dit einddoel uitvoeren: zij vragen modellen/brieven op en laten ze invullen,
berekenen afrekeningen en dienen bibliotheekwijzigingen enkel als voorstel in.
Het stappenplan dat een agent hierbij volgt staat machineleesbaar in
`data/werkwijze.ts` (operationId `haalWerkwijze`) — zie ook de tabel hieronder.

## Stappenplan naar het einddoel (voor Claude, de bouwer)

Elke fase dient het einddoel; latere fases bouwen voort op de vorige. Vink een
fase pas af zodra tests/tsc/eslint/build slagen en de wijziging is gepusht.

| Fase | Status | Inhoud |
| --- | --- | --- |
| 0 | ✅ | Twee-regimes-principe + einddoel vastgelegd in `AGENTS.md`, hier, `lib/openapi.ts` (info.description) en `lib/mcp/protocol.ts` (initialize-instructions). |
| 1 | ✅ | Kernlus: modelbrieven opvragen/invullen, modeldocumenten opvragen/invullen (ontwerp incl. facultatieve onderdelen), aktekosten/ereloon en schenk-/erfbelasting berekenen, verplichte vastgoedopzoekingen raadplegen. |
| 2 | ✅ | Orkestratie: `haalWerkwijze` (`data/werkwijze.ts`) beschrijft in welke volgorde en samenhang een agent de tools uit fase 1 combineert tot een volledig werkdossier, voor een concreet dossier op basis van brondocumenten. |
| 3 | ◧ | Bibliotheekdekking + afrekening-lus: meer aktetypes/modeldocumenten en modelbrieven, enkel via `voegModelonderdeelVoorstelToe` / `voegModeldocumentVoorstelToe` — inhoudelijk werk, geen architectuurwijziging. Stap A (kredietakte/`hypotheek` activeren in de rekenmodule) uitgevoerd; zie "Uitgewerkt stappenplan" hieronder. |
| 4 | ◻ | Kwaliteitslus: gevalideerde voorstellen (en eventuele correcties van de notaris op gegenereerde ontwerpen) systematisch terugkoppelen naar de bibliotheek, zodat ontwerpen na verloop van tijd minder nazicht vergen. |
| 5 | ✅ | Observability: `lib/gebruikslog.ts` (operationId `lijstGebruikslog`) registreert onbekende modeldocument-/modelbrief-/rechtshandeling-id's en genegeerde facultatieve onderdelen — geen persoonsgegevens — om fase 3/4 te prioriteren. |

Bewust **buiten scope** van Notary.AI zelf (blijft taak van de connecterende
AI-agent): het lezen/begrijpen van brondocumenten, het bepalen van het
aktetype en het extraheren van parameterwaarden. Notary.AI levert de
data/bereken-/sjabloonlaag; de agent levert het documentbegrip. Dit grijpt
niet in op `AGENTS.md`'s twee-regimes-principe — fase 3/4 lopen altijd via een
wijzigingsvoorstel, nooit rechtstreeks.

### Uitgewerkt stappenplan voor de open fases

De architectuur is af; wat rest is **inhoudelijk werk**. De volgende stappen
zijn incrementeel en grotendeels parallel (A/B/C/E); D is de strategische
sprong naar échte autonomie en bouwt voort op een gevulde bibliotheek.

- **A — Afrekening-lus sluiten (rekenmodule).** Elk courant aktetype moet een
  afrekening opleveren. `hypotheek` (kredietakte) is volledig geïmplementeerd
  in `kiesBarema`/`getRetributies`/`berekenHeffing` en sinds deze stap
  `actief: true` — de afrekening wordt nu in eigen huis berekend (indicatief,
  notaris.be blijft kruiscontrole). Resterend: `ruil` en `hypotheek-mandaat`
  evalueren; per geactiveerd type een `berekenAkteKostenOverzicht`-test.
- **B — Bibliotheekdekking, gestuurd door observability (fase 3).** Vul
  ontbrekende modeldocumenten/-brieven via `voegModeldocumentVoorstelToe` /
  `voegModelonderdeelVoorstelToe`, geprioriteerd door `lijstGebruikslog` (welke
  onbekende akte-/brief-id's agenten effectief opvragen). Voor de hand liggende
  gaten: kredietakte/hypotheekvestiging, basisakte/statuten mede-eigendom,
  verdeling-uit-onverdeeldheid, huwelijkscontract, aangifte van nalatenschap.
- **C — FR-pariteit systematisch afdwingen.** ✅ Gedaan: de generieke test
  `data/modeldocumenten/nl-fr-pariteit.test.ts` bewaakt voor élk onderdeel/
  document met een FR-spiegel (`vertalingVanId`) gelijke variant-ids/volgorde/
  categorie/`toepasbaarOp`/modelstructuur, plus de uniciteit van alle
  clausule- en model-ids. `detecteerPariteitsGaps`/`genereerPariteitsVoorstellen`
  (`data/modeldocumenten/pariteit.ts`) bouwen automatisch wijzigingsvoorstellen
  voor gaten. Schrijf géén documentspecifieke pariteitstests meer
  (`beschrijving-onroerend-goed-parity.test.ts` blijft enkel bestaan voor wat
  uniek is aan dié verplichte clausule: bestaan + basishypotheses). Resterend
  inhoudelijk werk: ontbrekende FR-spiegels aanvullen (zie `HUIDIGE_GATEN`).
- **D — Kwaliteitslus (fase 4).** Zet gevalideerde voorstellen én
  notaris-correcties op gegenereerde ontwerpen om in nieuwe/gewijzigde
  modelonderdeel-voorstellen, zodat ontwerpen na verloop van tijd minder
  nazicht vergen. Startpunt: een mechanisme dat het gevalideerde verschil
  tussen "gegenereerd ontwerp" en "definitieve akte" als `Wijzigingsvoorstel`
  in 'Te valideren' plaatst. Bestaande haken: `data/verbetervoorstellen.ts`,
  `lib/gebruikslog.ts`.
- **E — Modelmail-dekking gelijktrekken met aktedekking.** Voor elk gedekt
  aktetype hoort de modelmailketen te bestaan (eerste contact → ontwerp →
  afrekening → ondertekening → na akte); vul aan, gestuurd door
  `bepaalModelmailCategorieen` + `lijstGebruikslog`.

Volgorde-afhankelijkheid: A/B/C/E zijn los inzetbaar; D bouwt voort op B/C.
Elke stap pas afvinken als `tsc`/`eslint`/`vitest`/`build` slagen en de
wijziging is gepusht.

### Architectuur-roadmap voor opschaling (tientallen gouden paden)

Geprioriteerde structurele werven, elk **claimen in `WERVEN.md`** vóór de
start: ze herwerken bestanden waar de inhoudelijke integratie-agents
gelijktijdig in werken, dus timing/afstemming is deel van de opdracht. Kleine
bewakende tests die deze risico's intussen indammen bestaan al:
`data/modeldocumenten/registratie.test.ts` (barrel-volledigheid),
`nl-fr-pariteit.test.ts` (pariteit + unieke ids) en
`data/ereloon-contract.test.ts` (afrekeningsketen per akteType).

1. **Declaratieve parameter-mapping.** ✅ Gedaan:
   `lib/dossier/parameter-mapping.ts` bevat de tabel
   (`DOSSIER_PARAMETER_MAPPING`: bron-accessor → NL-namen + FR-namen +
   formaat, waarbij taalgebonden formaten zoals datums en rangtelwoorden
   automatisch per taal wisselen) en de engine (`pasParameterMappingToe`).
   `dossierNaarParameterwaarden` houdt enkel nog échte afleidingslogica
   (waarborg-default + saldo, oppervlakte-vergelijking, identiteitsblokken,
   vertaalde enums). Een nieuw dossierveld mappen = één tabelregel, NL en FR
   in dezelfde beweging. De volledigheidsguard hoort ín de bron-accessor
   (undefined = niet invullen), nooit in de engine.
2. **Kenmerken-engine veralgemenen buiten vastgoed.** ◧ Grotendeels gedaan:
   `lib/dossier/kenmerken-regels.ts` levert het generieke, declaratieve
   "feit → variant"-raamwerk (`KenmerkRegel`-tabellen + `pasKenmerkRegelsToe`,
   drieledige true/false/onbekend-semantiek, FR-spiegel inbegrepen); de
   schenking-clausules en de domeinoverstijgende akte-clausules lopen erover,
   en `schenkingKenmerkenUitDossier` (parameters.ts) leidt de kenmerken uit de
   dossierfeiten zelf af. Een nieuw gouden pad levert voortaan enkel een
   regeltabel + een afleidingsfunctie aan. De enkel-kenmerk-blokken van
   `heldereTaalKeuzes` (gezinswoning, zonnepanelen + groenestroom-
   certificaten, elektrische keuring, syndicus-info) zijn gemigreerd naar
   `HELDERE_TAAL_REGELS` (mét sleutel-volledig getypte variantentabellen).
   Resterend (bewust imperatief): de multi-kenmerk-logica (gewest × goedType,
   EPC, asbest-voorrang, renovatieplicht, onbekend→"open"-varianten) past
   niet in het één-kenmerk-regelmodel — pas migreren als het regelmodel ooit
   samengestelde condities krijgt (aparte ontwerpkeuze, niet mechanisch).
3. **Gedeelde akteType-union.** ✅ Gedaan: `data/modeldocumenten/akte-types.ts`
   is het centrale register (`AKTETYPES` as const → union `AkteTypeNaam` +
   type guard `isGekendAkteType`). `AkteTypeKeuze`, `UitbouwKandidaat` en
   `GoudenPadScenario.verwachtAkteType` gebruiken de union (typo = compile-
   fout); `Modeldocument.akteType` blijft bewust `string` (gebruikersmodellen
   uit IndexedDB zijn runtime-data) en wordt voor de seed-bibliotheek bewaakt
   door `akte-types.test.ts`. Nieuw akteType = één regel in het register.
4. **`Dossier` subtyperen per dossiertype.** ✅ Gedaan: `lib/dossier/types.ts`
   groepeert de velden per pad (`DossierKern` + `OnroerendGoedVelden` /
   `VerkoopVelden` / `OverdrachtsbelastingVelden` / `NalatenschapVelden` /
   `VennootschapVelden`) en `Dossier` is een discriminated union op
   `dossiertype` (Verkoop-/Schenking-/Nalatenschap-/Vennootschaps-/
   TestamentDossier). Schrijven van een pad-vreemd veld is een compile-fout;
   lezen op het brede type blijft werken (pad-vreemde velden zijn `?: never`
   en lezen als `undefined`). Narrowen doe je met de type guards
   (`isVerkoopDossier`, `heeftOnroerendGoedVelden`, …); bouwen vanuit een
   runtime-dossiertype met `maakLeegDossier`/`DossierVanType`. De generieke
   intake-merge (`voegIntakeVeldenSamen`, `maakDossierUitIntakeResultaat`)
   blijft bewust de ene veld-agnostische runtime-grens.
5. **Barrel-registratie automatiseren.** ✅ Gedaan:
   `scripts/genereer-barrels.mjs` genereert `onderdelen/index.ts` én
   `modellen/index.ts` deterministisch uit de bestandslijst (alfabetisch;
   exportnaam gescand uit het bestand zelf — precies één
   `export const …Onderdelen`/`…Modellen` per bestand is het contract, anders
   faalt de generator luid). Draait automatisch vóór elke
   dev/test/poort/build-run (pre-hooks in `package.json`); een nieuw
   themabestand toevoegen aan de map volstaat. Wijzigt de generator de barrel,
   dan weigert `npm run duw` te pushen tot ze mee gecommit is.
   `registratie.test.ts` blijft als vangnet voor runs buiten de pre-hooks om.

### Nieuwe verbeteringen — voorstel, getagd per model (2026-07-04)

Onderstaande punten zijn nog niet geclaimd of uitgevoerd; ze vullen de roadmap
hierboven aan met wat tijdens de lopende clausule-integratie als knelpunt naar
boven kwam. Elk punt draagt een modeltag — het model dat de taak het
efficiëntst aankan, van mechanisch/scherp-afgebakend (**Fable 5**, goedkoop en
snel, dus eerst ingepland) over gewone feature-/refactorwerk (**Sonnet 5**) tot
architecturale ontwerpkeuzes met veel onderlinge afwegingen (**Opus 4.8**,
enkel inzetten waar de meerprijs dat rechtvaardigt). Claim vóór de start in
`WERVEN.md`, zelfde regels als de roadmap hierboven.

**Fable 5 — mechanisch, scherp afgebakend, geen ontwerpkeuzes:**

6. **Barrel-codegen voor `onderdelen/index.ts`.** ✅ Gedaan — zie roadmap
   punt 5 hierboven (`scripts/genereer-barrels.mjs`, dekt ook
   `modellen/index.ts`).
7. **Conflictbestendige opslag voor de clausule-wachtrij.** ✅ Gedaan:
   `integratie/clausule-wachtrij.ndjson` (NDJSON, één object per regel,
   stabiel gesorteerd op `id`, vaste veldvolgorde) + `merge=union` in
   `.gitattributes`. Werkinstructies, het canonieke update-recept en de
   duplicaat-ontdubbelregel (zelfde regel aan beide kanten gewijzigd → meest
   gevorderde status behouden) staan in `integratie/INTEGRATIE-CLAUSULES.md`.
8. **Build-time schema-guard op de seed-`Modelonderdeel`/`Modeldocument`-data.**
   ✅ Gedaan: `data/modeldocumenten/schema-guard.ts` (pure
   `valideerSeedBibliotheek` + `assertSeedBibliotheek`, geen nieuwe
   dependency) draait bij het inladen van de datalaag (`index.ts`) en laat
   dev/test/build meteen falen op lege verplichte velden, dubbele
   onderdeel-/model-/variant-ids, onderdelen zonder inhoud, lege
   wetsbasis-vermeldingen en hangende verwijzingen (`vertalingVanId`,
   structuur → onbestaand onderdeel). `schema-guard.test.ts` bewaakt de
   geldigheid van de echte bibliotheek én de detectie per probleemklasse.
9. **Ratchet-test voor `agentwenken`/`toelichting`-dekking.** ✅ Gedaan:
   `data/modeldocumenten/agentwenken-ratchet.test.ts` telt de onderdelen met
   > 2 varianten zonder `agentwenken` als exact plafond (start: 127 van 149,
   2026-07-04) dat enkel mag dalen; de foutmelding stuurt in beide richtingen
   (regressie → wenken invullen bij het nieuwe onderdeel; verbetering →
   plafond mee verlagen). De `toelichting`-zustertaak is niet machinaal
   meetbaar (alleen de bron weet of er een toelichting was) en blijft een
   heropname-instructie in `AGENTS.md`.

**Sonnet 5 — gewoon feature-/refactorwerk, matige ontwerpruimte:**

10. **`react-hooks/set-state-in-effect`-schuld in `app/ereloon/page.tsx`
    wegwerken.** Al gedocumenteerd als bekende schuld hierboven (Conventies);
    reset-`useEffect`s vervangen door afgeleide state of een reducer, met de
    bestaande tests als vangnet.
11. **`lib/gebruikslog.ts` laten doorstromen naar een concrete werklijst.**
    Vandaag registreert het gebruikslog enkel; een periodieke
    samenvattingsfunctie die de top-N onbekende id's omzet in een
    voorgesteld `HUIDIGE_GATEN`/`WERVEN.md`-item zou Fase 4 (kwaliteitslus)
    dichter bij "automatisch" brengen.
12. **Implementatie van het parallelclausule-mechanisme** (zie
    `integratie/PARALLELLE-CLAUSULES.md`) ná het ontwerp door Opus 4.8
    hieronder: de `parallelMetIds`/`parallelVingerafdrukken`-velden op
    `Modelonderdeel` daadwerkelijk toevoegen en de bijhorende ratchet-test
    schrijven.

**Opus 4.8 — architecturale ontwerpkeuzes met veel onderlinge afwegingen:**

13. **Ontwerp van het parallelclausule-mechanisme.** ✅ Gedaan (ontwerp + kern):
    `data/modeldocumenten/parallelle-clausules.ts` bevat de beslissingen en de
    bewaakte primitieve. De vingerafdruk-afweging is beslecht met een
    GENORMALISEERDE hash (`berekenParallelVingerafdruk`): opmaak-markers,
    `[...]`-commentaren en placeholders eruit, witruimte samengevouwen,
    verkleind — stabiel onder cosmetische edits, gevoelig voor elke
    woordwijziging (incl. artikelnummer/decreetdatum). Drift wordt symmetrisch
    gedetecteerd (`detecteerParallelDivergentie`): elk onderdeel bewaart de
    vingerafdruk van elke partner zoals laatst nagekeken
    (`parallelVingerafdrukken`); wijkt de actuele af, dan faalt de ratchet-test
    (`parallelle-clausules.test.ts`) met een hersteltip. Velden
    `parallelMetIds`/`parallelVingerafdrukken` toegevoegd aan `Modelonderdeel`.
    Nog géén paren gekoppeld (test dus neutraal). Resterend (punt 12, Sonnet):
    het knipperlicht in `samenstellen.ts` en het koppelen van de eerste paren +
    vingerafdrukken — zie `integratie/PARALLELLE-CLAUSULES.md`.
14. **Discriminated union voor de werkdossier-output** (`werkdossier.ts`,
    `modelmails.ts`, `afrekening.ts`, `Onzekerheid`-types) — het
    uitvoer-analogon van roadmap-punt 4 (`Dossier` subtyperen). Pas aanpakken
    ná voltooiing van punt 4, anders dubbel werk; de afweging welke velden
    gedeeld blijven versus per pijler splitsen raakt de kernarchitectuur en
    verdient het hoogste redeneerniveau.

### Optimalisatie per gouden pad — modeltoewijzing (voorstel, vervolg, 2026-07-04)

`lib/dossier/gouden-paden.ts` registreert vandaag 7 gouden paden. Per pad meet
het scenario zelf al hoe ver het van het einddoel staat (`heeftMails`,
`heeftAfrekening`, `frModelId` aanwezig of niet). Onderstaande punten dichten
die per-pad gaten in de **einddoel-prioriteitsvolgorde uit `AGENTS.md`**
(ontwerp → FR-spiegel → detectie → mails → afrekening), en voegen twee
mechanismen toe die de hele registerlaag — dus élk huidig én toekomstig pad —
sterker maken. Claim vóór de start in `WERVEN.md`.

**Fable 5 — mechanisch, geen ontwerpkeuzes:**

15. **Generiek dekkingsrapport per gouden pad.** ✅ Gedaan:
    `berekenGoudenPadVolwassenheid` (`lib/dossier/gouden-pad.ts`) rangschikt
    alle geregistreerde paden naar afstand tot het einddoel (meeste
    pijler-gaten eerst, tie-break op einddoel-prioriteit van het eerste gat,
    dan op open keuzes) en telt de gedekte pijlers op als voortgangsteller.
    `gouden-pad-volwassenheid.test.ts` bewaakt de rangschikking als levende
    werklijst én ratchet (gedekte pijlers mogen enkel stijgen — dicht je een
    pijler-gat, verhoog het plafond mee in dezelfde commit).

**Sonnet 5 — contentwerk volgens de einddoel-prioriteit:**

16. **`verkoopakte-woning-fr` opbouwen (hoogste prioriteit — FR-spiegel).**
    ✅ Gedaan: het model `verkoopakte-woning-fr` bestond al volledig (eerdere
    clausule-integratiebatches hadden elk onderliggend onderdeel al een
    `-fr`-tegenhanger gegeven, structuur en variant-id's identiek aan de
    NL-bron, bewaakt door `nl-fr-pariteit.test.ts`) — het was enkel nog niet
    gekoppeld in `lib/dossier/gouden-paden.ts` (`frModelId` ontbrak), dus het
    gouden-pad-register en `gouden-pad-volwassenheid.test.ts` zagen het pad
    nog als onvolledig. Nu gekoppeld met een eerste, op het echte gedrag
    gemeten ratchet (`maxOpenVeldenFr: 291`, `maxOpenKeuzesFr: 30` — hoger dan
    de NL-cijfers 282/25, wat op zich geen regressie is: structuur en
    variant-id's lopen 1-op-1 gelijk, de motor genereert voor het FR-model
    louter meer open plekken op basis van dezelfde dossierinput). Het pad
    `verkoopakte` telt voortaan als volledig (alle vier pijlers gedekt);
    `gouden-pad-volwassenheid.test.ts` is mee opgeschoven (30 → 31 gedekte
    pijlers).
17. **Testament-mailketen (mails — twee paden delen één gat).** ✅ Gedaan:
    nieuwe `BriefCategorie "testament"` (`data/modelbrieven/types.ts`),
    gewired in `bepaalBasisCategorieen` (`lib/dossier/modelmails.ts`) voor
    zowel `keuzetestament` als `testament-gezinswoning`, en een eerste
    modelbrief-set NL+FR in `data/modelbrieven/brieven/testament.ts`: eerste
    contact (wensen/begunstigden/uitvoerder ophalen), uitnodiging tot
    ondertekening (afspraak + getuigen-hypothese) en een mail na de akte
    (registratie in het Centraal Register van Testamenten, herroepbaarheid).
    Beide gouden paden staan nu op `heeftMails: true`; enkel de afrekening
    (punt 18) blijft nog open. `bepaalUitbouwDekking`/`uitbouw.test.ts` en
    `gouden-pad-volwassenheid.test.ts` zijn mee opgeschoven.
18. **Afrekening activeren voor statutenwijziging-bv en de twee
    testamentpaden.** Drie paden hebben `heeftAfrekening: false`. Volg het
    precedent van de al doorgevoerde hypotheek-activering (`kiesBarema`/
    `berekenHeffing`/`getRetributies` in `data/ereloon.ts`, `akteMeta`-entry
    met `actief: true`): een vennootschapsbarema voor `statutenwijziging-bv`,
    en het vaste KB 1950-testamenttarief voor `keuzetestament` en
    `testament-gezinswoning`.
19. **`heeftMails`/`heeftAfrekening` berekenen in plaats van manueel
    bijhouden.** ✅ Gedaan: de twee auteur-onderhouden booleans zijn van
    `GoudenPadScenario` geschrapt; de mail- en afrekeningpijler komen nu
    rechtstreeks uit de motor-meting (`GoudenPadMeting.heeftMails`/
    `heeftAfrekening`, al gemeten door `beoordeelGoudenPad` via de volledige
    werkdossier-run — sterker dan de oorspronkelijk voorgestelde losse
    aanroepen). `berekenGoudenPadVolwassenheid` aggregeert voortaan over
    `GemetenGoudenPad` (scenario + meting); de bewuste omslag van een pijler
    bewaakt de volwassenheids-ratchet (`gouden-pad-volwassenheid.test.ts`,
    rangschikking + `gedektePijlers`), die met de meting live meebeweegt.

**Opus 4.8 — escalatie, niet de standaardkeuze:**

20. **Juridisch subtiele restvertalingen bij punt 16.** Voor élke bij de
    FR-opbouw van de verkoopakte ontdekte, nog onvertaalde clausule waarbij
    een letterlijke vertaling de juridische betekenis zou kunnen verschuiven
    (vgl. de eerder in deze roadmap gedocumenteerde bronafwijkingen tussen
    NL- en FR-cellen), gaat de FR-formulering naar Opus 4.8 — niet naar
    Sonnet 5. Zelfde escalatiepatroon als het parallelclausule-ontwerp
    (punt 13): de uitvoerder mag zelf niet inschatten of een clausule
    "subtiel" is, dus bij twijfel escaleren.

### Nieuwe gouden (sub-)paden — modeltoewijzing (2026-07-04, bijgewerkt)

`UITBOUW_KANDIDATEN` (`lib/dossier/uitbouw.ts`) somt 19 nog te bouwen
rechtshandelingen op — allemaal **nieuwe `Dossiertype`'s**. Er bestaat
vandaag al een ander, kleiner soort uitbreiding die er niet in past: het
**sub-pad** — dezelfde `Dossiertype` (bv. `verkoop-met-krediet`), maar met
een wezenlijk ander ontwerp/afrekening/mailtraject naargelang één
kenmerkveld. Het precedent bestaat al tweemaal: `verkoopdocument`
("compromis" vs. "verkoopakte") en `fiscaalRegime` ("registratiebelasting"
vs. "btw"/"gemengd") kiezen elk een ander model/afleidingspad binnen
hetzélfde dossiertype.

**Beslissing (definitief — niet langer een openstaande Opus-vraag).** Een
Wet-Breyne-verkoop (verkoop van een te bouwen of in aanbouw zijnde woning,
wet van 9 juli 1971) kent — net als een gewone verkoop — zowel een
compromis- als een aktefase. Het is dus **geen** derde waarde van
`verkoopdocument` (dat zou een tweede document i.p.v. een tweede fase
impliceren) en **geen** apart `Dossiertype`: het is een orthogonaal kenmerk,
naar het precedent van `fiscaalRegime`, dat kruist met de bestaande
`verkoopdocument`-waarden. Dat kruispunt levert vier combinaties op —
gewone compromis, gewone akte, Breyne-compromis, Breyne-akte — elk met hun
eigen akteType.

**✅ Geoperationaliseerd (deze wijziging):** het type-, intake- en
detectieskelet bestaat en is getest, nog zonder de inhoudelijke Breyne-
clausules/afrekening/mails erachter:
- `lib/dossier/types.ts`: `VerkoopVelden.verkoopRegime?: "gewoon" |
  "wet-breyne"`, orthogonaal op `verkoopdocument`.
- `lib/dossier/intake.ts`: volledig doorgetrokken (enum-lijst,
  `DossierIntake`/`IntakeResultaat`, parsing, `INTAKE_TOPVELDEN`) — een
  agent kan het veld vandaag al aanleveren in een intake-JSON.
- `data/modeldocumenten/akte-types.ts`: twee nieuwe, geregistreerde
  akteTypes — `"verkoopovereenkomst (compromis) - wet breyne"` en
  `"verkoopakte - wet breyne"` — nog zonder model (uitbouwkandidaat-status).
- `lib/dossier/akteType.ts`: `bepaalAkteType` kruist `verkoopRegime` met
  `verkoopdocument` (en, bij afwezigheid daarvan, met `compromisdatum`) naar
  de juiste Breyne-akteType, met tests in `akteType.test.ts`.
- **Bewust nog niet aangeraakt:** `lib/dossier/uitbouw.ts` (`proefScenarios`/
  `ALLE_DOSSIERTYPES`) blijft ongewijzigd. Zodra daar Breyne-scenario's
  worden toegevoegd zonder dat er al een model bestaat, slaat `ontwerpNL`
  voor het HELE dossiertype `verkoop-met-krediet` om naar `false` — de
  dekkingsmatrix kan vandaag geen gedeeltelijke dekking binnen één
  dossiertype uitdrukken (zie het nieuwe punt 27 hieronder). Wachten met die
  koppeling tot de modellen er zijn (punt 22) voorkomt een valse regressie
  van een pad dat in werkelijkheid nog gewoon werkt.

**Sonnet 5 — resterende uitvoering, nu ontblokkeerd en ondubbelzinnig:**

22. **Wet Breyne — ontwerp/clausules, in twéé modellen (compromis + akte),
    analoog aan de gewone verkoop.** ✅ Gedaan: twee nieuwe modellen, elk
    NL+FR (`verkoopovereenkomst-compromis-wet-breyne(-fr)` en
    `verkoopakte-wet-breyne(-fr)` in `data/modeldocumenten/modellen/
    vastgoed.ts`), analoog gekloond aan resp. `verkoopovereenkomst-compromis`
    en `verkoopakte-woning`, met de klassieke `prijs-en-betaling`-clausule
    vervangen door zes nieuwe onderdelen in `data/modeldocumenten/onderdelen/
    wet-breyne(-fr).ts`: toepassingsgebied (art. 1-2, hypotheses
    compromis/akte), betaling per bouwfase (art. 5, hypotheses
    compromis/akte), prijsherziening (art. 7, max. 80%), waarborg (art. 12,
    3 vormen), tienjarige aansprakelijkheid (art. 9 + wet Peeters-Borsus) en
    plannen/lastenboek/bedenktijd. `beschrijving-onroerend-goed` (verplichte
    clausule, AGENTS.md) wordt hergebruikt (variant 'grond'), geen tweede
    beschrijvingsclausule. `lib/dossier/parameters.ts` (`dossierNaarKeuzes`)
    kiest de compromis/akte-variant automatisch uit hetzelfde dossierveld
    (`verkoopdocument`) dat ook al het model bepaalt — geen extra open keuze.
    De wettelijke bedenktijd is bewust als open `[NAKIJKEN]` geformuleerd (de
    wet Breyne zelf kent geen algemene bedenktijd); dit en enkele andere
    dossierspecifieke punten (natrekking van de opstallen, ABR-verzekering
    tijdens de bouw, gemengd btw/registratiebelasting-regime) staan als
    conditie-tekst gemarkeerd voor de notaris. Bewaakt door
    `lib/dossier/wet-breyne.test.ts` (detectie → model → gegenereerde tekst
    → mails → afrekening, NL+FR) en de generieke schema-guard/pariteitstests.
23. **Wet Breyne — afrekening.** ✅ Gedaan, met een bewuste scope-keuze:
    `lib/dossier/afrekening.ts` voegt bij `verkoopRegime === "wet-breyne"` een
    expliciete toelichting toe (ereloon/verkooprecht blijven op de TOTALE
    prijs berekend — het betalingsritme per bouwfase wijzigt die grondslag
    niet — met een verwijzing naar het mogelijke btw/registratiebelasting-
    gemengd regime). Bewust GEEN parallel `EreloonAkteType` ingevoerd: het
    KB 1950-ereloonbarema en het verkooprecht/de btw-berekening zijn
    rekenkundig identiek aan een gewone verkoop (afhankelijk van prijs/gewest,
    niet van het betalingsritme) — een duplicaat-`AkteType` zou zeven
    akteType-geschakelde functies in `data/ereloon.ts` moeten spiegelen voor
    een uitkomst die toch nooit afwijkt van `"verkoop"`. Een eventueel
    afzonderlijk btw/registratiebelasting-splitsingsmechanisme (op basis van
    `fiscaalRegime`) is een generieke lacune die ALLE verkoopdossiers treft,
    geen Breyne-specifiek gat — apart te plannen indien gewenst.
24. **Wet Breyne — modelmails.** ✅ Gedaan: nieuwe modelbrief
    `betaling-termijnoproep-bouwfase-nl(-fr)` in
    `data/modelbrieven/brieven/betaling.ts` (zelfde categorie `"betaling"`
    als de bestaande generieke betaalmail, dus automatisch mee opgehaald door
    `relevanteBriefCategorieen`) — een termijnoproep per bereikte bouwfase in
    plaats van de klassieke eenmalige voorschot-/saldo-mail. Bedrag en fase
    blijven bewust open parameters (net als `{{bedrag}}` in de bestaande
    generieke betaalmail): ze schommelen per fase en zijn geen vast
    dossierveld.
25. **Wet Breyne — UI-toggle.** ✅ Gedaan: `app/dossier/page.tsx`
    (aanmaakformulier, naast de compromis/verkoopakte-keuze) en
    `app/dossier/[id]/page.tsx` (bewerkformulier, naast dezelfde
    document-toggle) krijgen een `verkoopRegime`-toggle (gewoon/wet-breyne),
    naar het bestaande knoppenpatroon van `verkoopdocument` — niet naar
    `fiscaalRegime`, dat vandaag géén manuele editor-toggle heeft (enkel via
    intake-JSON instelbaar; die eerdere aanname in dit backlogpunt klopte
    niet). De dossierlijst toont "· Wet Breyne" als extra label.
26. **Tweede sub-pad-kandidaat als toets op het mechanisme: verkoop tegen
    lijfrente** (bail à rente viagère). ✅ Gedaan, en het patroon bleek
    herbruikbaar met minder wijzigingen dan verwacht: `VerkoopVelden.
    verkoopRegime` kreeg een derde waarde `"lijfrente"` (naast
    `"gewoon"`/`"wet-breyne"`) + een nieuw dossierveld `lijfrente` (bouquet,
    jaarlijkseRente, frequentie, genotsrecht). Vier nieuwe onderdelen, NL+FR
    (`data/modeldocumenten/onderdelen/verkoop-lijfrente(-fr).ts`): aleatoir
    karakter + nietigheidsrisico bij overlijden binnen 20 dagen (art. 1968-
    1975 oud BW), bouquet/rente (varianten met/zonder bouquet), genotsrecht
    (vrij/vruchtgebruik/gebruik-en-bewoning) en een louter informatieve
    fiscale-grondslag-clausule (kanselement, art. 2.9.2.0.1 VCF). Twee nieuwe
    modellen, NL+FR (`verkoopovereenkomst-compromis-lijfrente(-fr)` en
    `verkoopakte-lijfrente(-fr)`). Anders dan bij wet Breyne verschilt de
    inhoud NIET tussen compromis en akte (geen aparte varianten nodig) — het
    patroon (`akteType.ts`) is bij deze gelegenheid gerefactored naar een
    `VERKOOP_REGIMES`-opzoektabel i.p.v. geneste ternaries, zodat een derde
    (en volgend) sub-pad geen herschrijving van de kruisingslogica meer
    vraagt. `dossierNaarKeuzes` kiest bouquet-variant en genotsrecht
    deterministisch uit het dossier (nooit "vrij" veronderstellen zonder
    expliciete bevestiging). **Bewust géén automatische afrekening**: het
    kanselement heeft geen vaste prijs als grondslag (gekapitaliseerde
    rente + bouquet, venale waarde als ondergrens) — een verzonnen
    kapitalisatieberekening zou minder betrouwbaar zijn dan geen berekening;
    `lib/dossier/afrekening.ts` geeft in plaats daarvan een gerichte
    toelichting die naar de fiscale-grondslag-clausule verwijst. UI-toggle
    (naar punt 25) meteen naar 3 opties uitgebreid op beide dossierpagina's.
    Bewaakt door `lib/dossier/verkoop-lijfrente.test.ts` (detectie → model →
    tekst → mails/geen-afrekening, NL+FR, met/zonder bouquet).

**Fable 5 — mechanische afronding zodra inhoud vaststaat:**

27. **Wet Breyne registreren als gouden pad + `uitbouw.ts` koppelen.**
    ✅ Gedaan: twee nieuwe scenario's in `lib/dossier/gouden-paden.ts`
    (compromis-wet-breyne en verkoopakte-wet-breyne, beide met FR-spiegel,
    mails en afrekening — alle vier de pijlers gedekt; eerste ratchet-meting
    116/9 en 250/21 open velden/keuzes NL, 118/13 en 252/25 FR), de twee
    Breyne-varianten in `proefScenarios` (`uitbouw.ts`, zichtbaar in de
    per-akteType-dekking van punt 28) en de volwassenheids-ratchet
    opgeschoven van 22 naar 30 gedekte pijlers.
28. **Dekkingsmatrix geschikt maken voor gedeeltelijke dekking binnen één
    dossiertype.** ✅ Gedaan: `DossiertypeDekking.perAkteType`
    (`lib/dossier/uitbouw.ts`, type `AkteTypeDekking`) drukt per gedetecteerd
    akteType uit of het NL-model en de FR-spiegel bestaan; de dossiertype-
    brede vlaggen zijn er de alles-of-niets-samenvatting van (bewaakt in
    `uitbouw.test.ts`). Zodra een sub-pad zoals wet Breyne via
    `proefScenarios` extra akteTypes aan een dossiertype toevoegt (punt 27),
    verschijnt de ontbrekende combinatie als zichtbaar deelgat in plaats van
    het hele dossiertype vals te laten regresseren.

### Word-output optimaliseren — maximaal gebruiksgemak (2026-07-04)

De .docx-uitvoer loopt over twee paden: (a) de **sjabloon-merge**
(`compromisSjabloon.ts`/`akteSjabloon.ts`) die het officiële kantoorsjabloon
overneemt (stijlen, hoofding/voettekst met logo, omkaderd vak, handtekeningen)
en enkel de clausule-inhoud vervangt, en (b) het **docx-library-pad**
(`genereerOntwerpWordDocument.ts`/`genereerWerkdossierWordDocument.ts`) dat een
document van nul opbouwt. Al aanwezig op beide: échte kopstijlen (Titel/Kop 1-4
met outline-nesting → navigatievenster), 'Wijzigingen bijhouden' geforceerd,
SEQ-velden voor automatische clausulenummering (sjabloon-pad), woordenlijst-
hyperlinks, gele arcering van te-kiezen passages, `keepNext`, uitvullen,
`updateFields` bij openen, DEFLATE-compressie.

**✅ Gedaan (deze wijziging, docx-library-pad):** spellingtaal `nl-BE`/`fr-BE`
op de docDefaults-run (`woordStijlen(taal)` in `ontwerpOpmaak.ts`) zodat Word
in de júiste taal spellingcontroleert i.p.v. `en-US`; documentmetadata
(titel/auteur/onderwerp/trefwoorden in `docProps/core.xml`); en een
paginavoettekst met een levend `PAGE / NUMPAGES`-veld (`paginavoettekst(taal)`).
Bewaakt in `genereerOntwerpWordDocument.test.ts` en
`genereerWerkdossierWordDocument.test.ts`.

**Fable 5 — mechanisch, scherp afgebakend:**

29. **Sjabloon-metadata en -spellingtaal auditen.** ✅ Grotendeels gedaan:
    de audit legde bloot dat de docDefaults-spellingtaal in drie van de vier
    sjablonen was weggedreven (NL-compromis en FR-compromis op `fr-FR`,
    FR-akte op `nl-BE`). `zetSpellingtaalInStyles` (`lib/word/sjabloonTaal.ts`)
    corrigeert bij de merge de docDefaults-`w:lang` naar de Belgische
    documenttaal (`nl-BE`/`fr-BE`) — de overervingswortel voor de
    geïnjecteerde clausules; de bewust gekozen taal van benoemde stijlen en
    sjabloon-body blijft ongemoeid (de FR-aktebody stond al correct op
    `fr-BE`). Toegepast in `compromisSjabloon.ts`/`akteSjabloon.ts`, bewaakt in
    `sjabloonTaal.test.ts` + integratietests. Resterend (laag, optioneel):
    de `docProps/core.xml`-auteur/titel dragen nog de sjabloonherkomst
    (bv. "Helderrecht-Sarah Debecker"); dat is kantoormodel-metadata en niet
    dringend te overschrijven.
30. **`w:noProof` op de werkblad-/instructiepagina's.** ✅ Gedaan:
    `RunOpties.noProof` in `ooxml.ts` (schemavolgorde: ná b/i, vóór
    u/highlight), doorgetrokken op álle werkbladparagrafen in
    `werkbladParagrafen` (compromisSjabloon.ts, dekt ook het
    akteSjabloon-pad) en op alle instructie-TextRuns in
    `aiAgentInstructies.ts` (docx-library-pad). Bewaakt in beide
    testbestanden, mét negatieve controle dat de eigenlijke clausule-tekst
    spellingsgecontroleerd blijft.

**Sonnet 5 — feature-werk met een plaatsingskeuze:**

31. **Klikbare inhoudsopgave (TOC-veld).** ✅ Gedaan: op het docx-library-pad
    bouwt `inhoudsopgaveParagraaf` (`ontwerpOpmaak.ts`) een `TableOfContents`
    (kopniveaus 1-4, klikbaar), ingevoegd ná de instructie-/structuuranker-
    paragrafen en vóór de kantoorhoofding/aktetekst in zowel
    `genereerOntwerpWordDocument.ts` als `genereerWerkdossierWordDocument.ts`.
    Op het sjabloon-merge-pad bouwt `inhoudsopgavePagina`
    (`compromisSjabloon.ts`, hergebruikt door `akteSjabloon.ts`) een raw-OOXML
    `TOC \o "1-4" \h \z \u`-veld op haar eigen pagina, tussen het (te
    verwijderen) werkblad en de eigenlijke akte — bewust ZONDER `w:noProof`
    (in tegenstelling tot het werkblad), want deze pagina blijft in het
    definitieve document staan. `updateFields` stond al aan op het
    sjabloon-pad; op het docx-library-pad toegevoegd (`features.updateFields`
    op `Document`, ontbrak daar tot nu) zodat Word de velden bij het openen
    vult. Enkel bij ontwerp/akte, niet bij losse modelmails (`download-
    bestand.ts` blijft ongemoeid). Bewaakt in alle vier de betrokken
    testbestanden (`ontwerpOpmaak`/`genereerOntwerpWordDocument`/
    `genereerWerkdossierWordDocument`/`compromisSjabloon`/`akteSjabloon`).
32. **Modelmail-metadata → losse .eml/mailvelden.** ✅ Gedaan, met een bewuste
    scope-keuze: `onderwerp` bestond al als gestructureerd veld; de
    geadresseerde was tot nu een vrij `ontvanger`-label ("Koper (cliënt)",
    "confrater (verkoper)", …), enkel via losse, onderling licht verschillende
    regexen herkend (`isAanConfrater`/`mailWaardenVoorOntvanger`/
    `standaardBegeleidendeBrieven`). Nieuwe gestructureerde `OntvangerRol`-enum
    (`data/modelbrieven/types.ts`: client-koper/client-verkoper/client-overig/
    confrater/bank/fiscus/makelaar/bieder/overig) + één bron van waarheid
    `bepaalOntvangerRol` (`samenstellen.ts`) die de bestaande regexen vervangt
    waar dat zonder gedragswijziging kon (`isAanConfrater`,
    `mailWaardenVoorOntvanger`) — `standaardBegeleidendeBrieven`s eigen,
    scherpere match ("koper (cliënt)" specifiek) bewust ongemoeid gelaten,
    geen tests om een gedragswijziging daar te bewaken. `vulModelbriefIn` zet
    de afgeleide rol nu automatisch op `IngevuldeBrief.rol`, dus elke
    `AutonomeModelmail`/`BegeleidendeBrief` draagt ze gratis mee; het
    werkdossier toont ze als "Geadresseerde: …" boven het onderwerp
    (`genereerWerkdossierWordDocument.ts`). Geen `.eml`-bestand of
    e-mailadres: er bestaat vandaag geen e-mailveld op `Partij`, en het
    effectief updaten wordt tier-3 (AGENTS.md) — dit dicht enkel de
    "gestructureerd veld"-helft van het backlogpunt.

**Opus 4.8 — ontwerpkeuze met een afruil tegen bestaande werkwijzen:**

33. **Echte Word-commentaren vs. de ster-markering.** Vandaag staan
    redactionele opmerkingen inline als geel `*[*…*]*` in de aktetekst,
    afgestemd op de kantoor-sneltoets "spring naar `*`". Echte Word-
    commentaren (`word/comments.xml`, door de docx-library ondersteund via
    `comments`) houden de aktetekst schóón en geven de notaris de
    Controleren-lint-navigatie — maar breken de bestaande `*`-workflow en de
    lokale-invul/AI-agent-verwerking die op de haaktekst rekenen. Weeg af:
    vervangen, naast elkaar, of enkel voor een deel van de opmerkingen; lever
    een ontwerp vóór implementatie.
34. **Invulvelden als content controls (`w:sdt`).** De sterkste Word-functie
    voor invuldocumenten: elke `[AAN TE VULLEN: …]`-plaats wordt een
    tekst-content-control met prompttekst, zodat de notaris met Tab tussen de
    velden springt en elk veld een afgebakend, klikbaar object is. Raakt
    echter `vulPlaceholdersLokaalIn.ts` (lokale invulling) én de AI-agent-
    invulling, die nu op de platte `[AAN TE VULLEN: …]`-tekst werken — de
    afruil (discreet invulobject vs. eenvoudige tekstvervanging) en de
    migratie van beide invulpaden vragen een expliciet ontwerp vóór de bouw.

### Vervolg werkdossier-sessies en boekhouding — vrijgegeven door de notaris (2026-07-08)

**Sonnet 5 — integratiewerk met matige ontwerpruimte:**

35. **Koppeling werkdossier-sessies ↔ app-dossiers.** De 24u-sessies
    (`lib/dossier/werkdossierSessies.ts`) en de IndexedDB-dossiers van de app
    leven vandaag naast elkaar. Koppel ze: bewaar het `dossierToken` op het
    app-dossier (nieuw veld, zelfde patroon als `koppelingen`), toon de
    actieve sessies op de dossierfiche (`app/dossier/[id]`), en laat een
    generatie die vanuit de fiche vertrekt automatisch koppelen. Raakt de
    grens lokaal (persoonsgegevens, IndexedDB) vs. server (geanonimiseerde
    sessies) — het token is geen persoonsgegeven en mag dus op beide kanten.
36. **Boekhouding vervolg (AUT-S8 rest).** (a) Decompte voor schenking en
    nalatenschap naar het precedent van `verkoopdecompte.ts` (schenkbelasting/
    erfbelasting i.p.v. verkooprecht; geen makelaar); (b) export naar het
    boekhoudpakket via de connector-laag (AUT-O5, mock-eerst); (c) de
    voorbereide doorstortingen als tier-3-escalatielijst (beoordeelDoorstorting
    is er al — de lijst "klaar voor goedkeuring door de notaris" nog niet).
    - ✅ *(c) gedaan:* `lib/dossier/doorstortingen-flow.ts`
      (`bereidDoorstortingenVoor`) levert de escalatielijst — sequentieel tegen
      het lopende saldo (de som overschrijdt het dossiersaldo nooit), elke post
      voorbereid-escaleren of geweigerd, `autonoomMogelijk: false`. *Resterend:*
      (a) de niet-verkoop-decomptes en (b) de boekhoud-export-connector.

**Fable 5 — mechanische afronding:**

37. **Werkdossier-sessies: mechanische afronding.** (a) Sessie verwijderen
    (DELETE-endpoint + knop in /werkdossiers + MCP-tool); (b) afbeeldingen
    (`afbeeldingenJson`) opnieuw kunnen aanleveren bij een regeneratie (ze
    worden bewust niet in de sessie bewaard); (c) kenmerken en facultatieve
    onderdelen zichtbaar en bewerkbaar in het UI-detail (vandaag enkel
    parameters).

### Nieuwe werven na het sluiten van HUIDIGE_GATEN (2026-07-08)

`HUIDIGE_GATEN` (lib/dossier/uitbouw.test.ts) is voor het eerst LEEG: elk
dossiertype en elke uitbreidingskandidaat dekt alle vijf de pijlers. De
volgende werven verdiepen de bestaande lagen in plaats van ze te verbreden —
elk gegrond in een concreet, gemeten gat.

**Sonnet 5 — juridische inhoud en integratie:**

38. **Akte-checklists uitbreiden naar alle gedekte akteTypes.** De kennisbank
    telt 6 checklists (compromis, verkoopakte, kredietakte, basisakte,
    schenking, aangifte nalatenschap) tegenover 40+ geregistreerde akteTypes:
    testamenten, huwelijkscontract, zorgvolmacht, EOT, vennootschapsakten
    (oprichting/ontbinding/omzetting/statutenwijziging), verdeling, ruil,
    erfpacht/opstal, handlichting, verkavelingsakte, openbare verkoop en
    volmacht missen er een — terwijl `haalAkteChecklists` (MCP) en de
    track-changes-werkwijze er wél naar verwijzen. Zelfde gewogen structuur
    (`data/kennisbank-checklists.ts`, cruciaal/belangrijk/nuttig/overbodig);
    voeg een dekking-guard toe die de checklistdekking meet tegen `AKTETYPES`
    (ratchet: dekking mag enkel stijgen), naar het model van de
    werkwijze-dekking-test.
39. **Opvolgingsroutes voor de niet-verkooptypes.**
    `stelOpvolgingSamenAutonoom` (lib/dossier/opvolging.ts) meldt voor alles
    behalve verkoop eerlijk "niet gemodelleerd" — dus geen route, geen
    termijnen, geen urgentie in de dossierlijst. Bouw de routes voor
    schenking, nalatenschap, testament en statutenwijziging. Nalatenschap is
    de dringendste: de wettelijke aangiftetermijn erfbelasting (4 maanden bij
    overlijden in België, 5 in de EER, 6 daarbuiten) is een verval-termijn
    met belastingverhoging — hoort als `OpvolgingTermijn` + urgentiebadge én
    in `berekenDossierTermijnen` (MCP).
    - ✅ *Deel gedaan:* de aangiftetermijn-kern staat als pure, geteste module
      `lib/dossier/aangiftetermijn.ts` (`berekenAangiftetermijn`, 4/5/6 maanden
      VCF art. 3.3.1.0.5, hergebruikt de AUT-O4-primitieven
      `vervaldatumNaMaanden`/`termijnStatus`). *Resterend:* een `overlijdensdatum`
      + `plaatsOverlijden` op het nalatenschapdossier, de opvolgingsroutes per
      niet-verkooptype in opvolging.ts, de urgentiebadge en de MCP-doorstroming.
40. **Modelmails lokaal verzendklaar (.eml/mailto).** `Partij` heeft geen
    e-mailveld en `IngevuldeBrief` kent sinds punt 32 wel de rol maar geen
    adres. Voeg een lokaal e-mailveld toe (IndexedDB; NOOIT naar de server —
    zelfde grens als alle persoonsgegevens) en geef de dossierfiche per
    klaargemaakte modelmail een "openen in mailclient"-knop (mailto met
    onderwerp/body) of .eml-download, volledig client-side opgebouwd;
    `OntvangerRol` kiest het adres (koper/verkoper/confrater).
41. **Tweetalige UI (FR).** De documentgeneratie is volledig tweetalig, maar
    álle schermteksten zijn Nederlandstalig — terwijl het kantoor ook in het
    Frans werkt. Gefaseerd per module (eerst Dossiers, Werkdossiers,
    Boekhouding en de navigatie): één teksten-map NL/FR per module (zelfde
    patroon als `DOSSIERSTATUS_LABEL`), taalkeuze in Kantoorinstellingen,
    en een guard die NL- en FR-sleutels paritair houdt (naar het model van
    nl-fr-pariteit.test.ts).

**Fable 5 — mechanisch, scherp afgebakend:**

42. **Contractuele aktetermijn in de termijnmotor.** De vier maanden in
    `opvolging.ts`/`berekenDossierTermijnen` is de standaardtermijn, maar een
    compromis bedingt vaak een eigen termijn (drie of zes maanden, of een
    vaste uiterste datum). Nieuw veld (bv. `aktetermijn?: { maanden?: number;
    uiterlijkeDatum?: string }` op VerkoopVelden + intake + MCP-parameter);
    de contractuele termijn stuurt de urgentie, en de FISCALE
    registratietermijn van vier maanden blijft apart bewaakt zodra de
    contractuele termijn er voorbij ligt (twee verschillende vervaldagen,
    elk met hun eigen gevolg).
43. **Opvolgingsacties als MCP-tool (`bepaalVolgendeStappen`).** De motor
    kent per dossierfase de concrete acties en mailcategorieën
    (`stelOpvolgingSamenAutonoom` → stappen[].acties), maar MCP-agenten
    kunnen er niet bij. Structureel-only endpoint (dossiertype + status +
    optionele vlaggen zoals appartement/krediet-aanvaard — geen
    persoonsgegevens) → huidige stap, acties, mailcategorieën; in de
    werkwijze opnemen (stap 6), dekking-guard dwingt dat af.

**Opus 4.8 — onderzoek vóór implementatie:**

44. **Geverifieerde tarieven voor punt 18 (afrekening testamenten en
    vennootschapsakten).** Punt 18 bleef liggen omdat de exacte KB
    1950-barema's (vast testamenttarief; schaal voor statutenwijziging/
    oprichting) niet zonder geverifieerde bron te reproduceren zijn — een
    verkeerd tarief in `data/ereloon.ts` is erger dan geen tarief.
    Onderzoekswerf: de tarieven met verifieerbare bronvermelding (KB-tekst,
    geïndexeerde bedragen 2026) vastleggen in een kort document; de
    implementatie daarna is een gewone Fable-werf (akteMeta + barema +
    gouden-paden-vlaggen omzetten).
    **Onderzoek uitgevoerd** → `ONDERZOEK-KB1950-TESTAMENT-VENNOOTSCHAP.md`:
    de toepasselijke schalen zijn geïdentificeerd (vennootschap = schaal L/M,
    min € 42,18; testament = vast verlijdenshonorarium + schaal E). De **exacte
    geïndexeerde schijventabellen** vergen echter de **primaire** tariefbesluit-
    tekst, die in de sandbox via de proxy niet bereikbaar was (WAF-403 op
    notaris.be/ejustice/etaamb). Daarom bewust nog **niet** in `data/ereloon.ts`
    verankerd — de conservatieve terugval (`berekenForfaitaireAfrekening`) blijft
    correct tot de notaris de primaire PDF aanlevert. Implementatievorm en
    vervolgstappen staan in het memo.

### Naar het einddoel — autonomie, flexibiliteit, schaalbaarheid, evolutiviteit (2026-07-07)

`HUIDIGE_GATEN` is leeg: de generatie-dekking (laag 4) is voor de huidige
akteTypes gesloten. De volgende werven **verdiepen de autonomie-lagen en maken
de app klaar voor groei buiten het ene kantoor** — ze dienen rechtstreeks het
einddoel (volledig autonome notaris) en de vier assen die de notaris noemde:
autonomie, flexibiliteit (akten sui generis), schaalbaarheid (bijkomende akten,
andere kantoren) en evolutiviteit (nieuwe functies, autonome verbetering). De
zwaarste ontwerpkeuzes staan óók als `AUT-O8…O11`/`AUT-S9` in
`AUTONOMIE-ROADMAP.md` (§5) — lees dat document vóór de start. De
ambtelijke-voorbehoudgrens en het drie-tiers-regime (§1/§2 van de roadmap)
blijven de bovenste invarianten van élke werf hieronder.

**Opus 4.8 — de dragende ontwerpkeuzes (eerst per as):**

49. **Autonome verbeteringslus — ontwerp (`AUT-O8`).** *De door de notaris
    uitdrukkelijk gevraagde functie: de app verbetert zichzelf op basis van de
    opgebouwde ervaring van Notary.AI.* De bouwstenen bestaan al los van elkaar
    — `lib/gebruikslog.ts` (signalen: onbekende ids, genegeerde facultatieve
    onderdelen, plus de zes AUT-F5-signalen uit perceptie/poort), de
    audit-trail (`lib/dossier/audit.ts`, append-only hashketen), de
    escalatiegronden (`lib/dossier/autonomie.ts`), `lijstVerbetervoorstellen`
    (MCP), de wijzigingsvoorstel-pijplijn (`voegKennisVoorstelToe` /
    `voegModelonderdeelVoorstelToe` / `voegModeldocumentVoorstelToe` → tab 'Te
    valideren') en `data/modeldocumenten/bibliotheek-audit.ts`. Ze zijn nog
    nergens tot één lus gekoppeld. Ontwerp het contract van die lus: welke
    signalen ze consumeert, welk **getypeerd `VerbetervoorstelKandidaat`** ze
    produceert (nieuw modelonderdeel/variant, ontbrekende hypothese, nieuw
    kennisitem, checklistpunt, gouden-pad-kandidaat, promptregel), hoe ze
    rangschikt (frequentie × impact × recentheid), en — dragend — de
    governance-grens: **álles landt als voorstel in 'Te valideren', nooit een
    automatische mutatie van de gedeelde bibliotheek** (tier 2 uit §2 van de
    roadmap; de notaris keurt goed). Twee expliciete afwegingen: (a) precisie
    versus dekking (enkel voorstellen bij een sterk signaal, of alles
    oppervlaktebrengen), en (b) een **anti-terugkoppeling** — een voorstel dat
    de notaris herhaaldelijk afwijst, mag niet elke ronde opnieuw opduiken
    (onderdrukkingslijst op basis van eerdere afwijzingen, PII-vrij). Leg ook
    de GDPR-grens vast: de lus draait op geaggregeerde, PII-vrije signalen
    (bronTYPE/veldpad/dienst/id-frequenties), nooit op dossierspecifieke
    persoonsgegevens.
50. **Kantoor-abstractie / multi-tenant architectuur (`AUT-O9`).** De app is nu
    impliciet één kantoor: de `KANTOOR`-constante, "notarissen Tervuren" in de
    PDF-voetteksten, het kantoorsjabloon-docx, en kantoorgebonden identifiers
    (e-notariaat, bankrekeningen derdengelden) zitten verspreid hardgecodeerd.
    Ontwerp een `Kantoorprofiel` dat het kantoorgebonden deel scheidt van het
    universele deel. Kernafweging: **het Belgische recht (barema's KB 1950,
    belastingtarieven, wettelijke termijnen, modelclausules) is universeel en
    gedeeld; branding, sjablonen, briefhoofden, bankrekeningen, ereloon-
    particulariteiten en gebruikersbeheer zijn kantoorgebonden.** Beslis de
    tenant-isolatie (één instantie per kantoor versus gedeelde instantie met
    tenant-sleutel), waar het profiel leeft (config/IndexedDB/Supabase-rij), en
    de GDPR-consequenties van gedeelde infrastructuur. Doel: een tweede kantoor
    kan aansluiten zonder de code te forken.
51. **Akte sui generis — graceful-degradation-contract (`AUT-O10`).** De motor
    kiest vandaag per `akteType` een vast model en deterministische hypotheses.
    Een akte sui generis (vrije vorm, geen vast model) doorbreekt die aanname.
    Ontwerp wat er dan gebeurt met de lagen die een vast type veronderstellen —
    akteType-detectie, checklist, afrekening, opvolgingsroutes, gouden-pad-test:
    welke blijven werken (de verplichte `beschrijving-onroerend-goed`-clausule
    en de generatie-basis), welke degraderen elegant, en welke **escaleren naar
    de notaris** (tier via AUT-O1) omdat er geen gemodelleerde zekerheid is. De
    invariant: een vrije-vorm-akte mag nooit de verplichte beschrijvingsclausule
    of de voorbehoudgrens omzeilen.
52. **End-to-end dossier-orkestratie — de "dirigent" (`AUT-O11`).** De
    laag-motoren bestaan afzonderlijk (intake `AUT-S1`, volledigheid `AUT-S2`,
    levenscyclus `AUT-S3`, opzoekingen `AUT-S4`, AML `AUT-S5`, generatie
    `AUT-S6`, formaliteiten `AUT-S7`, boekhouding `AUT-S8`). Ze zijn nog niet
    tot één autonome doorloop per dossier gekoppeld. Ontwerp de dirigent die de
    keten aanstuurt via de levenscyclus-toestandsmachine (`AUT-O4`), bij elke
    tier-3-poort de escalatiebeslissing (`AUT-O1`) raadpleegt, en elke transitie
    in de audit-trail (`AUT-O2`) verankert. Dragende keuze: hoever de doorloop
    autonoom vooruitgaat vóór een verplichte notaris-tussenkomst, en hoe een
    dossier na een escalatie hervat zonder werk over te doen (idempotentie via
    `AUT-O5`).

**Sonnet 5 — de opbouw (ná het bijhorende Opus-ontwerp):**

53. **Autonome verbeteringslus — implementatie (`AUT-S9`).** Bouw op het
    `AUT-O8`-contract een pure analysemotor `lib/dossier/verbeterlus.ts`:
    `analyseerGebruikspatronen(events, auditsamenvatting) →
    VerbetervoorstelKandidaat[]`, met de rangschikking en anti-terugkoppeling
    uit het ontwerp. Sluit ze aan op (a) een MCP-tool zodat een agent de
    kandidaten kan ophalen en na goedkeuring als échte voorstellen indienen via
    de bestaande `voeg…VoorstelToe`-functies, en (b) een paneel in de tab 'Te
    valideren' dat de kandidaten met hun onderbouwing (aantal treffers, laatste
    voorval, PII-vrij) toont. Guard-test: geen enkel pad muteert de gedeelde
    bibliotheek rechtstreeks — alles gaat via een voorstel.
54. **Vrije-vorm akte-samensteller (clausule-picker met invariant).** Op basis
    van `AUT-O10`: een flow die een akte/onderhands document van nul opbouwt uit
    losse modelonderdelen (kies-en-plaats uit de bibliotheek) zonder vast model,
    startend van een lege schil. De invariant is hard afgedwongen: zodra het
    dossier een onroerend goed bevat, wordt de verplichte
    `beschrijving-onroerend-goed`-clausule automatisch en niet-verwijderbaar
    opgenomen. Levert een gewoon werkdocument (tier 1) dat de notaris naleest.
55. **Autonomie-dashboard per dossier.** Een leesweergave die per dossier toont
    waar het in de levenscyclus staat (`AUT-S3`), wat de agent autonoom deed en
    wat naar de notaris escaleerde (uit de audit-trail `AUT-O2`, PII-gescheiden),
    en welke tier-3-poorten open staan. Maakt de autonome doorloop (`AUT-O11`)
    zichtbaar en controleerbaar voor de notaris — zuiver lezend, geen mutatie.
56. **Kantoorprofiel-implementatie (ná `AUT-O9`).** Zet het ontworpen
    `Kantoorprofiel` om: verplaats de kantoorgebonden waarden naar het profiel,
    laat PDF-voetteksten, sjablonen en briefhoofden het profiel lezen, en voorzie
    een Kantoorinstellingen-scherm om het profiel te beheren. Guard: geen "Tervuren"
    of kantoorgebonden identifier meer hardgecodeerd buiten het profiel.

**Fable 5 — mechanisch, scherp afgebakend:**

57. **Nieuwe-akte-scaffolding-script (`DRAAIBOEK-GOUDEN-PAD.md` automatiseren).**
    Een script `scripts/nieuw-gouden-pad.mjs <akteType>` dat de skeletbestanden
    genereert die het draaiboek per laag voorschrijft (akteMeta-stub, seed-
    clausule-stubs, checklist-stub, gouden-pad-test-stub) in de juiste volgorde,
    zodat de N-de akte toevoegen sneller en minder foutgevoelig wordt. Puur
    mechanisch: klonen volgens het draaiboek, geen juridische inhoud.
58. **`KANTOOR`-constanten centraliseren (voorbereiding multi-tenant).**
    Verzamel alle vandaag verspreide kantoorgebonden waarden (`KANTOOR`,
    "notarissen Tervuren"-strings in voetteksten/PDF/print, kantoorsjabloon-
    verwijzingen) in één bronbestand `data/kantoor.ts`, met een guard-test die
    hergeïntroduceerde losse hardcodes opspoort. Bereidt `AUT-O9`/punt 56 voor
    zonder al de volledige multi-tenant-keuze te maken.

### Kennisbank — verdere verdieping, getagd per model (2026-07-06)

Punt 38 hierboven dekt al de uitbreiding naar alle akteTypes + de
dekking-ratchet. Onderstaande punten zijn de resterende, niet-overlappende
verdieping van de kennisbank; claim vóór de start in `WERVEN.md`.

**Sonnet 5 — feature-/integratiewerk, matige ontwerpruimte:**

45. **Checklist automatisch tonen en afvinken in het dossier.** Op de
    dossierdetailpagina (`app/dossier/[id]/page.tsx`) de bijhorende checklist
    ophalen via het akteType van het dossier (`vindChecklistVoorAkteType`) en
    tonen met per-punt afvinkstatus. Ontwerpkeuze: de afvinkstatus is
    dossierspecifiek (de gedeelde checklist zelf blijft ongewijzigd) — een
    klein nieuw veld op het dossier of een aparte per-dossier IndexedDB-store,
    zodat twee dossiers van hetzelfde akteType onafhankelijk hun voortgang
    bijhouden.
46. **Checklistpunt koppelen aan een modelonderdeel.** Voor cruciale punten
    die overeenkomen met een bestaand modelonderdeel (bv. "beschrijving-goed"
    ↔ `beschrijving-onroerend-goed`) een optioneel `modelonderdeelId`-veld
    toevoegen aan `ChecklistPunt`, zodat een agent die een cruciaal punt moet
    invoegen (track-changes-werkwijze, stap 4) programmatisch de juiste
    clausule kan ophalen in plaats van op de vrije tekstomschrijving te
    vertrouwen. Vergt een bewuste keuze over granulariteit (één-op-één, of één
    punt met meerdere kandidaat-onderdelen per hypothese).

**Fable 5 — mechanisch, scherp afgebakend:**

47. **Kennisbank-voorstellen in de "te valideren"-teller.**
    `useOpenstaandeVoorstellenCount` (Sidebar-badge) telt vandaag enkel
    modeldocument-/modelvoorstellen; uitbreiden met openstaande
    kennisitem-/checklist-voorstellen (IndexedDB-stores `items`/`checklists`
    van `notary-kennisbank`) zodat de notaris in de zijbalk in één oogopslag
    ook kennisbank-werk ziet staan.

**Opus 4.8 — architecturale ontwerpkeuze met een afruil:**

48. **Checklistgewicht koppelen aan het zekerheids-/escalatiemodel (AUT-O1).**
    Vandaag is een checklist zuiver informatief. Onderzoek en beslis of een
    ontbrekend of afwijkend CRUCIAAL punt een blokkerende negatieve grond moet
    worden voor `bepaalZekerheid`/`beslisEscalatie`
    (`lib/dossier/autonomie.ts`) — analoog aan hoe `perceptieGronden` een
    ontbrekend verplicht veld al blokkeert (zie `AUTONOMIE-ROADMAP.md`,
    AUT-O1/AUT-O3) — zodat het genereren van een ontwerpdocument met een
    onopgelost cruciaal punt automatisch naar de notaris escaleert in plaats
    van enkel het algemene "werkdocument"-label te dragen. Vergt een
    expliciete afweging tussen te streng (elk cruciaal punt hard blokkerend)
    en te los (blijft louter een suggestie), en welke laag (perceptie,
    generatie, of een nieuwe checklist-gebonden grond) de check uitvoert.
    **Beslist & geïmplementeerd** (`lib/dossier/checklist-zekerheid.ts`): een
    NIEUWE, laagonafhankelijke checklist-gebonden grond, naar het model van
    `perceptieGronden` en hergebruikt via `bepaalZekerheid` (autonomie.ts en
    perceptie.ts blijven ongewijzigd). De afruil: enkel CRUCIALE punten poorten
    de autonomie (belangrijk/nuttig/overbodig blijven informatief); een
    afwijkend cruciaal punt = blokkerende negatieve grond (→ "onzeker"), een
    open cruciaal punt = niet-blokkerende negatieve grond (→ trapsgewijs). Elk
    cruciaal punt dat niet "ok" is doet de generatie escaleren: het etiket
    verspringt van "werkdocument" naar "voorleggen-aan-notaris"
    (`beoordeelGeneratieChecklist`). De per-punt-uitkomst komt van de
    dossierspecifieke checklist-voortgang (punt 45).

## Gelaagdheid

```
data/        ← domeinlaag: tarieven, barema's, regels, pure berekeningen
lib/         ← gedeelde utilities: formattering, handoff, context, pdf, db
components/  ← herbruikbare UI (presentatie, geen domeinlogica)
app/         ← pagina's: orkestreren state en stellen data + components samen
```

**Afhankelijkheidsrichting:** `app → components → lib → data`. De datalaag
importeert nooit uit `app/` of `components/`; componenten bevatten geen
tariefkennis.

### `data/` — domeinlaag (single source of truth)

| Bestand | Inhoud |
|---|---|
| `belastingen.ts` | Schenk- en erfbelastingbarema's per gewest, `berekenSchenking`, `berekenErfenis`, relatielabels/-iconen/-opties, wetreferenties |
| `ereloon.ts` | Notariële barema's (KB 1950), `akteMeta` (configuratie per akteType incl. `actief` en `infoBanner`), heffingen, hypothecaire retributies (`getRetributies`, als `IndividualiseerbarKost[]` met `btwPlichtig: false` voor overschrijving/inschrijving), kostenlijsten, `BTW_TARIEF`, `berekenAkteKostenOverzicht` (retourneert ook `vrijgesteldKosten`/`vrijgesteldBedrag` voor de btw-vrije posten) |
| `vastgoed-opzoekingen.ts` | Matrix verplichting × rechtshandeling × gewest |
| `regelgeving/` | Wetteksten en artikelstructuur |
| `ai-prompts.ts` | Instructieprompts voor AI-agenten (vier versies per prompt: standaard / compact ≤ 8000 tekens / uitgebreid / gesplitst met aanvullend bestand) |
| `modeldocumenten/` | Evolutieve bibliotheek: herbruikbare `Modelonderdeel`-clausules (met `{{parameters}}` en hypothese-varianten) + `Modeldocument`-structuren. `samenstellen.ts` bevat de pure functies (`stelModelSamen`, `genereerDocument`, `combineerMetSeed`, `bumpVersie`, `sorteerVoorstellen`), `verrijkingsprompt.ts` de `VERRIJKINGS_PROMPT`; `index.ts` is een barrel die bij het laden ook de schema-guard draait (`schema-guard.ts`: lege verplichte velden, dubbele ids, hangende verwijzingen → dev/test/build falen meteen). Seed-data statisch; gebruikersitems via IndexedDB (`lib/db/modeldocumenten-db.ts`, `useModeldocumentenStorage`); gegenereerd document gaat via `ModeldocumentHandoff` naar de AI-prompts module |

Regels voor deze laag:

- **Alle berekeningen zijn pure functies** — geen React, geen browser-API's.
  Daardoor zijn ze testbaar en deelbaar tussen modules (de belastingmodule
  hergebruikt `berekenAkteKostenOverzicht` voor het inline kostenblok).
- **Tarieven en bedragen staan uitsluitend hier.** Geen `0.21` of `€7` in een
  pagina — gebruik `BTW_TARIEF`, `PANDREGISTER_PRIJS`, kostenlijsten.
- **Nieuw akteType toevoegen** = entry in `akteMeta` (met `actief: true`) +
  cases in `kiesBarema`/`berekenHeffing`/`getRetributies`. De UI volgt
  automatisch via `actieveAkteTypen` en de `heeft*`-vlaggen.
- Elke datatabel draagt een `*_META`-blok met geldigheidsdatum en bron;
  werk dit bij bij tariefwijzigingen.

### `lib/` — utilities

- `format.ts` — `euro`, `pct`, `parseBedrag`, `valideerBedrag`. **Nooit lokaal
  herdefiniëren.** Uitzondering: `lib/pdf/genereer.ts` heeft een eigen
  `euro` omdat jsPDF/helvetica de Intl-spaties niet rendert.
- `download.ts` — `downloadTekst`/`downloadWord` (browser-download van
  gegenereerde documenten; enkel in client components).
- `handoff.ts` — getypeerde parameteroverdracht tussen modules via
  sessionStorage (`schrijfSchenkingHandoff` / `leesSchenkingHandoff`).
  `SchenkingHandoff` draagt naast `akteType`/`bedrag`/`relatie` ook optioneel
  `begunstigden` (multi-begunstigden) en `aantalSchenkers` (aantal
  overdragers, voor pandregister/retributies "per overdrager" in ereloon).
  Nieuwe cross-module flows: voeg hier een interface + schrijf/lees-paar toe.
- `dossier/koppeling.ts` — dossierkoppelingen (verkoop ↔ kredietdossier,
  vervolg-, deel- en verwante dossiers): symmetrisch koppelen/ontkoppelen —
  beide dossiers dragen de koppeling, elk met de inverse relatie
  (`DossierKern.koppelingen`); bewaakt in `koppeling.test.ts`. Aanknopingspunt
  voor dossierregie over een keten (laag 2) en de boekhoudmodule (laag 6,
  gereserveerd in `lib/modules.ts` + `app/boekhouding/`, uitwerking = AUT-S8).
- `context/gewest.tsx` — globaal gewest (met localStorage-persistentie).
- `pdf/genereer.ts` — PDF-export (jsPDF, lazy geladen via dynamic import).
- `db/`, `hooks/` — IndexedDB-opslag per module (snelle lokale cache),
  gesynchroniseerd met Supabase zodat data kantoorbreed (multi-device)
  beschikbaar is in plaats van vast te zitten in één browser.
- `supabase/server.ts` — `maakServiceClient()`/`heeftPersistenteOpslag()`,
  enkel server-side (service-role key, nooit in de browser).
- `auth.ts` — lichte login-gate (één gedeeld kantoorwachtwoord, geen
  multi-user systeem). `heeftAuthGate()` is true zodra `APP_WACHTWOORD` en
  `APP_AUTH_SECRET` beide gezet zijn (zie `.env.local.example`); zonder die
  vars staat de gate uit (dev-gemak), net als `heeftPersistenteOpslag()`.
  `maakSessieCookie()`/`valideerSessieCookie()` signeren/verifiëren een
  token via HMAC-SHA256 (`crypto`, geen externe library) met constant-time
  compare. `proxy.ts` (root, Next.js 16's opvolger van `middleware.ts`) past dit toe op alle browser-paginas en
  `/api/sync/*`; de publieke AI-agent-routes (`/api/aktekosten`,
  `/api/modeldocumenten`, `/api/opzoekingen`, `/api/modelbrieven`,
  `/api/verbetervoorstellen`, `/api/mcp`, `/api/openapi.json`,
  `/api/check-actualiteit`) blijven onbeschermd. Inloggen via
  `app/login/page.tsx` → `app/api/auth/login/route.ts`; uitloggen via
  `app/api/auth/logout/route.ts`.
- `api/jsonb-sync-route.ts` — `maakJsonbSyncRoute<T>(tabel)`: generieke
  GET/POST/DELETE-handlers voor een Supabase-tabel met `{id, data}`-jsonb-rijen.
  Gebruikt door alle `app/api/sync/*`-routes (intern, browser-naar-browser
  sync — te onderscheiden van `app/api/*`, dat voor externe AI-agenten is).
  Zonder Supabase-configuratie geeft de route `persistent: false` terug en
  blijft alles lokaal (IndexedDB) werken.
- `hooks/useGesynchroniseerdeOpslag.ts` — generieke clienthook: toont
  IndexedDB-data onmiddellijk, synchroniseert daarna met de bijhorende
  `/api/sync/*`-route (consume/push bij `persistent: true`). Modulehooks
  (`useDossierStorage`, `useModeldocumentenStorage`, `useRegelgevingStorage`)
  zijn dunne wrappers rond deze hook per opslagtype.

### `components/` — gedeelde UI

- `ui/EditableAmount.tsx` — klikbaar bedrag met manuele override + reset.
- `ui/AantalInput.tsx` — "Aantal: n × €prijs" spinner voor stukskosten.
- `BegunstigdenEditor.tsx` — editor voor meerdere begunstigden + het
  `Begunstigde`-model en aandeelhelpers; gebruikt door belasting én ereloon
  (`compact`/`toonPartnerToggle` props sturen de varianten). Met
  `toonRelatie={false}` en `itemLabel="Schenker"` ook herbruikt voor
  meerdere schenkers (enkel aandeel, geen relatie tot het goed).
- `WetReferentie.tsx`, `GewestSelector.tsx`, `Sidebar.tsx`, `regelgeving/`.
- `modeldocumenten/` — alle UI van de modeldocumentenmodule: één bestand per
  tab (`OnderdelenTab`, `ModellenTab`, `VerrijkenTab`, `ValiderenTab`), de
  editors (`OnderdeelEditor`, `ModelEditor`, `VoorstelFormulier`), de
  invul-/generatieflow (`InvulView`) en gedeelde presentatie
  (`StructuurLijst`, hergebruikt door de statische modelpagina's). De pagina
  zelf doet enkel state + compositie.

### `app/` — pagina's

Pagina's bevatten uitsluitend: state (useState/useMemo), compositie van
datalaagfuncties, en JSX. Vuistregels:

- Verschijnt dezelfde logica in twee pagina's → verplaats naar `data/` (als
  het berekening is) of `components/` (als het UI is).
- Een bedrag, tarief of drempel hardcoden in een pagina is een bug.
- Resultaat-useMemo's hangen af van de afgeleide *effectieve* waarden
  (bv. `effectiefEigenWoning`), nooit rechtstreeks van rauwe toggles.

`app/api/` — REST-endpoints voor externe AI-agenten (M365 Copilot, Claude):
`/api/modeldocumenten(/<id>)`, `/api/aktekosten`, `/api/opzoekingen`, plus de
OpenAPI-spec op `/api/openapi.json` (enum-waarden gegenereerd uit de datalaag).
Route handlers zijn dunne schillen rond de pure functies in `data/` — geen
eigen domeinlogica. Enkel seed-data: gebruikersitems (IndexedDB) zijn
server-side niet beschikbaar.

## Conventies

- Taal: Nederlands voor domeinbegrippen (functies, types, labels, commentaar).
- Bedraginvoer is altijd een string-state; parse met `parseBedrag` op het
  moment van berekenen.
- Print/PDF: schermweergave gebruikt Tailwind-printklassen
  (`no-print`, `print-toon`, `print-avoid-break`); PDF-export gaat via
  `lib/pdf/genereer.ts`. Beide vermelden BETA + notarissen Tervuren.
- Bekende schuld: de reset-`useEffect`s in `app/ereloon/page.tsx` triggeren
  `react-hooks/set-state-in-effect`-meldingen (pre-existent patroon).
  Bij een volgende verbouwing: afleiden tijdens render of reducer gebruiken.
