feat(kwaliteit): twee badges, leesbare bevindingen en acceptatie die je kunt terugdraaien #357

Merged
brenno merged 7 commits from feat/privacy-quality-badges into main 2026-07-19 13:46:36 +00:00
Owner

Vier klachten over de privacy- en kwaliteitsdetectie, die alle vier op hetzelfde neerkwamen: de app waarschuwde wel, maar gaf geen inzicht en liet je niet antwoorden.

1. Je kon niet zien wát er gevonden was. De scanner wist het allang — veld, fragment, begin- en eindpositie — maar die kennis sneuvelde in de vertaalslag naar het kwaliteitspaneel. Wat je overhield was "Slide 5 · Privacy — persoonsnaam (B…r)". Het paneel zet het veld er nu bij ("Opsomming 3"), en klikken op een melding selecteert het gemelde stuk tekst in het editorveld. Dat werkt ook in opsommingen, waar de meeste tekst staat — met de omrekening die daarbij hoort, want de scanner leest de ruwe bullet (tabs, [ ] ) en het tekstveld toont de kale tekst.

2. Een geaccepteerde bevinding verdween spoorloos. De melding ging uit het paneel én de badge van de thumbnail, en daarna zag die slide er precies zo uit als een slide waarop niets staat. Accepteren was hetzelfde geworden als verbergen. De badge blijft nu staan en wordt grijs; het paneel blijft wél stil, want een genomen beslissing hoort niet te blijven zeuren.

3. Kwaliteitsproblemen waren niet te accepteren. Nu wel, per slide, als <!-- ocideck_quality: accept -->. Twee standen en geen vier: een contrastprobleem heeft geen ontvanger om te waarschuwen en niets om weg te lakken.

4. Dubbelklikken beslist. Op een gekleurde badge accepteer je, op een grijze draai je terug. Bij redact en shield doet dubbelklikken bewust niets — dat zou zwart gelakte persoonsgegevens terugzetten in je export zonder dat iemand erom vroeg.

Onderweg viel op dat privacy in de kwaliteitsbadge gevouwen zat, dus hetzelfde oranje bolletje kon contrast, tekstdichtheid óf een BSN betekenen. Privacy heeft nu een eigen badge. Dat verklaart waarschijnlijk ook de oorspronkelijke klacht dat er "ten onrechte" persoonsgegevens op een slide verondersteld werden.

Eén bewuste asymmetrie: een onzekere privacytreffer krijgt wél een (gedempte) badge, een kwaliteitstip niet. Een tip is advies over vakmanschap; een onzekere treffer is een mogelijk persoonsgegeven.

Twee dingen die de poorten aanwezen en die het vermelden waard zijn: de AppShell overschreef deckQualityProvider met een gekopieerde berekening, waardoor de nieuwe filter per tab — de enige plek die de gebruiker ziet — níét gewerkt zou hebben; en de oude badge ontsnapte aan de Color(0x…)-ratchet door Color(cond ? 0x… : 0x…) te schrijven.

make check groen: 3461 tests, dekking 79,4%.

🤖 Generated with Claude Code

Vier klachten over de privacy- en kwaliteitsdetectie, die alle vier op hetzelfde neerkwamen: de app waarschuwde wel, maar gaf geen inzicht en liet je niet antwoorden. **1. Je kon niet zien wát er gevonden was.** De scanner wist het allang — veld, fragment, begin- en eindpositie — maar die kennis sneuvelde in de vertaalslag naar het kwaliteitspaneel. Wat je overhield was "Slide 5 · Privacy — persoonsnaam (B…r)". Het paneel zet het veld er nu bij ("Opsomming 3"), en klikken op een melding selecteert het gemelde stuk tekst in het editorveld. Dat werkt ook in opsommingen, waar de meeste tekst staat — met de omrekening die daarbij hoort, want de scanner leest de ruwe bullet (tabs, `[ ] `) en het tekstveld toont de kale tekst. **2. Een geaccepteerde bevinding verdween spoorloos.** De melding ging uit het paneel én de badge van de thumbnail, en daarna zag die slide er precies zo uit als een slide waarop niets staat. Accepteren was hetzelfde geworden als verbergen. De badge blijft nu staan en wordt grijs; het paneel blijft wél stil, want een genomen beslissing hoort niet te blijven zeuren. **3. Kwaliteitsproblemen waren niet te accepteren.** Nu wel, per slide, als `<!-- ocideck_quality: accept -->`. Twee standen en geen vier: een contrastprobleem heeft geen ontvanger om te waarschuwen en niets om weg te lakken. **4. Dubbelklikken beslist.** Op een gekleurde badge accepteer je, op een grijze draai je terug. Bij `redact` en `shield` doet dubbelklikken bewust niets — dat zou zwart gelakte persoonsgegevens terugzetten in je export zonder dat iemand erom vroeg. Onderweg viel op dat privacy in de kwaliteitsbadge gevouwen zat, dus hetzelfde oranje bolletje kon contrast, tekstdichtheid óf een BSN betekenen. Privacy heeft nu een eigen badge. Dat verklaart waarschijnlijk ook de oorspronkelijke klacht dat er "ten onrechte" persoonsgegevens op een slide verondersteld werden. Eén bewuste asymmetrie: een onzekere privacytreffer krijgt wél een (gedempte) badge, een kwaliteitstip niet. Een tip is advies over vakmanschap; een onzekere treffer is een mogelijk persoonsgegeven. Twee dingen die de poorten aanwezen en die het vermelden waard zijn: de AppShell overschreef `deckQualityProvider` met een gekopieerde berekening, waardoor de nieuwe filter per tab — de enige plek die de gebruiker ziet — níét gewerkt zou hebben; en de oude badge ontsnapte aan de `Color(0x…)`-ratchet door `Color(cond ? 0x… : 0x…)` te schrijven. `make check` groen: 3461 tests, dekking 79,4%. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
De privacyscanner weet precies waar een bevinding staat — veldnaam,
fragmentindex, begin- en eindpositie — maar die kennis sneuvelde in de
vertaalslag naar het kwaliteitspaneel. Wat de auteur overhield was
"Slide 5 · Privacy — persoonsnaam (B…r)": genoeg om te weten dát er iets
is, te weinig om het te vinden. Op een slide met veertig regels tekst is
dat geen melding maar een zoekopdracht.

SlideQualitySpan draagt de positie nu mee tot in de UI. Het paneel zet
het veld in de kop ("Slide 5 · Privacy · Opsomming 3"), en klikken op een
melding selecteert het gemelde fragment in het editorveld — de
accentuering die de gebruiker al kent uit elk tekstveld, meteen te
kopiëren of over te typen.

De gevonden waarde blijft buiten beeld. Alleen de coördinaten reizen mee;
het gemaskeerde fragment blijft het enige dat van de waarde zelf te zien
is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Opsommingen zijn waar de meeste tekst staat en dus waar de meeste
privacybevindingen landen, maar juist daar deed het springen niets: alleen
`title` en `quote` gaven hun veld ooit door. Een melding wees naar de
slide, en daarna mocht de auteur zelf de goede regel zoeken.

De opsommingseditor luistert nu mee en zet de cursor op het gemelde item.
Dat vroeg één omrekening die makkelijk te missen is: de scanner leest de
bullet zoals hij in de markdown staat — met tabs voor het niveau en
`[ ] ` voor een checklist-item — terwijl het tekstveld alleen de kale
tekst bevat. Een positie uit de scan één op één toepassen accentueert dus
het verkeerde stuk, precies zo veel te ver naar rechts als de opmaak lang
is. Alles wat gestript wordt is een prefix, dus het lengteverschil ís de
verschuiving; `endsWith` toetst die aanname in plaats van haar aan te
nemen.

Valt het bereik buiten de tekst — de auteur typte door terwijl het paneel
openstond — dan gebeurt er niets. Liever geen accentuering dan een
verkeerde.

De opsommingseditors lezen hierdoor de editorstate, en een
Riverpod-consumer zonder scope gooit meteen; twee testharnassen krijgen er
daarom een ProviderScope bij.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
De kwaliteitscontrole kon tot nu toe alleen maar gelijk hebben. Contrast,
tekstdichtheid, ontbrekende alt-tekst: terecht in het algemeen, en soms
onterecht op deze ene slide. Een titelbeeld dat met opzet rustig
contrasteert, een tabel die nu eenmaal veel rijen heeft — daar viel niets
over te zeggen. De melding bleef staan, de badge bleef oranje, en het
enige wat de auteur leerde was dat hij badges kon negeren. Dat is precies
de schade die de privacycontrole met haar disposities al vermijdt.

QualityDisposition kent daarom twee standen, en bewust geen vier: bij
privacy bestaan `shield` en `redact` omdat er iets met de ontvanger en met
de gegevens zelf moet gebeuren, maar een contrastprobleem heeft geen
ontvanger om te waarschuwen en niets om weg te lakken. Het is er, of de
auteur zegt dat het zo bedoeld is.

Slide-niveau alleen: een deck dat in één keer alle contrastfouten
accepteert is geen keuze maar een uitschakelaar, en die bestaat al bij de
instellingen. Een onbekende waarde valt terug op `warn` en niet op
`accept` — een nieuwer OciDeck mag een ouder exemplaar geen meldingen
laten onderdrukken die het niet begrijpt.

Onderweg één ding rechtgezet dat bijna stil misging: de AppShell
overschreef deckQualityProvider met een gekopiëerde berekening, dus de
nieuwe filter zou per tab — de enige plek die de gebruiker ooit ziet —
niet gewerkt hebben. Beide providers draaien nu op een benoemde
top-levelfunctie, zoals de privacy-providers dat al deden.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Twee klachten die dezelfde oorzaak hadden.

De eerste: privacy zat in de kwaliteitsbadge gevouwen, dus hetzelfde
oranje bolletje kon contrast betekenen, of tekstdichtheid, of een BSN in
de tekst. Wie een deck nakeek op persoonsgegevens kon niet zien wélke
slides daarover gingen — en wie een contrastwaarschuwing zag, kon denken
dat er persoonsgegevens stonden. Privacy krijgt daarom een eigen badge met
het PrivacyKat-merkteken, ernaast in plaats van erin.

De tweede: een slide waarvan je de bevinding accepteerde, verdween
spoorloos. De melding ging uit het paneel én de badge van de thumbnail, en
daarna zag die slide er precies zo uit als een slide waarop niets
gevonden is. Accepteren werd zo hetzelfde als verbergen. Beide badges
lezen nu de rúwe uitslag en worden grijs in plaats van te verdwijnen:
"hier is iets, en jij weet ervan". Het paneel blijft stil — daar hoort een
genomen beslissing niet meer te zeuren.

Eén asymmetrie is opzettelijk: een onzekere privacytreffer krijgt wél een
badge (gedempt), een kwaliteitstip niet. Een tip is advies over
vakmanschap; een onzekere treffer is een mogelijk persoonsgegeven. Dat
zijn niet dezelfde belangen, dus niet dezelfde drempel.

De schermlezer krijgt hetzelfde onderscheid: twee badges, twee
mededelingen. Ze samenvatten als één "Kwaliteitsprobleem" zou precies de
vraag laten staan die de badges beantwoorden.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
De badge zei dát er iets was, en niet wát. Dat was de klacht, letterlijk:
"er zouden zaken op slide 5 staan die gevoelig zijn, maar mij is niet
duidelijk welke informatie dat betreft."

Eén klik op de badge opent nu de meldingen van díe slide, verankerd aan de
badge zelf — regel, veld en gemaskeerd fragment, dus "bankrekeningnummer
(N…6), Opsomming 3" in plaats van een oranje bolletje. Het lijstje leest
de rúwe uitslag, want het bestaat juist om een geaccepteerde bevinding te
kunnen lezen: uit de gefilterde uitslag zou de popover van een grijze
badge leeg zijn, en dan is grijs weer net zo nietszeggend als verdwenen.

Dubbelklik beslist. Op een gekleurde badge accepteer je wat er staat, op
een grijze draai je die acceptatie terug. Dat het twee kanten op werkt is
niet symmetrie om de symmetrie: een beslissing die je niet met hetzelfde
gebaar kunt terugnemen, durf je niet te nemen.

Eén uitzondering, en die is opzettelijk: bij `redact` en `shield` doet
dubbelklikken niets. Die standen doen iets met de gegevens zelf of met de
ontvanger, en ze met een dubbelklik terugdraaien zou zwart gelakte
persoonsgegevens weer in de export zetten zonder dat iemand erom vroeg.
Die keuze hoort in het instellingenpaneel van de slide, waar je ziet wat
je kiest.

Terug naar "waarschuwen" kan op twee manieren, en welke goed is hangt van
het deck af. Erft de slide van een deck dat zelf accepteert, dan zet de
slide `warn` expliciet neer — anders erft hij de acceptatie meteen terug
en lijkt de dubbelklik niets te doen. Staat het deck op de standaard, dan
gaat de directive gewoon weg.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs+stijl(kwaliteit): badges beschreven, kleuren als thematoken
Some checks failed
CI / Gate (Linux) · Format · Analyze · Coverage (pull_request) Has been cancelled
CI / Test (macos-latest) (pull_request) Has been cancelled
CI / Test (windows-latest) (pull_request) Has been cancelled
CI / Web hardening (pull_request) Has been cancelled
CI / Docs links (pull_request) Has been cancelled
CI / Supply-chain (Trivy · advisory) (pull_request) Has been cancelled
CI / Gate (Linux) · Format · Analyze · Coverage (push) Has been cancelled
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Web hardening (push) Has been cancelled
CI / Docs links (push) Has been cancelled
CI / Supply-chain (Trivy · advisory) (push) Has been cancelled
847f447f74
USER_GUIDE krijgt de twee badges met hun kleurtabel, het lezen en
beantwoorden ervan, en het accepteren van kwaliteitsproblemen. FILE_FORMAT
had §3.1c al; SOURCE_MAP en CHANGELOG volgen nu.

Twee poorten wezen onderweg iets aan dat het opschrijven waard is:

De conventieratchet op rauwe `Color(0x…)` sloeg aan op de badge-kleuren.
Terecht — maar aardig detail: de oude badge ontsnapte er alleen aan omdat
hij `Color(cond ? 0x… : 0x…)` schreef, wat het patroon net niet raakt. De
vier tinten staan nu als benoemde tokens in AppTheme, waar ze horen.

De methodelengte-poort tikte `_parseBlockDirectives` aan: mijn vier regels
duwden hem van 150 naar 155. In plaats van de grens op te rekken zijn twee
tak-lichamen echte helpers geworden (`_listStyleFrom`, `_bulletMarkerFrom`)
— parse-een-waarde-functies die op zichzelf beter lezen dan als tak in een
keten van twintig.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge main into feat/privacy-quality-badges
Some checks failed
CI / Gate (Linux) · Format · Analyze · Coverage (push) Has been cancelled
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Web hardening (push) Has been cancelled
CI / Docs links (push) Has been cancelled
CI / Supply-chain (Trivy · advisory) (push) Has been cancelled
CI / Gate (Linux) · Format · Analyze · Coverage (pull_request) Has been cancelled
CI / Test (macos-latest) (pull_request) Has been cancelled
CI / Test (windows-latest) (pull_request) Has been cancelled
CI / Web hardening (pull_request) Has been cancelled
CI / Docs links (pull_request) Has been cancelled
CI / Supply-chain (Trivy · advisory) (pull_request) Has been cancelled
0259ef54a1
Beide branches voegden l10n-strings toe op hetzelfde ankerpunt, wat in
alle dertig talenbestanden een conflict gaf. Union opgelost: het zijn
zuivere toevoegingen aan dezelfde map, geen tegenstrijdige wijzigingen.
Geen dubbele sleutels; make check groen over de combinatie.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brenno merged commit c4cf208ef7 into main 2026-07-19 13:46:36 +00:00
brenno deleted branch feat/privacy-quality-badges 2026-07-19 13:46:37 +00:00
Sign in to join this conversation.
No description provided.