Skip to content

feat: AI-suggesties tijdens het editen van een wet#735

Draft
anneschuth wants to merge 6 commits into
mainfrom
feat/editor-ai-suggesties
Draft

feat: AI-suggesties tijdens het editen van een wet#735
anneschuth wants to merge 6 commits into
mainfrom
feat/editor-ai-suggesties

Conversation

@anneschuth

@anneschuth anneschuth commented Jun 3, 2026

Copy link
Copy Markdown
Member

Doel

Tijdens het bewerken van een wet in de editor op de achtergrond met claude -p
(headless Claude Code) suggesties genereren, op twee sporen:

  1. Aanwijzingen voor de regelgeving — toets de wettekst tegen de KCBR-richtlijnen en lever concrete edit-suggesties.
  2. Machine-uitvoerbare YAML — genereer concept-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:

  • De enrichworker (packages/pipeline/src/enrich.rs) draait al claude -p via de
    PostgreSQL job-queue. De suggestie-tak hergebruikt dat patroon via een gedeelde
    llm.rs, met job-types SuggestGuidelines / SuggestMachineReadable, op de
    traject-branch i.p.v. enrich/*.
  • Het notes/annotatie-systeem (W3C Web Annotations, TextQuoteSelector,
    WASM-resolver, creator: Agent → gestippeld in AnnotatedText.vue) is de sink.
  • Transport = polling (zoals harvest-status), geïsoleerd in useSuggestions.js.

Fasen (alle gemerged in deze branch)

  • Skill + contractlaw-aanwijzingen skill (SKILL.md + reference.md). Solo
    gevalideerd met claude -p op de zorgtoeslagwet: valide JSON met verankerbare quotes.
  • Pipeline kernJobType-varianten (migratie 0018), gedeelde llm.rs,
    suggest.rs (skill-output → W3C-annotatie-sidecar), 12 unit-tests.
  • Worker + APIrun_suggest_worker (draait mee in de enrich-binary),
    pipeline-api /suggest + /suggest/status, dedup-helper.
  • Editor-api — enqueue-na-save (fire-and-forget), /suggestions lees-route,
    /suggestions/status proxy, traject-branch resolutie.
  • Frontendpanel.suggestions flag (off, dark-launch), useSuggestions.js,
    SuggestionsPane.vue (nldd-componenten), poll-na-save, accept/reject.
  • machine_readable-tak — zelfde infra, 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

  • Skill solo via claude -p → valide JSON, exacte quotes.
  • cargo test -p regelrecht-pipeline: 43 tests groen (incl. 12 suggest/llm).
  • cargo check/clippy op pipeline + editor-api lib: groen.
  • Frontend 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_KEY in de
worker): save → job enqueued → worker draait claude -p → sidecar op traject-branch →
pane vult. Vereist ANTHROPIC_API_KEY in de enrich/suggest-worker en SKILLS_DIR
met de nieuwe skill in het container-image.

Bewust uit scope (follow-ups)

  • One-click apply van een machine_readable-suggestie patcht nu nog niet automatisch
    machineReadable.value; accept zet de suggestie op resolved (human-in-the-loop).
  • Gedeelde llm.rs wordt door suggest.rs gebruikt; enrich.rs houdt voorlopig zijn
    eigen runner (geen risicovolle refactor van productiecode in deze PR).

… 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.
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Preview Deployment — enrichworker

Your 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.
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Preview Deployment — harvester-admin

Your 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.
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Preview Deployment — harvester-worker

Your 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.
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Preview Deployment — editor — editor

Your 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.

@uittenbroekrobbert

Copy link
Copy Markdown

🛑 Migration 0018 crashes the harvester on startup

Heads-up from the RIG/ZAD platform side: this PR's preview deployment (regel-k4c-pr735) has its harvester / admin / enrich pods in CrashLoopBackOff (1481 restarts on harvester-admin, 831 on harvester-worker). The volume of restart → reconnect → retry attempts tripped our monitoring alerts, which is how this surfaced.

The problem

packages/pipeline/migrations/0018_suggest_job_types.sql fails on every run:

migration error: while executing migration 18:
operator does not exist: job_type = job_type_old

The migration never applies → the worker exits on startup → CrashLoopBackOff. This is the migration failing deterministically, not a preview-env glitch.

Why it happens

The migration recreates the job_type enum via rename/swap (since ALTER TYPE … ADD VALUE can't run in a transaction). That's the same pattern used safely for law_statusbut job_type is referenced by the predicates of two partial indexes from earlier migrations:

  • idx_unique_active_enrich_job … WHERE job_type = 'enrich' … (0003)
  • idx_unique_active_harvest_job … WHERE job_type = 'harvest' … (0011)

When ALTER COLUMN … TYPE rebuilds those indexes, their predicate literals are still bound to the renamed job_type_old, so there's no comparison operator between the new column type and the old type → the error. The migration is transactional, so it rolls back cleanly (no half-migrated DB) — it just never succeeds. law_status has no such dependent indexes, which is why the pattern looked proven.

How to fix

Drop the two dependent indexes before the swap, recreate them after:

DROP INDEX idx_unique_active_enrich_job;
DROP INDEX idx_unique_active_harvest_job;

ALTER TYPE job_type RENAME TO job_type_old;
CREATE TYPE job_type AS ENUM ('harvest','enrich','suggest_guidelines','suggest_machine_readable');
ALTER TABLE jobs ALTER COLUMN job_type TYPE job_type USING job_type::text::job_type;
DROP TYPE job_type_old;

-- recreate against the new type (confirm exact defs against 0003/0011)
CREATE UNIQUE INDEX idx_unique_active_enrich_job
    ON jobs (law_id, job_type, (payload->>'provider'))
    WHERE job_type = 'enrich' AND status IN ('pending','processing');
CREATE UNIQUE INDEX idx_unique_active_harvest_job
    ON jobs (law_id, job_type)
    WHERE job_type = 'harvest' AND status IN ('pending','processing');

-- the new suggest index (unchanged from this PR)
CREATE UNIQUE INDEX idx_unique_active_suggest_job
    ON jobs (law_id, job_type, (payload->>'traject_ref'))
    WHERE job_type IN ('suggest_guidelines','suggest_machine_readable')
      AND status IN ('pending','processing');

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants