feat: AI-suggesties tijdens het editen van een wet#735
Conversation
… de regelgeving Nieuwe skill die de tekst van een wet-YAML per artikel toetst tegen de Aanwijzingen voor de regelgeving (KCBR) en bevindingen als JSON levert met een TextQuoteSelector (exact/prefix/suffix), zodat de aanroeper ze als W3C- annotatie aan de wettekst kan verankeren. De skill wijzigt de wet niet; de mens accepteert of wijst suggesties af. Quotes worden met genormaliseerde witruimte geleverd; de fuzzy resolver-tier (threshold 0.7) verankert ze ondanks harde regelafbrekingen in de block-scalar wetteksten. Empirisch gevalideerd op artikel 2 van de zorgtoeslagwet. Eerste bouwsteen voor AI-suggesties tijdens het editen van een wet.
Preview Deployment — enrichworkerYour changes have been deployed to a preview environment: URL: https://enrichworker-pr735-regel-k4c.rig.prd1.gn2.quattro.rijksapps.nl This deployment will be automatically cleaned up when the PR is closed. |
…ar output)
Voegt de kern van de editor-suggestietak toe aan de pipeline:
- JobType::SuggestGuidelines + SuggestMachineReadable (migratie 0018 breidt de
job_type enum uit + unieke-actieve-job index per law+traject+kind).
- Gedeelde LLM-runner (llm.rs): provider, env-allowlist, ProcessLlmRunner met
timeout/kill, prompt-based en pipeline-agnostisch zodat enrich en suggest hem
kunnen delen.
- suggest.rs: SuggestPayload/SuggestKind/SuggestConfig, prompt-builders voor de
law-aanwijzingen en law-generate skills, parsing van de skill-JSON, en
conversie naar een W3C-annotatie-sidecar (creator Agent, TextQuoteSelector).
execute_suggest draait de skill, leest de bevindingen en schrijft
annotations/{law_id}/suggestions.yaml (los van de mens-notes).
- law_status: suggest-jobs raken de harvest/enrich law-lifecycle niet
(fail-count/exhaustion zijn no-ops).
Suggesties zijn advisory: ze committen nooit naar de wet zelf. 12 unit-tests
(serde, findings->annotaties, sidecar-vorm, fake-runner end-to-end).
- run_suggest_worker + process_next_suggest_job: claimt suggest-jobs (beide kinds), checkt de traject-branch uit (create_suggest_corpus, sparse op law-dir + features + annotations), draait de skill via execute_suggest, en commit de suggesties-sidecar naar de traject-branch. Draait mee in de enrich_worker-binary zodat er geen nieuwe ZAD-component nodig is. - pipeline-api: POST /suggest (enqueue beide kinds) + GET /suggest/status (laatste status per kind voor law+traject). - job_queue::create_suggest_job_if_not_exists: dedup via de unieke-actieve-job index (23505 -> Ok(None)) zodat snelle saves geen dubbele runs stapelen. Suggesties raken de wet of de law-lifecycle niet; ze schrijven alleen de sidecar op de traject-branch.
- save_law: na een succesvolle traject-save een fire-and-forget POST naar
pipeline-api /suggest (advisory, faalt nooit de save; overgeslagen als de
pipeline niet is geconfigureerd of de traject geen GitHub-branch heeft).
- TrajectCorpus onthoudt nu de writable-branch (traject/{slug}-{8hex}) zodat de
worker weet welke branch hij moet uitchecken en de sidecar op moet committen.
- GET .../suggestions: leest de suggestions.yaml-sidecar van de traject-branch
(los van de mens-annotations.yaml).
- GET .../suggestions/status: proxyt naar pipeline /suggest/status zodat de
editor kan pollen tot de suggesties klaar zijn.
Preview Deployment — harvester-adminYour changes have been deployed to a preview environment: URL: https://harvester-admin-pr735-regel-k4c.rig.prd1.gn2.quattro.rijksapps.nl This deployment will be automatically cleaned up when the PR is closed. |
Frontend voor de suggestiefeature, achter feature-flag panel.suggestions (default off, dark-launch): - useSuggestions.js: leest de suggestions.yaml-sidecar, resolvet via dezelfde WASM-resolver als notes, en pollt /suggestions/status na een save tot de jobs klaar zijn. Mirrort het useNotes-patroon (cache, generation-guard). - SuggestionsPane.vue: lijst van suggesties voor het actieve artikel met Toepassen/Afwijzen per item, gebouwd met nldd-* design-systeemcomponenten. - EditorApp: nieuwe 'suggestions'-view in VIEW_DEFINITIONS + paneel-flag, poll na opslaan, en accept/reject die de annotatie op workflow:resolved zet via de bestaande annotations-write-path. - Gestippelde span-weergave in de Tekst-pane werkt al via AnnotatedText (herkent creator: Agent als generated). Geen extra CSS bovenop het design-systeem.
Preview Deployment — harvester-workerYour changes have been deployed to a preview environment: URL: https://harvester-worker-pr735-regel-k4c.rig.prd1.gn2.quattro.rijksapps.nl This deployment will be automatically cleaned up when the PR is closed. |
…ccept/reject Adresseert drie review-bevindingen: - Worker ruimt de per-job traject-checkout nu op na elke suggest-job (zoals de enrich-worker al deed); voorheen lekte elke save twee git-clones op schijf. - create_suggest_corpus + execute_suggest normaliseren yaml_path (rejecteert '..', absolute paden, shell-metatekens) zoals het enrich-pad, en zoeken de wet op het gematerialiseerde pad. - Accept/reject werkte niet: de pane leest suggestions.yaml maar resolve schreef naar annotations.yaml, dus items kwamen terug. Nu onthoudt useSuggestions resolved suggesties lokaal (localStorage, per traject+wet) en filtert ze uit de lijst. Accepteren kopieert de voorgestelde tekst naar het klembord.
Preview Deployment — editor — editorYour changes have been deployed to a preview environment: URL: https://editor-pr735-regel-k4c.rig.prd1.gn2.quattro.rijksapps.nl This deployment will be automatically cleaned up when the PR is closed. |
🛑 Migration
|
Doel
Tijdens het bewerken van een wet in de editor op de achtergrond met
claude -p(headless Claude Code) suggesties genereren, op twee sporen:
machine_readable-secties terwijl je schrijft.Suggesties verschijnen als annotaties (RFC-018): gestippelde spans in de wettekst
plus een "Aanwijzingen"-pane met accepteren/afwijzen. De trigger is opslaan.
Aanpak
Vooral bestaande, geteste onderdelen aan elkaar knopen:
packages/pipeline/src/enrich.rs) draait alclaude -pvia dePostgreSQL job-queue. De suggestie-tak hergebruikt dat patroon via een gedeelde
llm.rs, met job-typesSuggestGuidelines/SuggestMachineReadable, op detraject-branch i.p.v.
enrich/*.TextQuoteSelector,WASM-resolver,
creator: Agent→ gestippeld inAnnotatedText.vue) is de sink.useSuggestions.js.Fasen (alle gemerged in deze branch)
law-aanwijzingenskill (SKILL.md + reference.md). Sologevalideerd met
claude -pop de zorgtoeslagwet: valide JSON met verankerbare quotes.JobType-varianten (migratie 0018), gedeeldellm.rs,suggest.rs(skill-output → W3C-annotatie-sidecar), 12 unit-tests.run_suggest_worker(draait mee in de enrich-binary),pipeline-api
/suggest+/suggest/status, dedup-helper./suggestionslees-route,/suggestions/statusproxy, traject-branch resolutie.panel.suggestionsflag (off, dark-launch),useSuggestions.js,SuggestionsPane.vue(nldd-componenten), poll-na-save, accept/reject.kind=machine_readable+ law-generate-prompt.Verankering (geverifieerd)
Wetteksten zijn block scalars met harde regelafbrekingen midden in zinnen. De skill
levert quotes met genormaliseerde witruimte; de fuzzy resolver-tier (threshold 0.7)
verankert ze betrouwbaar (~0.97 similarity). Geen engine-wijziging nodig.
Wat is geverifieerd
claude -p→ valide JSON, exacte quotes.cargo test -p regelrecht-pipeline: 43 tests groen (incl. 12 suggest/llm).cargo check/clippyop pipeline + editor-api lib: groen.vite build+ 238 vitest tests: groen.Wat nog een live stack nodig heeft
End-to-end op een draaiende stack (editor + pipeline + DB +
ANTHROPIC_API_KEYin deworker): save → job enqueued → worker draait claude -p → sidecar op traject-branch →
pane vult. Vereist
ANTHROPIC_API_KEYin de enrich/suggest-worker enSKILLS_DIRmet de nieuwe skill in het container-image.
Bewust uit scope (follow-ups)
machineReadable.value; accept zet de suggestie op resolved (human-in-the-loop).llm.rswordt doorsuggest.rsgebruikt;enrich.rshoudt voorlopig zijneigen runner (geen risicovolle refactor van productiecode in deze PR).