Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .specter-prompt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ You are running HEADLESS. Do NOT use AskUserQuestion, interactive prompts, or sk
Write files directly using the Write tool.

## Inputs
- Change: mcp-tools (title: mcp-tools Specification)
- Context brief: openspec/changes/mcp-tools/context-brief.md
- Change: commissievergaderingen (title: Commissievergaderingen)
- Context brief: openspec/changes/commissievergaderingen/context-brief.md
- Data model ADR: openspec/architecture/adr-000-data-model.md

## Step 1: Read context
Read these files COMPLETELY before writing anything:
- openspec/changes/mcp-tools/context-brief.md (features, stories, stakeholders, journeys)
- openspec/changes/commissievergaderingen/context-brief.md (features, stories, stakeholders, journeys)
- openspec/architecture/adr-000-data-model.md (entity definitions)
- All files in openspec/architecture/ (app ADRs)
- All files in .claude/openspec/architecture/ (company ADRs, if present)

## Step 2: Get artifact templates
Run: `openspec instructions proposal --change mcp-tools --json`
Run: `openspec instructions proposal --change commissievergaderingen --json`
This returns the template and rules for the proposal artifact.
Then do the same for: design, specs, tasks (in that order).

Expand All @@ -35,8 +35,8 @@ For each artifact (proposal → design → specs → tasks):
## Step 4: Commit
After all 4 artifacts are written:
```bash
git add openspec/changes/mcp-tools/
git commit -m 'feat: Add OpenSpec change mcp-tools from Specter'
git add openspec/changes/commissievergaderingen/
git commit -m 'feat: Add OpenSpec change commissievergaderingen from Specter'
```
Do NOT push — the caller handles pushing.
Do NOT create branches — stay on the current branch.
Expand Down
2 changes: 2 additions & 0 deletions openspec/changes/commissievergaderingen/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-05-22
206 changes: 206 additions & 0 deletions openspec/changes/commissievergaderingen/context-brief.md

Large diffs are not rendered by default.

280 changes: 280 additions & 0 deletions openspec/changes/commissievergaderingen/design.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions openspec/changes/commissievergaderingen/hydra.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"spec_slug": "commissievergaderingen",
"title": "Commissievergaderingen",
"app": "decidesk",
"repo": "https://github.com/ConductionNL/decidesk",
"depends_on": [],
"issue": null
}
103 changes: 103 additions & 0 deletions openspec/changes/commissievergaderingen/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
kind: feature
depends_on: [decidesk-base]
---

# Commissievergaderingen

## Why

Vrijwel elke Nederlandse gemeenteraad werkt met raadscommissies als voorbereidende organen op de plenaire raadsvergadering. De griffie moet commissievergaderingen plannen, samenstelling beheren, belangenconflicten registreren, en commissieadviezen traceerbaar koppelen aan plenaire besluiten. Commissieleden en burgers hebben grote handmatige lasten: fractie-voorzitters moeten per e-mail plaatsvervangers doorgeven, commissieadviezen verdwijnen in PDF-bijlagen, en insprekers moeten hun persoonlijke gegevens publiek maken.

Deze spec breidt Decidesk uit met een eersteklas commissie-model dat:
- Sub-vergaderingen voor commissies beheert (planning, agendavoorbereiding, verslaglegging)
- Vaste samenstelling per commissie (leden, plaatsvervangers, voorzitter, griffier) bijhoudt
- Belangenverstrengelings-declaraties per agendapunt verplicht stelt (artikel 28 Gemeentewet)
- Commissieadvies automatiseert naar de plenaire raadsagenda (met fractie-standpunten als gestructureerde data)
- Inspraak-aanmelding biedt via publiek portaal met privacy-scheiding (contactgegevens intern, onderwerp publiek)
- Besloten zittingen ondersteunt met aparte toegangscontrole en audit-trail voor Woo-openbaarmaking

De spec adresseert drie pijnpunten uit griffies-interviews:
1. **Plaatsvervanging**: Fractie-voorzitter geeft plaatsvervanger door via fractie-portaal; presentie en stemrecht synchroniseren automatisch
2. **Advies-tracering**: Commissieadvies wordt eersteklas annotatie op plenaire agendapunt met fractie-standpunten, niet losse PDF
3. **Privacy bij inspraak**: Burgers melden inspraak aan, contactgegevens blijven intern, inspraak-onderwerp en spreker-naam worden publiek

## What Changes

- **NEW** 8 schemas: `Commissie`, `CommissieLidmaatschap`, `CommissieVergadering`, `CommissieAgendapunt`, `CommissieAdvies`, `BelangenverstrengelingDeclaratie`, `InspraakAanmelding`, `Presentielijst`
- **NEW** `lib/Settings/commissievergaderingen_register.json` met schema-definities en seed data (3-5 Nederlandse commissies met leden en vergaderingen)
- **NEW** `lib/Migration/CommissionRegistration.php` die de register via `ConfigurationService::importFromApp()` importeert
- **MODIFIED** `lib/AppInfo/Application.php` – registratie van CommissionRegistration
- **NEW** `lib/Service/CommissionService.php` – business logic voor commissie-beheer (samenstelling, plaatsvervanging)
- **NEW** `lib/Service/CommissionMeetingService.php` – commissievergadering-lifecycle en adviesvorming
- **NEW** `lib/Controller/CommissionController.php` – REST API voor commissies en leden
- **NEW** `lib/Controller/CommissionMeetingController.php` – REST API voor commissievergaderingen
- **NEW** `lib/Controller/CommissionAdviceController.php` – adviesvorming en publicatie naar plenaire raad
- **NEW** frontend views voor griffie en commissieleden (Vue.js met Nextcloud Vue)
- **NEW** `tests/Unit/Service/CommissionServiceTest.php` en bijbehorende integration tests
- **NEW** `docs/features/commissievergaderingen.md` – operator docs en gebruikers-handleiding

## Capabilities

### New Capabilities

- `commissie-management`: Commissies instellen op basis van raadsbesluit, samenstelling beheren per fractie, plaatsvervangers aanwijzen
- `commissie-vergaderingen`: Vergaderingen plannen, agenda opstellen, agendapunten typen (mededeling, bespreekstuk, advies-aan-raad, inspraak, besloten), verslaglegging
- `adviesvorming`: Commissieadvies per agendapunt (strekking, tekst, fractie-standpunten), automatische doorgeleiding naar plenaire raad
- `belangenverstrengeling`: Declaratie per commissielid per agendapunt (financieel-belang, aandeelhouderschap, bestuursfunctie, familierelatie, eerder-betrokken, geen), gevolg (onthoudt-zich, verlaat, meldt-maar-blijft)
- `inspraak-aanmelding`: Burgers en organisaties kunnen zich aanmelden voor inspraak via publiek portaal; griffie goedkeurt/wijst af; contactgegevens gescheiden van publieke onderwerp
- `besloten-zittingen`: Agendapunten kunnen als besloten gemarkeerd worden; publieke livestream en portaal onderbr oken; aparte audit-trail voor Woo-openbaarmaking
- `presentielijst-met-plaatsvervanging`: Dynamische presentielijst met aanwezigheid-status, plaatsvervanger-indicatie, aankomst- en vertrektijd
- `commissie-verordening-synchronisatie`: Commissie gekoppeld aan verordening-versie; wijzigingen traceerbaar vastgelegd

### Modified Capabilities

- `p2-meeting-management`: CommissieVergadering is sub-type van Meeting; vererft meeting-lifecycle maar voegt commissie-specifieke velden toe
- `p2-decision-flow`: CommissieAdvies wordt linked annotation op plenaire agendapunt; fractie-standpunten als gestructureerde data
- `p3-governance-bodies`: GovernanceBody krijgt commissie-relaties; Commissie heeft raadslid-koppelingen via CommissieLidmaatschap

## Impact

**Backend:**
- `lib/Settings/commissievergaderingen_register.json` (new)
- `lib/Migration/CommissionRegistration.php` (new)
- `lib/Service/CommissionService.php` (new)
- `lib/Service/CommissionMeetingService.php` (new)
- `lib/Controller/CommissionController.php` (new)
- `lib/Controller/CommissionMeetingController.php` (new)
- `lib/Controller/CommissionAdviceController.php` (new)
- `lib/AppInfo/Application.php` (modified – migration registration)

**Frontend:**
- `src/views/CommissionList.vue` (new)
- `src/views/CommissionDetail.vue` (new)
- `src/views/CommissionMeetingAgenda.vue` (new)
- `src/components/CommissionAdvicePanel.vue` (new)
- `src/components/ConflictDeclaration.vue` (new)
- `src/components/InspraakPanel.vue` (new)

**Data:**
- 8 new OpenRegister schemas; no existing data affected

**Dependencies:**
- Decidesk base (decidesk-base)
- OpenRegister app (runtime autoloader)
- Nextcloud Vue for frontend components

**Other apps:**
- `docudesk`: PDF-rendering van commissie-verslagen met eIDAS-handtekening
- `opentalk`: Livestream van openbare commissievergaderingen met tijdcode-koppeling
- `opencatalogi`: Publicatie van publieke commissie-stukken als TOOI-catalogus
- `openconnector`: Synchronisatie naar iBabs/Notubiz; agenda-sync naar Outlook/Google
- `mydash`: Dashboards voor griffie (commissie-activiteit, attendance-trends, advies-ratio)
- `openklant`: Inspraak-aanmeldingen via openklant contactgegevens-beheer

## Out of Scope

- Frontend UI in v1 (REST API only; UI follows in p3 sprint)
- Audio/video-recording integration (opentalk-koppeling volgt in p3)
- Livestream-onderbreking bij besloten zitting (opentalk-specifiek, volgt in p3)
- PDF-watermarking voor besloten stukken (docudesk-integratie volgt in p3)
- Automatische kalender-sync (openconnector volgt in p3)
- Dashboard-rendering (mydash volgt in p3)
- Per-role tool visibility voor MCP tools (v2)
142 changes: 142 additions & 0 deletions openspec/changes/commissievergaderingen/specs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Specs — Commissievergaderingen

## REQ-CVG-001: Commissie instellen op basis van raadsbesluit

**GIVEN** de raad heeft een commissie ingesteld via een formeel raadsbesluit
**WHEN** de griffier via de API een Commissie-record aanmaakt met `instellings-besluit`-referentie
**THEN** wordt de Commissie gekoppeld aan het instellings-besluit via `instellings_besluit_id`
**AND** is de Commissie nadien onwijzigbaar zonder verwijzing naar een wijzigings-raadsbesluit
**AND** verschijnt de Commissie in het publieke register van bestuurlijke organen (TOOI-export)

## REQ-CVG-002: Samenstelling beheren met fractie-evenredigheid

**GIVEN** een Commissie heeft vaste samenstelling en fractie-verdeling wijzigt in de raad
**WHEN** de griffier de Commissie-samenstelling herziet via `PUT /commissions/{id}/composition`
**THEN** wordt per fractie het aantal commissiezetels berekend volgens het Reglement van Orde van de gemeente
**AND** kan elke fractie-voorzitter via fractie-portaal haar commissieleden aanwijzen
**AND** wordt de gewijzigde-samenstelling-melding vastgelegd met ingangsdatum en vorig-besluit-referentie

## REQ-CVG-003: Plaatsvervanging bij afwezigheid

**GIVEN** een CommissieLid kan niet aanwezig zijn bij een CommissieVergadering
**WHEN** het lid zich afmeldt via `/meetings/{meeting_id}/absences` en een plaatsvervanger uit zijn fractie-groep aanwijst
**THEN** ontvangt de plaatsvervanger automatisch agenda en dossier via e-mail-notificatie
**AND** wordt de Presentielijst bijgewerkt: `plaatsvervanger_door`-veld toont plaatsvervanger plaats van afgemeld lid
**AND** kan de plaatsvervanger meestemmen in adviesvorming en gaat zijn stemming naar fractie-count (niet dubbel)

## REQ-CVG-004: Belangenverstrengelingsdeclaratie per agendapunt

**GIVEN** een CommissieVergadering start en een CommissieAgendapunt wordt geopend
**WHEN** voor elk CommissieLid (rol = lid, plaatsvervanger, voorzitter) nog geen BelangenverstrengelingDeclaratie voor dit agendapunt bestaat
**THEN** wordt per lid een declaratie-record aangemaakt met `soort: 'nog-in-te-vullen'`
**AND** wordt lid via notificatie gevraagd declaratie in te vullen (JSON POST naar `/declarations/{id}`)
**AND** kan lid waarde wijzigen naar financieel-belang, aandeelhouderschap, bestuursfunctie, familierelatie, eerder-betrokken, of geen
**AND** bij `soort !== 'geen'` krijgen voorzitter + griffier automatisch alert met lid + soort + gevolg

## REQ-CVG-005: Gevolg van belangenverstrengeling (onthoudt-zich, verlaat, meldt-maar-blijft)

**GIVEN** een CommissieLid heeft BelangenverstrengelingDeclaratie met `soort !== 'geen'` ingediend
**WHEN** de voorzitter de gevolg-optie kiest (onthoudt-zich-van-stemming, verlaat-vergadering, meldt-maar-blijft)
**THEN** wordt gevolg opgeslagen op declaratie-record
**AND** bij `onthoudt-zich`: lid blijft op presentielijst maar krijgt geen stemrecht op dit agendapunt
**AND** bij `verlaat`: lid wordt van presentielijst afgehaald (vertrek-time geregistreerd)
**AND** bij `meldt-maar-blijft`: notatie wordt in verslag opgenomen, lid behoudt stemrecht

## REQ-CVG-006: Adviesvorming met fractie-standpunten

**GIVEN** een CommissieVergadering behandelt een CommissieAgendapunt van type `advies-aan-raad`
**WHEN** de voorzitter de beraadslaging afsluit via `POST /agenda-items/{id}/finalize-advice`
**THEN** wordt voor elke fractie met leden in commissie gevraagd hun standpunt (voor, tegen, verdeeld, geen-mening)
**AND** wordt CommissieAdvies aangemaakt met:
- `advies-strekking`: samengestelde aanbeveling (positief, negatief, verdeeld, geen-advies, verzoek-tot-aanpassing)
- `fractie-standpunten`: array van {fractie-naam, standpunt}
- `advies-tekst`: rich-text samenvatting van beraadslaging
- `stemverhouding-samenvatting`: bijv "3 voor, 1 tegen, 1 verdeeld"
**AND** wordt CommissieAdvies automatisch gekoppeld aan het beoogd-raadsvoorstel (plenaire AgendaItem) via relatie

## REQ-CVG-007: Commissieadvies op plenaire agendapunt

**GIVEN** een plenaire raadsvergadering bereidt een agendapunt voor dat eerder in commissie is voorbesproken
**WHEN** griffier of raadslid het CommissieAdvies opvraagt via `/agenda-items/{id}/commission-advice`
**THEN** verschijnt het CommissieAdvies in volledige vorm:
- Titel vorige agendapunt (commissie)
- Advies-strekking
- Fractie-standpunten gegroepeerd per fractie
- Advies-tekst
- Link naar commissie-verslag
**AND** kan raadslid dit advies gebruiken ter voorbereiding plenaire beraadslaging

## REQ-CVG-008: Thema-bijeenkomst zonder advies-vorming

**GIVEN** de griffie organiseert een raadsbrede beeldvormende sessie (geen advies)
**WHEN** CommissieVergadering wordt gemarkeerd met `type: 'thema-bijeenkomst'`
**THEN** vervalt de verplichte adviesvorming-stap
**AND** kunnen alle raadsleden (niet alleen commissieleden) zich aanmelden via `/public-api/registration`
**AND** wordt presentielijst en lichtgewicht verslag opgesteld (geen stemverhoudingen)

## REQ-CVG-009: Inspraak-aanmelding via publiek portaal

**GIVEN** een CommissieVergadering staat gepland en burger wil inspreken op specifiek agendapunt
**WHEN** burger zich aanmeldt via publiek portaal `POST /public-api/commissions/{id}/inspraak`
**THEN** wordt InspraakAanmelding aangemaakt met twee veldgroepen:
- `contactgegevens` (naam, email, telefoon, adres — alleen voor griffie zichtbaar)
- `onderwerp` (spreker-naam (kan pseudoniem), organisatie, spreektijd-aanvraag — publiek)
**AND** griffier ziet aanvraag in griffie-portaal en kan goedkeuren, afwijzen (met reden), of doorverwijzen
**AND** burger ontvangt bevestiging met spreektijd-toewijzing
**AND** sluit aanmelding automatisch 24 uur voor vergadering (konfigurabel per commissie via `inspraak-deadline-uren`)

## REQ-CVG-010: Besloten zitting met aparte toegangscontrole

**GIVEN** een CommissieAgendapunt is gemarkeerd als type `besloten-onderdeel` (personeels-aangelegenheid, grond-aankoop)
**WHEN** CommissieVergadering bij dit punt aanvangt
**THEN** wordt het publieke portaal en eventuele livestream onderbroken
**AND** krijgen alleen Commissieleden, voorzitter, griffier en expliciet uitgenodigde adviseurs (via lijst) toegang tot agendapunt-stukken
**AND** wordt een aparte besloten-zittings-notulen-track aangemaakt in CommissieVergadering (veld `besloten_verslag`)
**AND** audittrace worden geregistreerd: wie heeft wanneer besloten-stukken ingezien

## REQ-CVG-011: Verslag publicatie naar griffieportaal

**GIVEN** een CommissieVergadering is afgesloten en conceptverslag is goedgekeurd in volgende vergadering
**WHEN** griffier het verslag via API als definitief markeert (`PUT /meetings/{id}/minutes` met `status: 'approved'`)
**THEN** wordt verslag gepubliceerd op griffieportaal met:
- Volledige agendalist
- Presentielijst (met afwezigen-reden waar getoond)
- CommissieAdviezen (per agendapunt waar advies gegeven)
- InspraakAanmeldingen (publieke delen: spreker-naam, organisatie, onderwerp)
- Definitief verslag (rich text)
**AND** wordt OWMS-metadata gegenereerd voor zoekmachine-indexering en opendata-export (Via Who/Woo)

## REQ-CVG-012: Conflict-of-interest rapportage per periode

**GIVEN** de rekenkamer, journalist, of raadslid wil zicht op gemelde belangen
**WHEN** gebruiker via griffie-portaal rapportage `belangen-overzicht` opvraagt voor periode (bv "januari-maart 2026")
**THEN** wordt overzicht gegenereerd van alle BelangenverstrengelingDeclaraties per CommissieLid in die periode, gegroepeerd op soort (financieel-belang, aandeelhouderschap, etc.)
**AND** worden alleen openbare delen getoond (besloten-zittings-declaraties worden afgeschermd)
**AND** is rapport exporteerbaar als CSV/PDF voor archief/audit

## REQ-CVG-013: Commissie-verordening synchronisatie

**GIVEN** raad wijzigt Verordening op de Raadscommissies (bv aantal commissies, samenstelling, spreektijd-limits)
**WHEN** griffier de gewijzigde verordening importeert via import-wizard
**THEN** wordt Commissie-record gekoppeld aan verordening-versie via immutable-referentie
**AND** worden Commissie-wijzigingen (samenstelling, type, frequentie) gevalideerd tegen verordening-tekst (logische checks via rules-engine)
**AND** eerdere CommissieVergaderingen tonen met op-dat-moment-geldende verordening-regels (bv "spreektijd destijds 3min, nu 5min")
**AND** wordt elke gemeente-specifieke afwijking gemarkeerd in audit-trail

---

## Implementation Notes

- REQ-CVG-004 en REQ-CVG-005 implementeren artikel 28 Gemeentewet (verhindering belangenverstrengeling)
- REQ-CVG-009 implementeert privacy (contactgegevens ↔ publieke inspraak) conform AVG
- REQ-CVG-010 implementeert artikel 86 Gemeentewet (geheimhouding besloten zitting) + audit-trail voor Woo-toetsing
- REQ-CVG-011 en REQ-CVG-012 implementeren Woo-transparantie + Archiefwet-naleving (bewaartermijn)
- REQ-CVG-013 implementeert traceerbaarheid naar juridisch-bindend raadsbesluit (verdedigingsstrategie tegen bestuursrechter-bezwaar)

## Data Integrity Constraints

- `CommissieLidmaatschap.fractie` is immutable snapshot; wijziging vereist eindigen-date + nieuw record
- `BelangenverstrengelingDeclaratie` per lid-agendapunt combinatie is uniek; create-if-not-exists pattern
- `CommissieAdvies` kan alleen ontstaan na beraadslaging (CommissieAgendapunt.status: 'behandeld')
- `Presentielijst` één per CommissieVergadering; wijzigingen via transactie-pattern (versioning)
- `InspraakAanmelding` onveranderlijk na `status: 'goedgekeurd'`; geen edit-after-approval
Loading