Wijzigingslog I See You
Wijzigingslog — I See You Domeinmodel
Section titled “Wijzigingslog — I See You Domeinmodel”Overzicht van alle iteraties op het domeinmodel, inclusief de aanleiding en de exacte wijzigingen per bestand.
Huidige modelversie:
2026-07-14a— Eigen beoordelingscriteria als categorie + sub-vragen (twee niveaus): een eigen open vraag is niet langer één losse score maar een categorie-kop (titel) met daaronder eigen sub-vragen (VOOpenSubvraag) die elk op de smiley-schaal worden gescoord — precies zoals de vaste secties (Gezicht → ogen/mond/…); de kop zelf wordt niet gescoord. Elke categorie wordt als een aparte synthetische sectieopen:<sleutel>aan deBeoordelingsselectiegehangen. Twee beheerinstellingen:maxOpenVragen(max. categorieën, standaard 3) en nieuwmaxOpenSubvragen(max. sub-vragen per categorie, standaard 5). Titel én sub-vraagteksten gaan door de tekstmoderatie. Verfijning van ADR-043; issue #20. Eerder —2026-07-13d: Eigen beoordelingscriteria (open vragen op de smiley-schaal): de maker kan bij het stellen totmaxOpenVragen(nieuwe instelling, standaard 3; 0 = uit) korte eigen vragen (OpenVraag={sleutel, tekst}) toevoegen die elke opiniegever op dezelfde smiley-schaal 1–5 scoort — dus scores, geen vrije-tekst-antwoorden (bewust anders dan de in2026-07-04dgeschrapte open antwoorden). Technisch als synthetische sectieopenaan deBeoordelingsselectiegehangen zodat verdeling, verplichte scoring, AI-vangnet en aggregatie ongewijzigd werken; de vraagtekst gaat door de tekstmoderatie. Hulptekst beheerbaar (uitleg/open-vragen). Nieuwe ADR-043; issue #20. Eerder —2026-07-13c: Beheerbare e-mailteksten (uit de code): de teksten van de e-mailseintjes staan niet langer hard in de code maar in een nieuwe, beheerbare aggregateEmailSjabloon(Beheer-context): per mailtype (sleutel, bv.uitslag-klaar) en taal eenonderwerp+ platte-tekst-bodymet een placeholder-contract (alleen bekende placeholders zoals{link}; onbekende{…}geweigerd bij opslaan). De verzendcode (IUitslagNotificaties) leest de beheerde tekst en valt fail-graceful terug op een in code vastgelegde standaardtekst. Respecteert ADR-023 (alleen een link, geen inhoud; UI waarschuwt) en is toekomstvast voor latere mails (wachtwoordherstel). Nieuwe ADR-042; issue #26. Eerder —2026-07-13: Login-normalisatie + uniek e-mailadres + mislukte-login-audit (testfeedback Han): degebruikersnaamwordt bij registreren, account-upgrade én inloggen getrimd en hoofdletter-ongevoelig vergeleken (de gekozen schrijfwijze blijft weergavenaam; de sessie/het token gebruiken de canonieke opgeslagen naam) — zo mislukt een login niet meer door een mobiel toetsenbord dat de eerste letter een hoofdletter maakt of een spatie toevoegt. Het optionele e-mailadres wordt genormaliseerd (getrimd + lowercase) opgeslagen en is voortaan uniek (hoogstens één account per adres), afgedwongen zowel met een applicatiecheck (registreren/profiel-wijzigen/upgrade) als een unieke DB-index op het niet-lege adres; accounts zónder e-mail blijven onbeperkt. Elke mislukte login wordt alsAuditgebeurtenisMislukteLoginvastgelegd mét de reden (OnbekendeGebruiker/OnjuistWachtwoord) — admin-only in de Audittrail; de gebruiker houdt één neutrale melding (geen enumeratie). Sterkte-eisen aan het wachtwoord blijven bewust buiten scope (aparte routekaart-story). Nieuwe ADR-040; breidt ADR-038 uit met hetMislukteLogin-type. Eerder —2026-07-11n: Audittrail voor beheer-support (mét actor-identiteit, admin-only): nieuweAuditgebeurtenis(issue #22) legt vast wie welke vraag stelde, wie wat beoordeelde, wie wanneer zijn euro-saldo ophoogde, plus registratie/uitbetaling/account-upgrade/melding — telkenstype,actor, optioneeltarget/details,tijdstip, weggeschreven in dezelfde transactie als de mutatie. In tegenstelling tot de anoniemeMeetgebeurtenis(ADR-036) draagt dit type bewust wél identiteit, maar is uitsluitend zichtbaar voor de beheerder (nieuwe tab Audittrail) en bevat nooit media. Eigen, instelbare retentieauditRetentieDagen(standaard 365, ruimer dan de 90 dagen van de statistieken maar nog begrensd). Nieuwe ADR-038. Eerder —2026-07-11m: Land vervalt (single-tenant per site); Regio wordt een vlakke keuzelijst: I See You wordt per land als aparte site gehost (eigen domein), NL eerst; het profielveld en doelgroepcriteriumLand(ISO 3166-1) is daarmee overbodig en geschrapt, samen met de nooit-geïmplementeerde afgeleideRegioM49-verbreding (land → subregio → werelddeel).Regioblijft (per2026-07-10d), maar wordt een vlakke keuzelijst zonder landprefix (sleutel is de regionaam zelf, bv.gelderland, i.p.v."nl.gelderland"); het keuzelijst-typelandvervalt. Nieuwe ADR-037 (vervangt ADR-016); issue #23. Eerder —2026-07-11g: Visuele moderatie van opnames vóór distributie: elke opname (foto direct; video via ~4 FFmpeg-frames) wordt bij het stellen langs de nieuwe poortIVisueleModeratieDienst(OpenAI Moderation API met beeld-input) gehaald vóórdat de vraag de pool ingaat; afgekeurd = de vraag geweigerd met een nette melding en de opname direct gewist; fail-open bij een ontbrekende sleutel/storing (consistent met de tekstmoderatie); nieuwe beheerinstellingvisueleModeratieActief(bool, standaard aan). Het reactieve melden blijft als tweede linie. Nieuwe ADR-034. Eerder —2026-07-11a: Account-upgrade is een volledige identiteitsmigratie: bijPOST /api/anoniem/upgradegaan naast credits + sterren nu óók de gestelde vragen (incl.makerIsGeregistreerd = true), de ontvangen opinies, de zelf gegeven beoordelingen, actieve slots en berichten mee naar het nieuwe account — de eerdere keuze “lopende vragen blijven op de oude actor-referentie staan” is teruggedraaid (bug: je vraag verdween uit Mijn vragen na de upgrade). Bewust niet mee: meldingen en anti-thrash-uitsluitingen (kortlevend/moderatie-administratie). Eerder —2026-07-10o: AI-opinie draagt haar herkomst + persona-afhankelijke vangnet-scores (punt 11 Han):Opiniekrijgt een optioneel veldaiHerkomst("vision"of"vangnet"; null bij een menselijke opinie) zodat de beheerder per AI-opinie ziet of de vision-route of de deterministische terugval is gebruikt; de deterministische vangnet-scores zijn niet langer vlak maar gevarieerd en persona-afhankelijk (deterministische seed op persona + vraag + attribuut, met een mild/streng-karakter per persona) — meerdere AI-opinies op dezelfde vraag scoren dus niet meer identiek. Eerder —2026-07-10i: Account-upgrade anoniem → geregistreerd:AnoniemeGebruikerkrijgt een levensloop-tellertotaalVerdiendeCredits;POST /api/anoniem/upgradezet de sessie om naar eenGebruikermet behoud van credits en kent per gepasseerdesterCreditsMijlpaaldirect een ster toe (“neem je ster mee”); de anonieme sessie wordt verwijderd en de client krijgt een nieuw actor-token; het portaal nodigt (wegklikbaar) uit op het stermoment en de upgrade kan altijd via Mijn profiel. Eerder —2026-07-10h: Vrije reacties alleen van niveau-beoordelaars, via vrije-reactie-slots: de maker-keuzeBeoordelingsselectie.vrijeTekstGevraagdvervalt (een vraag is geldig met ≥1 sectie); elk slot (Opdracht) krijgt eenminimumNiveau— zonder betaald advies max.maxVrijeReactiesZonderBetaald(nieuwe instelling, standaard 3, gevalideerd <aantalOpiniegevers) vrije-reactie-slots exclusief voor beoordelaars met adviesniveau ≥ 1 (verplichte, geblurde tekst); mét betaald advies zijn deniveaueisenleidend; gewone slots zijn smiley-only (niveau 0 levert geen tekst); nieuwe bool-instellingaiVangnetVultVrijeReactieSlotsbepaalt of het AI-vangnet op niveau-slots tekst levert;minTeaserNiveauBeoordelaarsvervallen. Eerder —2026-07-10f: Smiley-schaal overal 5 treden: de score per attribuut gaat van 1..6 naar 1..5 (domeininvariant, AI-dienst, consensus-formule ÷4 i.p.v. ÷5, UI); bestaande 6-scores zijn per datamigratie 5 geworden en de sectiegemiddelden zijn herrekend; de smiley-reeks is beheerbare stamdata (keuzelijst-typesmiley, standaard ☹️ 😕 😐 🙂 😊). Eerder —2026-07-10d: Regio per land als profielveld en doelgroep-criterium:Profiel/ProfielSnapshotenSelectieCriteriakrijgen een optioneelregio-veld; de regio-indeling is per land beheerbare stamdata (keuzelijst-typeregio, sleutel"<landcode>.<regiosleutel>", NL geseed met de 12 provincies, andere landen grover); de matching filtert erop binnen het gekozen land (het criterium verbreedt niet mee naar UN M49). Eerder —2026-07-08f: Secties én attributen zijn beheerbare stamdata: naast het bestaande keuzelijst-typesectieeen nieuw typeattribuutmet samengestelde sleutel"<sectie>.<attribuut>"(bijv.gezicht.ogen), passend in de bestaande platteKeuzelijst-structuur (seed + frontend, geen schema-migratie). Daarnaast in dezelfde entry een reeks documentatie-correcties uit een docs↔code-audit (geen modelwijzigingen): docs volgen de code-naamVraag.aantalOpiniegevers, betaald advies staat als gebouwd (dummy-iDeal) gemarkeerd, de verplichte 1–6-scoreschaal als gebouwd, enBericht+ de frontend-SignalR-client zijn eerlijk als nog niet gebouwd gemarkeerd. Eerder —2026-07-08b: Verificatie Han verwerkt: sterrendrempels per niveau (30/60/90/120★, ⚠️ Expert 150★), welkomstbonus max €5, verdeelsleutel 50/50, uitbetaaldrempel €10 via het profielscherm, per-niveau beoordelaaraantallen, max. 1 teaser, ster zonder directe geldwaarde. Han heeft het voorstel2026-07-05beoordeeld (transcript “Notities beoordelings- en beloningssysteem”); zijn beslissingen zijn verwerkt indocs/énapp/(code geïmplementeerd op2026-07-08; iDeal blijft gesimuleerd). Eerder —2026-07-05: Sterren als verdiende reputatie; betaald advies met adviesniveaus en teaser-flow; euro-saldo met iDeal en welkomstbonus; uitbetaling terug; onboarding. Gebaseerd op de handgeschreven notities van Han (3-7-2026), die eerdere beslissingen overrulen; het gezaghebbende verificatiedocument isvoorstel-2026-07-05. De implementatie inapp/is bijgewerkt naar2026-07-05(verplicht 1–6 scoren + default 10; onboarding met welkomsttekst/poort/tips; sterren + consensus-ranking; euro-saldo + betaald advies + teaser — met gesimuleerde (dummy) iDeal-betalingen, echte PSP volgt later). De gemarkeerde interpretaties/open vragen blijven ter verificatie door Han. (1) DeSterkeert terug als verdiende kwaliteitsreputatie (géén loterij), in een geherintroduceerde context Beloning & Financieel: de beste opiniegever per vraag via de consensus-ranking (consensus + correlatie-controle; de 0–5-maker-waardering als instelbaar gewogen factor; pas ranken vanaf ≥3 beoordelaars — interpretatie), plus 5× 2e, 10× 3e en elke 200 verdiende credits (drempels/gewichten = beheerinstellingen). (2) Betaald advies: opiniegevers kunnen een adviesniveau hebben (1–4 + Expert; €0,20/0,40/0,60/0,80/1,00 per beoordeling, beheerinstelbaar; niveau ontsloten door sterren — voorstel); vanaf niveau 1 is het tekstvak vrijgegeven én verplicht; de vraagsteller stelt het gewenste niveau + aantal betaalde beoordelingen in (totaalkosten vooraf getoond, bv. €1,20); de gratis route blijft ernaast bestaan. (3) Teaser-flow: zonder account/saldo/niveau-instelling tóch minimaal 2 niveau-beoordelaars; hun tekst geblurd in de uitslag met niveau + prijs; betalen ontgrendelt per beoordeling (on-the-fly het simpelste account + iDeal); betaling aan de beoordelaar volgt de ontgrendeling (voorstel). (4) Euro-saldo (“inleg”) naast de credits, opladen via iDeal (PSP); eenmalige welkomstbonus: eerste inleg verdubbeld tot max. €10 bonus; credits en euro’s strikt gescheiden (geen omwisselkoers). ADR-019 (“geen geld”) is hiermee overruled. (5) De Uitbetaling keert terug (mechaniek deels open; voorstel: sterren ontsluiten vooral het adviesniveau); de anonimiteitsbelofte is geherformuleerd: anoniem richting andere gebruikers, betaalgegevens bij de payment provider, het platform bewaart minimale transactiedata. (6) Onboarding: welkomsttekst, “eerst zelf 2 vragen beantwoorden” vóór de eigen (eerste) vraag de verdeling ingaat (2 × 5 = 10 credits = de inzage-prijs bij 10) mét cold-start-vangrail, en tips-vensters bij de deelvragen (beheerbare content). (7) DefaultaantalOpiniegeversterug naar 10 (was 1 sinds 3-7-2026); de tarieven blijven verdien-5/inzage-10. (8) Scoren is verplicht — “geen mening” (score 0) vervalt (aanscherping 3-7-2026): de scoreschaal wordt 1–6, verplicht per gevraagd attribuut — de vraagsteller bepaalt via de Beoordelingsselectie waarop geantwoord moet worden en de opiniegever geeft op élk gevraagd attribuut een mening; alle “alleen scores > 0”-regels vervallen mee (SectieGemiddelden over álle scores; de consensus-ranking zonder uitzonderingen voor niet-gescoorde attributen; ook een AI-opinie scoort verplicht 1–6). Nieuwe ADR-027 t/m ADR-030 en ontwerpbeslissingen 29–33; ontwerpbeslissing 21 en ADR-019 deels overruled; ontwerpbeslissing 5 (score 0 = geen mening) vervallen; het default-deel van ADR-026/beslissing 28 teruggedraaid. Eerder —2026-07-04h: Standaard 1 beoordeling; AI-herkomst verborgen voor de gebruiker; elke beoordeling waardeerbaar; beheer-filter. Vier samenhangende punten (besloten met de gebruiker, 3-7-2026). (1) DeSysteeminstellingen-defaultaantalOpiniegeversging van 10 → 1 (blijft instelbaar via Beheer; betekenis ongewijzigd: het vaste aantal beoordelingen per vraag, bevroren bij het stellen). (2) AI-herkomst is niet zichtbaar voor de maker: het veldbron(mens/AI) is uit de user-facing uitslag-respons (GET /api/vragen/{id}/uitslag) verwijderd — in “Mijn vragen” ziet de maker niet meer of een beoordeling van een mens of een AI-persona komt. De beheerder ziet de herkomst nog wél (beheer-endpoints); het onderscheid bestaat dus nog in het model, maar wordt niet aan de eindgebruiker getoond. (3) Elke ontvangen beoordeling is door de maker waardeerbaar (0–5), óók een AI-opinie — de eerdere regel “AI-beoordelingen worden niet gewaardeerd” vervalt (consistent met punt 2: omdat de maker niet ziet wat AI is, moet hij alles kunnen waarderen). Een waardering op een AI-opinie wordt wél op de opinie vastgelegd maar beïnvloedt géén reviewer-profiel (een AI-persona heeft er geen); het waardering-endpoint werkt op elke opinie. (4) Beheer: de beheerder kan in het vragen-overzicht filteren op “vragen die nog niet alle opinies hebben” (aantalOpinies < aantalOpiniegevers); en de stel-vraag-defaults worden functioneel bijgesteld: de schakelaar “vrije reactie” staat standaard aan, en de categorieën staan standaard uit en dichtgeklapt. Nieuwe ADR-026 en ontwerpbeslissing 28. Eerder —2026-07-04g: Maker bepaalt of er een vrije reactie gevraagd wordt (verplicht wanneer gevraagd; alleen-vrije-tekst mag); AI geeft nu een reactie + krijgt de toelichting. DeBeoordelingsselectiekrijgt naastSectieseen vlagVrijeTekstGevraagd(bool); een vraag is geldig bij ≥1 sectie OFVrijeTekstGevraagd= true; is die true, dan is deOpinie.reactieverplicht (was optioneel). De AI-persona (IAiPersonaService) krijgt óók deVraag.toelichtingmee en levert naast scores een korte vrijereactie(herkaderde, opt-in prompt). Nieuwe ADR-025 en ontwerpbeslissing 27. Eerder —2026-07-04f: Handmatige AI-persona-opinie via het beheerportaal (vision-model + FFmpeg). Naast het bestaande automatische AI-vangnet kan de beheerder handmatig, met een gekozen AI-persona, direct een AI-opinie op een open slot van een vraag laten genereren (POST /api/beheer/vragen/{id}/ai-opinie, body{ personaId }; 404/400 als er geen open slot is; knop “AI-opinie toevoegen” in het Vragen-overzicht). De AI-invulling is nu een echte, vision-gebaseerde dienst: de poortIAiPersonaService(voorheenIAIPersona/IOpinieGenerator) draait op OpenAIgpt-4o-mini(goedkoop vision-model) — de persona-instructiestuurt de persoonlijkheid, het model scoort alleen de door de maker gekozen secties/attributen (0–6). Foto gaat rechtstreeks mee; video (max. 10s) via ~4 met FFmpeg uit de opname gehaalde frames (FFmpeg zit in het API-Docker-image). De AI produceert geen vrije tekst (geenreactie, dus geen tekstmoderatie; niet waardeerbaar, geen credits). Fail-graceful: zonder OpenAI-sleutel, zonder chat/completions-permissie of bij een fout valt de dienst terug op de bestaande deterministische scores. De sleutelOpenAI__ApiKeyheeft nu naast de Moderations-permissie ook chat/completions (“Model capabilities: write”) nodig. Kosten: ordegrootte < $0,01 per AI-opinie. Nieuwe ADR-024 en ontwerpbeslissing 26. Eerder —2026-07-04e: E-mailnotificatie met alleen een link zodra de uitslag klaar is.Vraagkrijgt een optioneelnotificatieEmail(nullable string): de maker kan bij het stellen een e-mailadres achterlaten om een seintje te krijgen zodra zijn uitslag klaar is — volledig los van het optionele account-e-mailadres voor wachtwoordherstel. Zodra alle opinies binnen zijn (beoordeeldOpgezet), stuurt het systeem één korte mail met alleen een link naar het gebruikersportaal (“je uitslag staat klaar”) — bewust géén beoordelingsinhoud (robuust tegen aflever-/spamproblemen; de site is de bron). Privacy: het adres wordt direct na de verzendpoging gewist (notificatieEmail→ null); er blijft geen mailinglijst achter. Nieuwe applicatie-poortIUitslagNotificaties(UitslagKlaarAsync(email)), infra-implementatie op Azure Communication Services Email (afzenderDoNotReply@…azurecomm.net; app-settingsEmail__ConnectionString,Email__Afzender,App__PortaalUrl), fail-graceful (zonder configuratie of bij een fout blijft de app werken; de uitslag is altijd in de app te zien). API:POST /api/vragenkrijgt een optioneelNotificatieEmail(simpele formaatvalidatie). UI: optioneel e-mailveld in het vraagformulier. Nieuwe ADR-023 en ontwerpbeslissing 25. Eerder —2026-07-04d: Eén begeleidende tekst (maker) en één reactie (opiniegever); open vragen vervallen. De open vragen en open antwoorden vervallen volledig: elke kant houdt precies één vrije tekst. De maker geeft alleen nog de bestaandeVraag.toelichtingmee; de opiniegever geeft naast zijn smiley-scores optioneel één tekstreactie — het nieuwe veldOpinie.reactie(nullable string; vervangtOpinie.OpenAntwoorden; het VOOpenAntwoordvervalt).Beoordelingsselectiebevat alleen nogSecties(het veldOpenVragenvervalt); een vraag is geldig met minstens één sectie. Beide teksten gaan door de OpenAI-tekstmoderatie (afgekeurde reactie = beoordeling niet opgeslagen, geen credit). API:POST /api/vragenzonderOpenVragen;POST /api/vragen/{id}/opiniesmetReactiei.p.v.OpenAntwoorden; de uitslag toont per beoordelingreactie. De waardering (0–5, smiley-schaal) door de maker blijft ongewijzigd. Eerder —2026-07-04c: Waardering op de smiley-schaal (presentatie). De 0–5-waardering die de maker aan elke ontvangen beoordeling geeft, gebruikt in de UI de vertrouwde smiley-schaal in plaats van sterren; de schaal (0 = slechtste, 5 = beste),Opinie.waarderingen de reviewer-statistieken zijn ongewijzigd. De term “sterren” verdwijnt uit de levende teksten. Eerder —2026-07-04b: Credits-tarieven: verdien 5, inzage 10.creditsPerBeoordelingstandaard 1 → 5; metaantalOpiniegevers = 10encreditkostenPerBeoordeling = 1kost een uitslag inzien 10 credits — twee gegeven beoordelingen betalen precies één inzage. De UI benadrukt bovendien dat het ontgrendelen eenmalig per vraag is (daarna gratis herbekijken). Eerder —2026-07-04: Vast aantal beoordelingen per vraag (keuze door de maker vervallen). ElkeVraagkrijgt het vaste aantal beoordelingen uit deSysteeminstellingen:aantalOpiniegevers(standaard 10) betekent weer gewoon “aantal beoordelingen per vraag”.Vraag.aantalBeoordelingenblijft bestaan als bevroren snapshot van die instelling op het moment van stellen (geen keuze van de maker); er ontstaan evenveelOpenslots en de bevroren inzage-prijs blijftcreditkosten = aantalBeoordelingen × creditkostenPerBeoordeling. Het veldAantalBeoordelingenvervalt uitPOST /api/vragen; de kaart “Aantal beoordelingen” verdwijnt uit het vraagformulier — de vaste inzage-prijs wordt wel vóór het versturen getoond. Eerder —2026-07-03: Loterij-ster + uitbetaling geschrapt; “sterren” = 0–5 kwaliteitswaardering; OpenAI-tekstmoderatie; open vragen i.p.v. opmerkingen; instelbaar aantal beoordelingen; toelichting bij de vraag. De hele bounded context Beloning & Financieel vervalt (Ster,Sterrensaldo,Uitbetaling,UitbetalingStatus,FinanciëleGegevens,sterrenDrempelOpmerkingen— geen geld meer). “Sterren” betekent nu uitsluitend een kwaliteitswaardering 0–5 die de maker aan elke ontvangen beoordeling geeft (Opinie.Waardering); op de reviewer (Gebruiker) staanAantalBeoordelingen+GemiddeldeWaardering. Alle vrije tekst (toelichting, open vragen, open antwoorden) gaat door de OpenAI Moderation API (gratis, fail-open, poortIModeratieDienst). Het algemeneopmerkingen-veld is vervangen doorOpenAntwoordenop door de maker gestelde open vragen (Beoordelingsselectie.OpenVragen).VraagkrijgtToelichtingen een gekozen aantal beoordelingen; de inzage-prijscreditkosten = aantalBeoordelingen × creditkostenPerBeoordeling(instellingcreditkostenPerVraag→creditkostenPerBeoordeling). Eerder —2026-07-02: Credits-poort verplaatst: van insturen → uitslag inzien. Een vraag stellen is gratis (de opname gaat direct de pool in = beoordeelbaar werk voor anderen); je betaalt de bevroren inzage-prijs pas om je eigen uitslag te ontgrendelen. Dit lost de cold-start deadlock op en is de credit-vorm van de oorspronkelijke wederkerigheid.VraagkreegresultaatOntgrendeld; ADR-17 herzien. Eerder —2026-07-01: Resultaat terugzien + resultaat-retentievenster: na afronding verdwijnt de opname niet meer direct. De maker kan zijn eigen vraag terugzien (resultaat + de foto/video) in “Mijn vragen”, kan de opname zelf verwijderen, en de opname wordt automatisch verwijderd zodraresultaatRetentieUren(nieuw, standaard 168u/7d) verstrijkt — geteld vanafbeoordeeldOp.VraagkrijgtbeoordeeldOp;Retentiebeleid.verwijderDirectNaBeoordeling→resultaatRetentieUren. Anonimiteit blijft geborgd: de opname is na afronding alleen zichtbaar voor de maker en verdwijnt gegarandeerd binnen het venster. Eerder —2026-06-30b: Credits-economie (verdien via beoordelen, betaal per vraag; vervangt de twee “eerst beoordelen”-poorten; credits ≠ sterren; anoniem = sessie-credits).2026-06-30a: instelbare aantallen, retentie, online = voorrang, AI-persona-vangnet, beoordelingsselectie, opmerkingen-drempel, land-keuzelijst, e-mailherstel (en tussentijds: pull-based keuzeset/reservering);d–f:Inzending→Vraag, twee gebruikerstypen,AnoniemeGebruiker;a–c: eerste vastlegging, Beheer-rename, gestandaardiseerde locatie (ISO 3166-1 + UN M49). De implementatie inapp/pint op deze versie; bij elke modelwijziging wordt deze regel bijgewerkt. Let op: niet elke changelog-entry is een modelwijziging — implementatie-, beveiligings- en presentatie-entries (zoals2026-07-08ADR-031,2026-07-08den2026-07-08e) hebben een eigen datum-letter maar laten de modelversie ongemoeid; alleen entries die het domeinmodel raken bumpen de versieregel hierboven. Zie Workflow voor hoe wijzigingen op één branch worden verwerkt.
[2026-07-14b] Meerdere omgevingen in Clermond-2 met custom domains en persistente DNS — issue #24
Section titled “[2026-07-14b] Meerdere omgevingen in Clermond-2 met custom domains en persistente DNS — issue #24”Aanleiding: de opdrachtgever wil een verse productie-omgeving (domein advice-4-me.com, deploy vanaf branch productie) en een testing-omgeving (domein advies-4-mij.nl, continuous deploy vanaf main) naast elkaar in één resourcegroep Clermond-2, waarbij omgevingen goedkoop up/down te halen zijn zonder dat de DNS-delegatie bij de registrar (Vimexx) ooit breekt. Implementatie-/infrastructuur-entry; geen modelwijziging → geen modelversie-bump.
Beslissing (nieuwe ADR-044, vervangt deel 2 van ADR-039): één resourcegroep met een omgevingslabel (slug) in elke resourcenaam i.p.v. een aparte resourcegroep per omgeving; een apart, persistent Pulumi-project infra/dns/ per domein (Azure DNS-zone + ACS-e-maildomein noreply@<domein> met DKIM/SPF, protect, geen destroy-workflow) naast het omgevingsproject infra/ (dat de omgevingsgebonden records/bindings in de bestaande zone schrijft en volledig op/neer kan); custom domains apex+www/beheer/api/docs per omgeving; branch-per-omgeving (main → testing, productie → productie); Pulumi zet voortaan óók App__PortaalUrl/Cors__Origins en de registry-credentials via een expliciete WebAppApplicationSettings-resource.
Wijzigingen per bestand:
docs/:architectuur/beslissingen.md(nieuwe ADR-044; ADR-039-status),architectuur/deployment.md(§ Meerdere omgevingen herschreven naar de gebouwde situatie),changes.md.infra/dns/(nieuw): persistent projectiseeyou-dns— zone, EmailService/Domain/SenderUsername, verificatie-/SPF-/DKIM-records, stacksproductie/testing.infra/:config.ts(customDomain,dnsZoneResourceGroup,dnsStack),communication.ts(managed domain alleen zónder custom domein; anders StackReference-link),api.ts(app-settings naarWebAppApplicationSettings, incl.App__PortaalUrl/Cors__Origins/DOCKER_REGISTRY_SERVER_*),customDomains.ts(nieuw: DNS-records, SWA-custom-domains incl. apex-token-orkestratie, App Service-hostname + managed certificate + SNI-bind),Pulumi.test.yamlverwijderd (dode Test-omgeving),README.mdherschreven..github/workflows/:infra.yml(vangrails +customDomain/dnsZoneResourceGroup-inputs),infra-dns.yml(nieuw: preview/up/check-delegatie/verify-email, bewust geen destroy),ci.yml(branch → omgeving-mappingmain→testing /productie→productie, geparametriseerde deploy-jobs, eerste-push-van-een-branch deployt alles).
Verificatie: (volgt — DNS-delegatie, e-mailverificatie, omgeving up, custom domains + certificaten, e2e tegen advies-4-mij.nl, up/down-cyclustest).
[2026-07-14a] Eigen beoordelingscriteria als categorie + sub-vragen (twee niveaus) — issue #20
Section titled “[2026-07-14a] Eigen beoordelingscriteria als categorie + sub-vragen (twee niveaus) — issue #20”Aanleiding: testfeedback op de eerste versie van #20 (open vragen als losse scores): de opdrachtgever wil dat een eigen vraag zich gedraagt als een categorie — een kop met daaronder een instelbaar aantal sub-vragen, net zoals de vaste categorieën (Gezicht → ogen/mond/…). Modelwijziging → modelversie-bump 2026-07-14a (verfijning van ADR-043).
Wijziging: VO OpenVraag gaat van {sleutel, tekst} naar {sleutel, titel, subvragen} met een nieuw VO OpenSubvraag {sleutel, tekst}. Elke categorie wordt als een eigen synthetische sectie open:<sleutel> aan de Beoordelingsselectie gehangen (attributen = de sub-vraag-sleutels), zodat verplichte scoring, verdeling, AI-vangnet en aggregatie ongewijzigd blijven; de kop wordt niet apart gescoord. Nieuwe instelling maxOpenSubvragen (standaard 5) naast maxOpenVragen. Titel + sub-vraagteksten door de tekstmoderatie.
Wijzigingen per bestand:
docs/:domeinmodel.md(VO’s +maxOpenSubvragen+ synthetische sectie per categorie),termen.md(Open vraag herzien),architectuur/beslissingen.md(ADR-043 herzien),functionele-beschrijving/vraag-stellen.md+opinie-geven.md+systeembeheer.md,changes.md.app/backend: VOOpenSubvraag+ herzieneOpenVraag,Vraag.Stellenbouwt sectie per categorie,maxOpenSubvragen,OpenVraagInvoer-DTO,VraagServicemodereert titel + sub-vragen, migratieOpenVragenCategorieen(reset oude platte data).app/frontend: geneste invoer in Stel een vraag (categorie + sub-vragen, add/remove), 2-niveau-labels in Beoordelen + Resultaat.app/beheer:maxOpenSubvragenin de Instellingen-tab.
Verificatie: (volgt — build + deploy + live).
[2026-07-14] Wachtwoordvelden gehard tegen autocorrectie (vervolg op ADR-040)
Section titled “[2026-07-14] Wachtwoordvelden gehard tegen autocorrectie (vervolg op ADR-040)”Aanleiding: een tester (Han) kon niet inloggen met wat hij dacht dat zijn wachtwoord was (han123). Diagnose via de productie-API + de MislukteLogin-audit (ADR-040): de gebruikersnaam werd correct gevonden (reden telkens OnjuistWachtwoord), en het echte wachtwoord bleek ham123 — het mobiele toetsenbord had bij het registreren “han” naar “ham” geautocorrigeerd (een bestaand woord), juist doordat het wachtwoord zichtbaar was gemaakt (type=text, issue #4). Geen auth-bug: de hash was exact wat er ongemerkt was ingetypt (bevestigd met een register→login-rondje). Implementatie-/beveiligingsentry; geen modelwijziging → geen modelversie-bump.
Wijziging: ADR-040 hardde al de gebruikersnaam-velden (autoCapitalize=none, autoCorrect=off, spellCheck=false); die hardening is nu uitgebreid naar alle wachtwoord- en toegangscodevelden, zodat een (mobiel) toetsenbord ze niet meer kan “verbeteren” — vooral relevant wanneer het wachtwoord zichtbaar wordt gemaakt. Zo wordt bij registreren/wijzigen niet ongemerkt een ander wachtwoord opgeslagen dan de gebruiker denkt te typen.
Wijzigingen per bestand: app/frontend/src/ui.tsx (gedeelde WachtwoordVeld), app/frontend/src/screens/MijnProfiel.tsx (wachtwoord wijzigen), app/beheer/src/Login.tsx (admin-login: naam + wachtwoord), app/shared/src/Toegangspoort.tsx (toegangscode); changes.md.
[2026-07-13d] Eigen beoordelingscriteria: open vragen op de smiley-schaal — issue #20
Section titled “[2026-07-13d] Eigen beoordelingscriteria: open vragen op de smiley-schaal — issue #20”Aanleiding: issue #20 — een maker wil naast de vaste secties (gezicht/kleding/accessoires) soms een eigen, vrij geformuleerd criterium kunnen laten scoren (“past deze bril bij mijn gezicht?”). Simpel en optioneel; hulptekst + aantal beheerbaar. Modelwijziging → modelversie-bump 2026-07-13d.
Beslissing (ADR-043): de maker voegt bij het stellen tot maxOpenVragen (instelling, standaard 3; 0 = uit) korte open vragen toe. Ze worden op dezelfde smiley-schaal 1–5 gescoord als een attribuut — geen vrije tekst (dus geen herleving van de in 2026-07-04d geschrapte open antwoorden). Bewaard als lijst OpenVraag op de Vraag én als synthetische sectie open in de Beoordelingsselectie, zodat verplichte scoring, verdeling, AI-vangnet en aggregatie ongewijzigd werken (alleen de labels komen uit openVragen). De vraagtekst loopt door de tekstmoderatie; de hulptekst is beheerbaar (uitleg/open-vragen).
Wijzigingen per bestand:
docs/:domeinmodel.md(VOOpenVraag,Vraag.openVragen,maxOpenVragen, synthetischeopen-sectie, diagrammen),termen.md(nieuwe term Open vraag; Beoordelingsselectie + Systeeminstellingen bijgewerkt),architectuur/beslissingen.md(ADR-043 + indextabel),functionele-beschrijving/vraag-stellen.md+opinie-geven.md+systeembeheer.md,changes.md.app/backend: VOOpenVraag+Vraag.openVragen,maxOpenVragenopSysteeminstellingen, EF jsonb-mapping + migratie,open-sectie samenstellen + open-vraag-teksten modereren inVraagService, slot-detail + uitslag gevenopenVragenmee, seeduitleg/open-vragen,POST /api/vragenaccepteertOpenVragen.app/frontend: open-vragen-invoer in Stel een vraag (met hulptekst +maxOpenVragen), rendering van deopen-sectie met vraagteksten in Beoordelen en in het resultaat.app/beheer:maxOpenVragenin de Instellingen-tab.
Verificatie (lokaal, tegen Postgres in Docker): migratie OpenVragen (jsonb-kolom, default [] voor bestaande vragen) toegepast bij startup; maxOpenVragen = 3 in GET /api/beheer/instellingen; uitleg/open-vragen geseed. Een anonieme sessie stelde een vraag met twee open vragen (+ één lege, die correct wegviel): de opgeslagen Beoordelingsselectie bevat naast gezicht/ogen de synthetische sectie open met attributen q1/q2, en de OpenVragen-kolom houdt de teksten met sleutels q1/q2 vast — dus verplichte scoring, verdeling en aggregatie nemen ze mee. Backend + alle frontends bouwen/typechecken; 46 domein-unittests + 19 frontend-unittests groen. (Deploy naar Azure + live e2e met de beoordeel-/resultaat-UI: nog te doen.)
[2026-07-13c] E-mailteksten beheersbaar maken (uit de code halen) — issue #26
Section titled “[2026-07-13c] E-mailteksten beheersbaar maken (uit de code halen) — issue #26”Aanleiding: de teksten van de e-mailseintjes stonden hard in de code. Het uitslag-seintje (ADR-023, “alleen een link”) had onderwerp + body als inline strings in AcsUitslagNotificaties; elke tekstwijziging vereiste een code-aanpassing + volledige deploy. Modelwijziging → modelversie-bump 2026-07-13c.
Beslissing (ADR-042): de e-mailteksten worden beheerbare stamdata via een nieuwe aggregate EmailSjabloon (Beheer-context) — een eigen entiteit i.p.v. een Keuzelijst-rij, omdat een mail twee velden heeft (onderwerp + body). Vaste sleutel = het mailtype (bv. uitslag-klaar), per taal. Placeholder-contract per mailtype (voor uitslag-klaar alleen {link}); bij opslaan worden onbekende {…} geweigerd. Fail-graceful: ontbreekt de beheerde rij of is de DB onbereikbaar, dan valt de verzendcode terug op de in code vastgelegde standaardtekst (= de vroegere hardcoded tekst, geseed). Toekomstvast: een nieuw mailtype = seed-rij + placeholder-definitie, zónder de verzendcode te herschrijven.
Wijzigingen per bestand:
docs/:domeinmodel.md(Beheer-context:EmailSjabloonin klassediagram + prose + context-tabel),termen.md(nieuwe term E-mailsjabloon; Uitslagnotificatie verwijst nu naar het sjabloon),architectuur/beslissingen.md(ADR-042 + indextabel),functionele-beschrijving/systeembeheer.md(nieuwe sectie E-mailteksten + overzichtstabel),changes.md.app/backend: nieuwe aggregateEmailSjabloon+ deEmailSjablonen-definitieregistratie (Beheer), EF-mapping + migratieEmailSjablonen, seed vanuitslag-klaar, applicatie-poortIEmailSjabloonProvider, infra-implementatie die per verzending een scope opent,AcsUitslagNotificatiesleest het sjabloon (placeholder{link}) met code-fallback, beheer-endpointsGET/PUT /api/beheer/emailteksten.app/beheer: nieuwe tab E-mailteksten (onderwerp + body-textarea, placeholder-hint, ADR-023-waarschuwing, “herstel standaard”), router/nav/api/types.
Verificatie (lokaal, tegen Postgres in Docker): migratie EmailSjablonen toegepast bij startup en uitslag-klaar geseed; GET /api/beheer/emailteksten geeft het sjabloon mét toegestane placeholders + standaardtekst; PUT met een onbekende placeholder {naam} → 400 (“Onbekende placeholder {naam}. Toegestaan voor dit mailtype: {link}.”), met {link} → 200; zonder admin-token → 401. De beheer-tab E-mailteksten rendert het sjabloon, de placeholder-chip en de ADR-023-waarschuwing (geverifieerd via de accessibility-tree van het beheerportaal). Backend + alle frontends bouwen/typechecken; 46 domein-unittests + 19 frontend-unittests groen. (Deploy naar Azure + live e2e: nog te doen.)
[2026-07-13b] Client-side crash-rapportage + auto-reload bij verouderde chunks
Section titled “[2026-07-13b] Client-side crash-rapportage + auto-reload bij verouderde chunks”Aanleiding: een tester zag ná de deploy een witte foutpagina (“Er ging iets mis — herlaad de pagina”) bij het registreren. Telemetrie-analyse (Application Insights iseeyou-insights) toonde geen server-side fout: de API gaf correct 400 op de duplicaat-e-mail, geen exceptions. De crash was puur client-side (verouderde, lazy-geladen PWA-chunk na de deploy) én nergens vastgelegd, want de frontend stuurde geen telemetrie. Implementatie-/observability-entry; geen modelwijziging → geen modelversie-bump.
Wijzigingen (zie ADR-041):
- Beide portalen luisteren naar
vite:preloadErroren herladen dan één keer → een verouderde-chunk-fout na een deploy geeft een naadloze reload i.p.v. de foutpagina. - Nieuwe gedeelde helper
installFoutmelders/meldClientFout(app/shared/src/foutmelding.ts): meldtwindow.onerror,unhandledrejectionenErrorBoundary-crashes aan het nieuwe publieke endpointPOST /api/client-fout, dat ze via Application Insights logt. Cookieloos, geen Microsoft-browser-SDK, alleen metadata (bron, boodschap, stacktrace,pathnamezonder querystring, user-agent) — consistent met ADR-036.
Wijzigingen per bestand: app/shared/src/foutmelding.ts (nieuw), app/shared/src/index.ts, app/shared/src/ErrorBoundary.tsx, app/frontend/src/main.tsx, app/beheer/src/main.tsx, app/backend/src/ISeeYou.Api/Endpoints.cs (endpoint + ClientFoutRequest), integratietest; docs/: architectuur/beslissingen.md (ADR-041 + tabel), changes.md.
[2026-07-13] Login-normalisatie, uniek e-mailadres en mislukte-login-audit (testfeedback Han)
Section titled “[2026-07-13] Login-normalisatie, uniek e-mailadres en mislukte-login-audit (testfeedback Han)”Aanleiding: Han meldde tijdens het testen dat hij met meerdere zelf aangemaakte accounts niet kon inloggen (“onbekend”), terwijl dezelfde accounts vanaf een ander apparaat wél werkten. Diagnose: de gebruikersnaam-opzoeking was hoofdletter- én spatiegevoelig (g.Id == naam) zonder enige normalisatie, terwijl het gebruikersnaam-invoerveld op mobiel niet gehard was (auto-hoofdletter/autocorrectie) — registratie en login weken daardoor stilletjes af. Bijkomend: één e-mailadres kon aan willekeurig veel accounts hangen, en de generieke foutmelding maakte support onmogelijk. Modelwijziging (identiteitssemantiek + nieuw audittype) → modelversie-bump naar 2026-07-13.
Beslissingen (zie ADR-040):
- Gebruikersnaam getrimd + hoofdletter-ongevoelig bij registreren, account-upgrade en inloggen. De uniciteitscheck en login-opzoeking vergelijken genormaliseerd; sessie/token gebruiken de canonieke opgeslagen naam (downstream-opzoekingen blijven exact). De gekozen schrijfwijze blijft weergavenaam.
- E-mailadres optioneel maar uniek: genormaliseerd (getrimd + lowercase) opgeslagen; applicatiecheck (nette melding) + unieke DB-index op het niet-lege adres. Accounts zonder e-mail blijven onbeperkt.
- Mislukte login geaudit, niet gelekt: nieuw
AuditType.MislukteLoginmet de reden (OnbekendeGebruiker/OnjuistWachtwoord) indetails, admin-only in de Audittrail; de gebruiker houdt één neutrale melding. - Frontend-hardening: het gebruikersnaam-veld krijgt
autoCapitalize=none,autoCorrect=off,autoComplete=username,spellCheck=false. - Wachtwoordbeleid bewust buiten scope — aparte routekaart-story.
Wijzigingen per bestand: docs/: functionele-beschrijving/aanmelden-registratie.md, termen.md (Gebruiker + Auditgebeurtenis), architectuur/beslissingen.md (nieuw ADR-040 + ADR-tabel + ADR-038-uitbreiding), changes.md. app/: ISeeYou.Domain/Gebruikers/Gebruikers.cs (naam-/e-mailnormalisatie), ISeeYou.Application/Services.cs + AuthServices.cs (login-/registratie-/upgrade-normalisatie + audit), ISeeYou.Application/Abstractions/Repositories.cs + ISeeYou.Infrastructure/Persistence/Repositories.cs (genormaliseerde opzoekingen), ISeeYou.Domain/Beheer/Beheer.cs (AuditType.MislukteLogin), ISeeYou.Infrastructure/Persistence/AppDbContext.cs + EmailDeduplicatie.cs (zelfhelende aliasing-SQL) + nieuwe EF-migratie UniekEmail (unieke e-mail-index), frontend/src/Auth.tsx, beheer/src/tabs/Audittrail.tsx, integratietests.
Migratie (niet-destructief zelfhelend): de UniekEmail-migratie normaliseert bestaande adressen en maakt gedeelde adressen uniek met een +N-alias (Gmail-stijl; het oudste account houdt het kale adres) vóórdat de unieke index wordt aangemaakt. Han’s opzettelijk-gedeelde test-accounts blijven zo allemaal bestaan en de app loopt bij het opstarten niet vast op de duplicaten — geen handmatige opschoning of accountverlies nodig.
[2026-07-11u] Volledige e2e-suite tegen de nieuwe omgeving; e2e-config env-var-overschrijfbaar
Section titled “[2026-07-11u] Volledige e2e-suite tegen de nieuwe omgeving; e2e-config env-var-overschrijfbaar”Aanleiding: vervolg op 2026-07-11t — “test alle end-to-end-testen op de nieuwe omgeving”: de bestaande, lokaal-tegen-dev-server-gerichte Playwright-suite (~20 spec-bestanden) daadwerkelijk laten draaien tegen de live “test”-omgeving, niet alleen handmatige/curl-verificatie. Implementatie-/verificatie-entry; geen modelwijziging → geen modelversie-bump.
Wijzigingen: playwright.config.ts en e2e/helpers.ts (API/BEHEER) zijn nu overschrijfbaar via E2E_BASE_URL/E2E_API/E2E_BEHEER; een nieuwe, conditionele storageState (alleen actief met E2E_TOEGANGSCODE gezet) seedt de Toegangspoort-localStorage-sleutel voor beide portaal-origins vooraf, zodat geen enkele spec zelf de toegangscode-poort hoeft te doorlopen. auth-regressie.spec.ts (die bewust geen helpers.ts importeert) en de seedBeheer-default in _setup-admin-auth.ts kregen dezelfde env-var-fallback. Lokaal (zonder env-vars) is het gedrag ongewijzigd.
Uitkomst: 53 van de 53 tests slagen tegen de live “test”-omgeving. De eerste run gaf 3 mislukkingen (allemaal dezelfde oorzaak: de dummy-iDeal-redirect); root cause: App__PortaalUrl was niet gezet op de nieuwe API (production heeft dit wél), waardoor DummyBetaalDienst terugviel op http://localhost:5173 — de browser navigeerde daardoor naar een lokale dev-server i.p.v. de gedeployde testomgeving, en de webhook-call kwam op de verkeerde API terecht (“Onbekende betaling”). Gezet + herstart → alle 3 slaagden alsnog, 53/53 in totaal.
Wijzigingen per bestand: app/frontend/playwright.config.ts, app/frontend/e2e/helpers.ts, app/frontend/e2e/_setup-admin-auth.ts, app/frontend/e2e/auth-regressie.spec.ts; live app-setting App__PortaalUrl op iseeyou-api-test (buiten git); changes.md.
[2026-07-11t] Pulumi-pipeline live getest: volledige nieuwe omgeving in resourcegroep “Test”
Section titled “[2026-07-11t] Pulumi-pipeline live getest: volledige nieuwe omgeving in resourcegroep “Test””Aanleiding: verificatie van 2026-07-11s — “test hem volledig” tegen een echte, nieuwe resourcegroep (Test, subscriptie Playground), niet alleen pulumi preview. Implementatie-/verificatie-entry; geen modelwijziging → geen modelversie-bump.
Uitkomst: een volledig werkende, vers opgespinde I See You-omgeving (18 Azure-resources: App Service-plan + API-container, PostgreSQL, ACR, 4 Static Web Apps, Log Analytics/App Insights/actiegroep, Communication Services) — API-image gebouwd en gepusht, alle drie de portalen + docs gedeployd, en live end-to-end geverifieerd: settings-endpoint, registratie + login via de API én via de echte UI (Playwright, screenshots), CORS, en beide andere portalen bereikbaar.
Onderweg drie echte bugs gevonden en gefixt (dus waardevol dat dit echt is uitgevoerd, niet alleen ge-previewd):
- Quota: de Playground-subscriptie had 0 B1-App-Service-quota in
northeurope(het bestaande productieplan gebruikt de enige capaciteit) → nieuwe configlocationApiPlan(standaardwesteurope) laat nieuwe omgevingen een regio met vrije quota kiezen. - Verkeerd gekoppelde vlag:
createResourceGroup(reageert op “bestaat de resourcegroep al”) werd ook gebruikt om te bepalen of Pulumi het Postgres-wachtwoord/Auth:Secretmoest bekabelen — maar een omgeving kan prima in een al bestaande resourcegroep leven en toch een gloednieuwe Postgres-server/API-app nodig hebben (zoals “Test”). Nieuwe, aparte vlagmanageAppSecretslost dit correct op. siteConfig-drift: Pulumi’sapiApp.siteConfig(image-tag, app-settings) werd bij elkepulumi upteruggezet naar de in-code placeholder (:latest, zonder CORS/registry-credentials) — ontdekt toen een parallelle workflow-run (tijdens het live testen) de net-werkende, handmatig gezette container-tag en CORS-instelling overschreef. Opgelost metignoreChanges: ["siteConfig"]: Pulumi zet dit alleen bij de allereerste aanmaak, en laat het daarna volledig aan de CI/CD-pijplijn / handmatig beheer (net als bij productie).
Wijzigingen per bestand: infra/config.ts (manageAppSecrets, locationApiPlan), infra/api.ts (ignoreChanges, gebruik van beide nieuwe vlaggen), infra/database.ts (manageAppSecrets i.p.v. createResourceGroup), infra/Pulumi.production.yaml (manageAppSecrets: false), .github/workflows/infra.yml (locationApiPlan-input); live: resourcegroep Test met 18 resources (blijft staan als werkend voorbeeld, geen doorlopende kosten van betekenis boven de Basic/Burstable-tarieven); nieuwe screenshots + app/frontend/live-verify-test-omgeving.cjs; changes.md.
[2026-07-11s] Pulumi-pipeline: een hele omgeving opspinnen in een configureerbare resourcegroep (issue #24, vervolg)
Section titled “[2026-07-11s] Pulumi-pipeline: een hele omgeving opspinnen in een configureerbare resourcegroep (issue #24, vervolg)”Aanleiding: vervolg op ADR-039 deel 1 (IaC-codificatie van productie) — de opdrachtgever vroeg expliciet om een pijplijn die de hele applicatie (App Service, Postgres, ACR, de vier Static Web Apps, monitoring, Communication Services) kan opspinnen in een zelf te configureren resourcegroep, niet alleen de bestaande productie-omgeving beschrijven. Implementatie-entry; geen modelwijziging → geen modelversie-bump.
Wijzigingen: infra/config.ts kreeg een createResourceGroup-vlag (standaard true) en leidt alle resourcenamen af van een korte slug tenzij expliciet overschreven (zoals Pulumi.production.yaml doet, met createResourceGroup: false) — een nieuwe omgeving heeft dus alleen een resourcegroep-naam + slug nodig. Nieuw bestand infra/resourceGroup.ts maakt de resourcegroep zelf aan (nieuwe omgeving) of hergebruikt een bestaande (productie), met een expliciete dependsOn naar elke resource die er direct in leeft. Twee echte gaten gedicht die een verse omgeving zouden laten crashen: database.ts zette nooit een admin-wachtwoord (verplicht bij het aanmaken van een nieuwe Postgres-server) en api.ts verwees nooit daadwerkelijk naar ConnectionStrings__Postgres/Auth__Secret in de app-settings — beide zijn nu bekabeld (verplicht via pulumi config set --secret bij een nieuwe omgeving, ongebruikt bij het importeren van productie). Nieuwe workflow .github/workflows/infra.yml (workflow_dispatch: stacknaam, resourcegroep, slug, actie preview/up/destroy, verplichte bevestiging bij up/destroy) draait op dezelfde self-hosted runner, met een lokale, self-hosted Pulumi-statebackend (bestand op de runner, passphrase eenmalig gegenereerd en lokaal bewaard) — blokkeert expliciet de production-stack.
Geverifieerd: pulumi preview tegen een wegwerp-teststack toonde correct 19 aan te maken resources met van de slug afgeleide namen, zonder fouten; pulumi preview tegen de bestaande production-config bleef ongewijzigd werken (geen creatie, alleen de bestaande resourcegroep uitlezen).
Wijzigingen per bestand: infra/config.ts, infra/index.ts, infra/resourceGroup.ts (nieuw), infra/database.ts, infra/api.ts, infra/registry.ts, infra/monitoring.ts, infra/staticSites.ts, infra/communication.ts, infra/Pulumi.production.yaml; .github/workflows/infra.yml (nieuw); changes.md.
[2026-07-11r] Auth:Secret verplicht buiten Development (kwetsbaarheid gedicht)
Section titled “[2026-07-11r] Auth:Secret verplicht buiten Development (kwetsbaarheid gedicht)”Aanleiding: routekaart Fase 1 (“de dev-fallback van het token-secret hard laten falen in productie”), bij het uitwerken hiervan bleek het daadwerkelijk actueel: de live API had geen Auth__Secret app-setting geconfigureerd, waardoor HmacTokenDienst terugviel op de in de broncode hardgecodeerde dev-sleutel ("iseeyou-dev-secret-change-me") — bearer-tokens (admin én actor) werden dus ondertekend met een voorspelbare sleutel. De repository is privé, wat het risico beperkt, maar het is een reële kwetsbaarheid die niet mocht blijven staan. Implementatie-/beveiligingsentry; geen modelwijziging → geen modelversie-bump.
Wijzigingen: (1) direct verholpen op de live omgeving: een sterke, willekeurige Auth__Secret is gegenereerd en gezet als app-setting op iseeyou-api-clermond (waarde nergens gelogd/gecommit) — dit verving meteen de dev-fallback in de draaiende (oude) code, en heeft als neveneffect dat alle op dat moment actieve tokens ongeldig werden (opnieuw inloggen nodig, geen dataverlies). (2) code-hardening: Program.cs gooit nu een startup-InvalidOperationException als Auth:Secret ontbreekt buiten Development — een verkeerd geconfigureerde deploy faalt voortaan hard en zichtbaar in plaats van stil terug te vallen op een onveilige sleutel. Lokale ontwikkeling en de integratietests (die op Development draaien) zijn onveranderd.
Wijzigingen per bestand: backend Api/Program.cs (guard); Azure app-setting Auth__Secret op iseeyou-api-clermond (buiten git, via az webapp config appsettings set); changes.md.
[2026-07-11q] Wachtwoord-hashing naar Argon2id
Section titled “[2026-07-11q] Wachtwoord-hashing naar Argon2id”Aanleiding: routekaart Fase 1 — IPasswordHasher documenteerde Argon2id altijd al als doel (ADR-007/ADR-031), maar de implementatie was nog PBKDF2-SHA256. Implementatie-entry; geen modelwijziging → geen modelversie-bump.
Wijzigingen: nieuwe Argon2idPasswordHasher : IPasswordHasher (OWASP-parameters: 19 MiB geheugen, 2 iteraties, 1 lane) vervangt Pbkdf2PasswordHasher als DI-registratie — geen wijziging aan de interface, dus geen wijziging aan aanroepende code (registratie, login, admin-login). Backward-compatible: Verifieer herkent het oude PBKDF2-formaat (salt.key, twee base64-delen) nog steeds en blijft dat verifiëren; alleen nieuwe/gewijzigde wachtwoorden krijgen het nieuwe argon2id$…-formaat. Zo blijven bestaande accounts (inclusief het al gezette admin-wachtwoord in productie) geldig zonder wachtwoord-reset of migratie.
Wijzigingen per bestand: backend Infrastructure/Services/InfraServices.cs (Argon2idPasswordHasher), Infrastructure/DependencyInjection.cs (registratie), ISeeYou.Infrastructure.csproj (Konscious.Security.Cryptography.Argon2); test tests/ISeeYou.Domain.Tests/Argon2idPasswordHasherTests.cs (nieuw); docs architectuur/beslissingen.md (ADR-031-status bijgewerkt), changes.md.
[2026-07-11p] Media naar Azure Blob Storage (IMediaStore)
Section titled “[2026-07-11p] Media naar Azure Blob Storage (IMediaStore)”Aanleiding: routekaart-item — opnames (foto/video) stonden nog uitsluitend op de lokale schijf van de API-container (LocalMediaStore); dat overleeft geen container-herstart/-schaling en is niet geschikt voor productie. ADR-008 had deze precieze uitbreiding al voorzien (“Azure Blob kan via een dunne AzureBlobMediaStore met dezelfde interface — wisselen = DI/config”). Implementatie-entry; geen modelwijziging → geen modelversie-bump (pure infra-swap achter de bestaande poort IMediaStore).
Wijzigingen: nieuwe AzureBlobMediaStore : IMediaStore (Azure.Storage.Blobs) naast de bestaande LocalMediaStore; fail-graceful config-keuze: zonder Media__BlobConnectionString blijft de lokale schijf de dev-fallback, mét connection string wordt Blob Storage gebruikt (container standaard media, overschrijfbaar via Media__BlobContainer) — geen domein- of API-wijziging, uitsluitend een DI-registratiekeuze in AddInfrastructure. Getest tegen een echte Azurite-emulator in een Testcontainers-fixture (geen mocks, geen Azure-kosten in de testrun). Het daadwerkelijk aanmaken van een Azure Storage Account en het configureren van de connection string op de live App Service is bewust niet uitgevoerd in deze iteratie (nieuwe, doorlopende kleine kosten) — de code is inert zonder die configuratie en kan zonder verdere codewijziging aangezet worden zodra gewenst.
Wijzigingen per bestand: backend Infrastructure/Services/InfraServices.cs (AzureBlobMediaStore, gedeelde MediaExtensies-helper), Infrastructure/DependencyInjection.cs (config-gestuurde keuze), Api/Program.cs (Media:BlobConnectionString/Media:BlobContainer), ISeeYou.Infrastructure.csproj (Azure.Storage.Blobs); test tests/ISeeYou.IntegrationTests/AzureBlobMediaStoreTests.cs (nieuw, Testcontainers.Azurite); changes.md.
[2026-07-11o] Infrastructure as Code met Pulumi; meerdere omgevingen (voorstel, issue #24)
Section titled “[2026-07-11o] Infrastructure as Code met Pulumi; meerdere omgevingen (voorstel, issue #24)”Aanleiding: issue #24 — er is nu precies één omgeving (resourcegroep Clermond) en geen infrastructure-as-code; de opdrachtgever wil op termijn minimaal test/preproductie/productie, met een e2e-gate vóór promotie naar preproductie, en stelde zelf self-hosted Pulumi voor. Implementatie-entry; geen modelwijziging → geen modelversie-bump.
Wijzigingen: nieuwe ADR-039, in twee delen met een bewust verschillende status. Aanvaard en gebouwd: Pulumi (TypeScript) als IaC-tool; de bestaande productie-resources voor I See You staan gecodificeerd in infra/ — puur beschrijvend, er is geen pulumi up/import tegen de echte Azure-omgeving uitgevoerd. Voorgesteld, ter beoordeling: twee extra resourcegroepen (Clermond-Test, Clermond-Preprod) met dezelfde stack, plus een pijplijn-uitbreiding die bij elke push automatisch naar test deployt, daar de volledige Playwright-e2e-suite draait, en pas na een groene run (handmatig goedgekeurd) naar preproductie promoveert — dit deel wacht op goedkeuring van de opdrachtgever vanwege de doorlopende extra Azure-kosten.
Wijzigingen per bestand: docs architectuur/beslissingen.md (ADR-039 + index), architectuur/deployment.md (nieuwe sectie “Meerdere omgevingen”), changes.md; nieuwe map infra/ (Pulumi-project + README).
[2026-07-11l] PWA: het gebruikersportaal is installeerbaar (routekaart fase 2)
Section titled “[2026-07-11l] PWA: het gebruikersportaal is installeerbaar (routekaart fase 2)”Aanleiding: Routekaart fase 2, opgepakt op verzoek van de opdrachtgever-zijde (11-7-2026): een camera-first product hoort op het thuisscherm; het portaal was een gewone website zonder manifest of iconen (zelfs zonder favicon). Implementatie-/presentatie-entry; geen modelwijziging → geen modelversie-bump.
Wijzigingen: het gebruikersportaal is een PWA (vite-plugin-pwa): web-app-manifest (naam, beschrijving, display: standalone, merk-themakleur #1FA3AD) + app-iconen uit het oog-logo (192/512 + maskable + apple-touch-icon, gegenereerd met sharp); service worker (Workbox generateSW, autoUpdate) die alléén de app-shell cachet met SPA-navigatiefallback — al het API-/media-verkeer is cross-origin en blijft altijd netwerk (geen stale uitslagen; retentiebeloften onverkort). Web push bewust buiten scope (aparte vervolgstory). Het beheerportaal blijft een gewone website.
Wijzigingen per bestand: frontend vite.config.ts (VitePWA-configuratie), index.html (theme-color, favicon, apple-touch-icon), public/pwa-*.png + apple-touch-icon.png (nieuw), package.json (dev-dependency); docs architectuur/frontend.md (sectie PWA), changes.md.
[2026-07-11k] Onboarding-funnel gemeten: portaal-instrumentatie + funnel-weergave in beheer (routekaart fase 2)
Section titled “[2026-07-11k] Onboarding-funnel gemeten: portaal-instrumentatie + funnel-weergave in beheer (routekaart fase 2)”Aanleiding: Routekaart fase 2, opgepakt op verzoek van de opdrachtgever-zijde (11-7-2026); vervolg op 2026-07-11j: de meet-infrastructuur bestond, maar het gebruikersportaal stuurde nog geen events en beheer had geen funnel-zicht op de grootste activatie-frictie (de eerst-2-beoordelen-poort). Implementatie-/telemetrie-entry; geen modelwijziging → geen modelversie-bump.
Wijzigingen: het gebruikersportaal instrumenteert de zes funnel-stappen via een nieuwe meet-helper (fire-and-forget, keepalive, meet-id per browsersessie in sessionStorage, eenmalig-per-sessie waar passend): portaal_geopend (app-start), welkom_gezien (welkomstscherm getoond), poort_gestart (eerste keer beoordeelwerk ophalen), beoordeling_gegeven (opinie ingediend), vraag_gesteld (vraag ingestuurd), resultaat_ontgrendeld (uitslag ontgrendeld). Het beheer-endpoint GET /api/beheer/statistieken levert nu ook periode-totalen in funnel-volgorde (uniek = distinct meet-id’s over de hele periode, inclusief nul-stappen); de beheertab Statistieken toont daarboven een funnel-weergave met per stap de unieke bezoekers en de conversie t.o.v. de vorige stap.
Wijzigingen per bestand: frontend meet.ts (nieuw), main.tsx, App.tsx, screens/Geef.tsx, screens/StelVraag.tsx, screens/MijnVraagDetail.tsx; backend Api/Meetpunten.cs (totalen); beheer tabs/Statistieken.tsx (funnel), api.ts; docs functionele-beschrijving/onboarding.md + systeembeheer.md, changes.md; integratietest uitgebreid met de funnel-totalen.
[2026-07-11j] First-party product-analytics: events + beheertab Statistieken (routekaart fase 0, top-5 nr. 5)
Section titled “[2026-07-11j] First-party product-analytics: events + beheertab Statistieken (routekaart fase 0, top-5 nr. 5)”Aanleiding: Routekaart fase 0, opgepakt op verzoek van de opdrachtgever-zijde (11-7-2026): er was geen enkel funnel-inzicht — hoeveel bezoekers openen het portaal, halen de onboarding-poort, stellen een vraag? Implementatie-/telemetrie-entry; geen modelwijziging → geen modelversie-bump (de metingen leven bewust in de Infrastructure-laag, niet in het domein).
Wijzigingen: first-party, cookieloze event-registratie zonder PII (nieuwe ADR-036; afgewogen tegen PostHog EU/Plausible): publiek endpoint POST /api/events met strikte allowlist van funnel-gebeurtenistypen (portaal_geopend → welkom_gezien → poort_gestart → beoordeling_gegeven → vraag_gesteld → resultaat_ontgrendeld); een gebeurtenis is (type, meetId, tijdstip) met een willekeurige meet-id per browsersessie (niet herleidbaar); opslag in tabel Meetgebeurtenissen (EF-migratie); dagelijkse opruimtaak verwijdert metingen ouder dan 90 dagen; GET /api/beheer/statistieken (admin) levert aantallen + unieke meet-id’s per dag per type; nieuwe beheertab Statistieken (periode 7/14/30/90 dagen). De instrumentatie van het gebruikersportaal + de funnel-weergave volgen in de aparte onboarding-funnel-story.
Wijzigingen per bestand: docs architectuur/beslissingen.md (ADR-036 + index), functionele-beschrijving/systeembeheer.md (sectie Statistieken), changes.md; backend Api/Meetpunten.cs (nieuw: endpoints + allowlist + opruimtaak), Api/Program.cs (registratie), Infrastructure/Persistence/Meetgebeurtenis.cs (nieuw), AppDbContext (DbSet + indexen), EF-migratie Meetgebeurtenissen; beheer tabs/Statistieken.tsx (nieuw), App.tsx/router.ts/ui.tsx/api.ts; integratietests (ingest + allowlist + beheer-statistieken).
[2026-07-11i] Voorstel: eensgezindheid in de uitslag + aanvinkbare verbeterpunten (ter beoordeling Han)
Section titled “[2026-07-11i] Voorstel: eensgezindheid in de uitslag + aanvinkbare verbeterpunten (ter beoordeling Han)”Aanleiding: brainstorm van Han (ChatGPT-sessie, 11-7-2026): (1) een gemiddelde zegt niets over de overeenstemming — een 3,0 uit tien 3’en is iets anders dan een 3,0 uit vijf 2’en en vijf 4’en (“smaakgevoelig”); (2) per sectie aanvinkbare verbeterpunten (met dropdown voor bv. kleur/maat) als concreet advies én terugkeer-prikkel (“kom terug en zie of je beter scoort”). Documentatie/voorstel; geen modelwijziging → geen modelversie-bump.
Wijzigingen: nieuwe pagina Voorstel — eensgezindheid & verbeterpunten (ter beoordeling): §1 spreiding/eensgezindheid naast elk sectie- en attribuutgemiddelde (mini-histogram + label eensgezind/gemengd/verdeeld op σ-drempels 0,5/1,0, beheerinstelbaar; AI telt mee; geen nieuwe data nodig), §2 verbeterpunten als keuzelijst-stamdata per sectie (type verbeterpunt, max. 5 getoond, optionele dropdown-parameters, geen moderatie nodig) met de expliciete keuze optie A (gratis tussenlaag) vs. optie B (alleen niveau-slots) in relatie tot het betaald-advies-model, §3 “Mijn voortgang” (compacte sectiegemiddelden + verbeterpunt-aggregaten bewaren over vragen heen — een bewuste, wisbare versoepeling van de retentiebelofte; beeld/tekst/individuele scores verdwijnen onverkort), §4 geraakte beslissingen en §5 checklist voor Han.
Wijzigingen per bestand: docs voorstel-2026-07-11.md (nieuw), astro.config.mjs (sidebar-entry, badge “voorstel”), changes.md.
[2026-07-11h] Echte PSP: Mollie-adapter achter IBetaalPoort (iDeal-inleg + teaser); poort async + status-opvraag (routekaart fase 1)
Section titled “[2026-07-11h] Echte PSP: Mollie-adapter achter IBetaalPoort (iDeal-inleg + teaser); poort async + status-opvraag (routekaart fase 1)”Aanleiding: Routekaart fase 1, lanceerblokker (top-5): de betaalstroom draaide op een gesimuleerde (dummy) iDeal — “zonder echt geld geen echt verdienmodel”. Keuze bevestigd (opdrachtgever-zijde, 11-7-2026): Mollie als PSP (iDeal-thuisspel, NL-eerst, EUR, laagste frictie), na een vergelijking met Stripe. Implementatie-/infrastructuur-entry; het domeinmodel (aggregates/termen/invarianten) verandert niet — alleen de poort-/adapterlaag → geen modelversie-bump.
Wijzigingen: nieuwe ADR-035 — Mollie als PSP-implementatie achter IBetaalPoort, met de dummy als terugval voor lokaal/CI (zonder Mollie:ApiKey). De poort wordt uitgebreid omdat een echte PSP anders werkt dan de dummy: Mollie roept de webhook server-to-server aan met alleen een payment-id (geen status in de body). Daarom wordt Start async (StartAsync → BetaalStart) en krijgt de poort IsGeslaagdAsync(referentie) → bool?; een nieuwe publieke endpoint POST /api/betalingen/psp-webhook leest het id, haalt de status bij Mollie op (GET /v2/payments/{id}, body nooit vertrouwd) en roept de bestaande, idempotente BetaalService.WebhookAsync aan. De bestaande body-based /api/betalingen/webhook blijft voor de dummy. Secrets Mollie__ApiKey + publieke API-basis-URL (App__ApiBaseUrl, voor de webhookUrl) via Azure-config/Key Vault. De frontend-flow (opwaarderen → redirect → webhook) blijft gelijk; bij Mollie is de redirect de gehoste iDeal-checkout met een retourpagina die het saldo herlaadt. Buiten scope (latere story): geautomatiseerde uitbetaling aan beoordelaars (IUitbetaalPoort / Mollie Connect of heroverweeg Stripe/Adyen), inclusief KYC en de DAC7-rapportageplicht.
Wijzigingen per bestand: docs architectuur/beslissingen.md (ADR-035 + index; ADR-029 beperking bijgewerkt), architectuur/tech-stack.md (PSP = Mollie + status-opvraag), functionele-beschrijving/betaald-advies.md (PSP = Mollie; KYC/DAC7-caveat bij uitbetaling), roadmap.md (fase 1 geconcretiseerd), changes.md; backend Application/Abstractions/Ports.cs (IBetaalPoort async + IsGeslaagdAsync), Application/Services.cs (BetaalService naar await StartAsync), Infrastructure/Services/* (MollieBetaalDienst, DummyBetaalDienst async), Infrastructure/DependencyInjection.cs (DI-swap op Mollie:ApiKey), Api/Endpoints.cs (psp-webhook), Api/Program.cs (config); frontend screens/BetalingRetour.tsx (nieuw) + routing; tests.
[2026-07-11g] Visuele moderatie van opnames vóór distributie (routekaart fase 0, top-5 nr. 1)
Section titled “[2026-07-11g] Visuele moderatie van opnames vóór distributie (routekaart fase 0, top-5 nr. 1)”Aanleiding: Routekaart fase 0, opgepakt op verzoek van de opdrachtgever-zijde (11-7-2026): de opname zelf — de kern van het product — ging ongescreend naar de opiniegevers; alleen vrije tekst werd proactief gemodereerd. Ongepaste beelden werden pas via meldingen (reactief) verwijderd, nádat opiniegevers eraan waren blootgesteld. Modelwijziging (nieuwe poort + nieuwe instelling) → modelversie-bump.
Beslissingen:
- Visuele moderatie vóór distributie: de check draait in het stel-vraag-proces, ná de tekstmoderatie van de toelichting en vóór het aanmaken van de vraag en haar slots.
- Afgekeurd = weigeren + direct wissen: de maker krijgt een nette melding (patroon van de afgekeurde tekst); de geüploade opname wordt meteen uit de mediastore verwijderd.
- Fail-open (consistent met ADR-020): zonder sleutel, bij een niet-ondersteund beeldformaat of bij een storing wordt de opname toegestaan; de terugval is gestructureerd gelogd (ADR-032). Het melden blijft als tweede linie.
- Beheerinstelling
visueleModeratieActief(bool, standaard aan), thema Moderatie.
Wijzigingen per bestand: docs functionele-beschrijving/moderatie.md (drie vormen + Deel C), termen.md (nieuwe term visuele moderatie + Systeeminstellingen), architectuur/beslissingen.md (ADR-034 + index), changes.md; backend Domain/Beheer (Systeeminstellingen.VisueleModeratieActief), Application/Abstractions/Ports.cs (IVisueleModeratieDienst), Application/Services.cs (check in VraagService.StelVraagAsync + wissen bij afkeuring), Infrastructure/Services/InfraServices.cs (OpenAiVisueleModeratieDienst + gedeelde FFmpeg-frame-extractie), Infrastructure/DependencyInjection.cs; beheer tabs/Instellingen.tsx (toggle); integratietests (weiger-flow met stub + fail-open-flow).
[2026-07-11f] Integratietest-laag: de echte API tegen Testcontainers-PostgreSQL (routekaart fase 1)
Section titled “[2026-07-11f] Integratietest-laag: de echte API tegen Testcontainers-PostgreSQL (routekaart fase 1)”Aanleiding: Routekaart fase 1, opgepakt op verzoek van de opdrachtgever-zijde (11-7-2026): de integratietest-laag die de deployment-documentatie al beschreef, bestond nog niet — endpoint-gedrag (autorisatie, credits-poort, upgrade-migratie) werd alleen indirect door e2e-tests op een draaiende omgeving gedekt. Implementatie-/test-entry; geen modelwijziging → geen modelversie-bump.
Wijzigingen: nieuw testproject tests/ISeeYou.IntegrationTests — WebApplicationFactory over de echte API met een echte PostgreSQL in een container (Testcontainers, postgres:16-alpine); migraties + seed draaien bij het opstarten precies zoals in productie; géén mocks. 10 tests dekken: registreren/inloggen (incl. fout wachtwoord), admin- vs. actor-token-autorisatie op de beheer-endpoints, publiek-lezen/admin-schrijven van de instellingen, de media-roundtrip, de anonieme sessie, credits verdienen via de volledige beoordeel-cyclus (keuzeset → kies → opinie), de volledige reis (onboarding-poort met 2 beoordelingen → vraag stellen → test-opinies → uitslag ontgrendelen met het bevroren credittarief), de account-upgrade-identiteitsmigratie (credits + gestelde vragen gaan mee, per 2026-07-11a) en de eigenaarscontrole op het verwijderen van een vraag. Program kreeg een public partial class-declaratie voor de testhost. De tests draaien mee in dotnet test ISeeYou.slnx en dus in de CI-backendjob (Docker vereist op de runner).
Wijzigingen per bestand: backend tests/ISeeYou.IntegrationTests/* (nieuw: csproj, ApiFixture, IntegratieTests), ISeeYou.slnx (project toegevoegd), Api/Program.cs (partial class); docs architectuur/deployment.md (huidige situatie), changes.md.
[2026-07-11e] CI/CD op een self-hosted runner: automatische deploy naar Azure (API, portalen, docs)
Section titled “[2026-07-11e] CI/CD op een self-hosted runner: automatische deploy naar Azure (API, portalen, docs)”Aanleiding: GitHub-hosted runners bleken niet beschikbaar voor deze repo (elke CI-run faalde binnen seconden); daarnaast liep de Azure-deploy tot nu toe los van CI als handmatige stap. Verzoek opdrachtgever-zijde (11-7-2026): een self-hosted runner op een ontwikkelaarslaptop, en de CI/CD-pijplijn zo uitbreiden dat build, test én deploy (inclusief de documentatiesite) automatisch via die runner lopen. Implementatie-/infrastructuur-entry; geen modelwijziging → geen modelversie-bump.
Wijzigingen: nieuwe ADR-033: een self-hosted Windows-runner (actions/runner, geregistreerd op de repo) draait .github/workflows/ci.yml; naast de bestaande docs/app/backend-verificatiejobs zijn er drie deploy-jobs (deploy-docs, deploy-app, deploy-api) die bij een push naar main — alléén voor gewijzigde onderdelen — automatisch uitrollen, met credentials live opgevraagd via de al ingelogde az-CLI op de runner (geen GitHub-secrets nodig). De API-deploy tagt op commit-SHA en pollt de gezondheidscontrole vóór de job slaagt. Nieuwe skill deploy (.claude/skills/deploy/SKILL.md) start de runner (indien nodig) en bewaakt een pijplijn-run. De handmatige az-stappen in de pak-issue-skill blijven als fallback bestaan.
Wijzigingen per bestand: docs architectuur/deployment.md (huidige situatie + CI/CD-in-het-kort + nieuwe sectie “Waarom een self-hosted runner”), architectuur/beslissingen.md (ADR-033 + index), changes.md; CI .github/workflows/ci.yml (changes-detectiejob + drie deploy-jobs); nieuwe skill .claude/skills/deploy/SKILL.md.
[2026-07-11d] Observability: Serilog-gestructureerde logging + Application Insights (routekaart fase 1)
Section titled “[2026-07-11d] Observability: Serilog-gestructureerde logging + Application Insights (routekaart fase 1)”Aanleiding: Routekaart fase 1, opgepakt op verzoek van de opdrachtgever-zijde (11-7-2026): de API logde met losse Console.Error.WriteLine-regels, zonder request-context, metrics of alerting — opschalen zou blind varen zijn. Implementatie-/infrastructuur-entry; geen modelwijziging → geen modelversie-bump.
Wijzigingen: Serilog vervangt de console-writes (gestructureerd, request-logging, console-JSON in productie); Application Insights verzamelt requests/dependencies/exceptions en de logregels als traces, aangezet via de app-setting APPLICATIONINSIGHTS_CONNECTION_STRING (leeg → alleen console, fail-graceful); de drie Infrastructure-diensten met stille terugvallen (moderatie, uitslagmail, AI-persona) loggen nu via ILogger<T> met properties. In Azure (resourcegroep Clermond): Log Analytics-werkruimte + Application Insights-resource + één metric-alert op mislukte requests. Nieuwe ADR-032.
Wijzigingen per bestand: docs architectuur/beslissingen.md (ADR-032 + index), architectuur/deployment.md (huidige situatie + configuratietabel), changes.md; backend Api/ISeeYou.Api.csproj (Serilog + Application Insights-pakketten), Api/Program.cs (UseSerilog, AddApplicationInsightsTelemetry, request-logging), Infrastructure/Services/InfraServices.cs (ILogger in OpenAiModeratieDienst/AcsUitslagNotificaties/OpenAiPersonaService), Infrastructure/DependencyInjection.cs (logger-injectie).
[2026-07-11c] Routekaart naar lancering & groei (voorstel) toegevoegd
Section titled “[2026-07-11c] Routekaart naar lancering & groei (voorstel) toegevoegd”Aanleiding: Vraag opdrachtgever-zijde (11-7-2026): een gefaseerd plan om van de huidige pilot naar een publiek product met veel gebruikers te komen, deelbaar via de documentatiesite. Documentatie/voorstel; geen modelwijziging → geen modelversie-bump.
Wijzigingen: nieuwe pagina Routekaart naar lancering & groei (voorstel ter beoordeling): fase 0 positionering & vangrails (beachhead-niches, DPIA/leeftijdsbeleid, visuele moderatie, analytics), fase 1 productieklaar (Mollie, Blob-mediastore, Argon2id, observability, CD/IaC, Redis, integratietests, pentest, eigen domein, juridische pagina’s), fase 2 activatie & retentie (PWA/push, funnel-metingen, liquiditeits-SLA, Engels, referral & deelkaart), fase 3 lancering & groei (landing, gefaseerd open, community-seeding, PR, app-stores), fase 4 schalen (expertmarktplaats, fraudedetectie, i18n, AI-kwaliteit), plus een KPI-meetlat en een top-5 “doe dit eerst”. Goedgekeurde onderdelen worden stories op het projectboard.
Wijzigingen per bestand: docs roadmap.md (nieuw), astro.config.mjs (sidebar-entry, badge “voorstel”), changes.md.
[2026-07-11b] Beheer: vanuit een vraag naar de maker springen + gebruikerslijst nieuwste eerst
Section titled “[2026-07-11b] Beheer: vanuit een vraag naar de maker springen + gebruikerslijst nieuwste eerst”Aanleiding: Melding opdrachtgever (10/11-7-2026): een zojuist aangemaakt (test)account was in het gebruikersoverzicht lastig terug te vinden — de lijst stond niet op aanmaaktijd en er was geen route van een vraag naar haar maker. Beheer-presentatie; geen modelwijziging.
Wijzigingen: het beheer-vraagdetail maakt de maker klikbaar (bij een geregistreerd account) en springt naar het deep-linkbare gebruikersdetail (#/gebruikers/<naam>); het gebruikersoverzicht sorteert standaard op aanmaaktijd, nieuwste eerst (GET /api/beheer/gebruikers).
Wijzigingen per bestand: backend Api/Endpoints.cs (sortering); beheer tabs/Vragen.tsx (maker-link); docs functionele-beschrijving/systeembeheer.md.
[2026-07-11a] Account-upgrade: volledige identiteitsmigratie (vragen gaan mee)
Section titled “[2026-07-11a] Account-upgrade: volledige identiteitsmigratie (vragen gaan mee)”Aanleiding: Melding opdrachtgever (10/11-7-2026): “Ik had een tijdelijk account, stelde een vraag, genereerde test-opinies, waardeerde het account op naar een vast account om credits te kunnen ontvangen — en toen was mijn vraag ineens weg en kon ik het resultaat alsnog niet inzien.” Oorzaak: de upgrade (per 2026-07-10i) nam credits + sterren mee, maar hernoemde de actor-referenties niet — de gestelde vraag bleef aan de (verwijderde) anonieme identiteit hangen en verdween daarmee uit Mijn vragen. De destijds gedocumenteerde keuze “lopende vragen blijven op de oude actor-referentie staan” is hiermee teruggedraaid. Modelwijziging (upgrade-flow-contract) → modelversie-bump.
Beslissingen:
- De upgrade is een volledige identiteitsmigratie. Mee gaan: gestelde vragen (
Vraag.makerId→ nieuwe gebruikersnaam,makerIsGeregistreerd→ true), de ontvangen opinies (Opinie.targetGebruiker), de zelf gegeven beoordelingen (Opinie.reviewer— die tellen voortaan dus ook mee in de consensus-ranking, consistent met “neem je ster mee”), actieve slots (Opdracht.voorgesteldAan/reviewer— een vastgehouden beoordeling loopt gewoon door) en berichten (Bericht.ontvanger). - Bewust niet mee: meldingen (moderatie-administratie van het moment) en anti-thrash-uitsluitingen (leven ~30 minuten). De anonieme sessie zelf wordt ongewijzigd verwijderd.
- Atomair: de migratie muteert via het change-trackingmodel en commit in dezelfde transactie als de upgrade — geen halve migraties.
Wijzigingen per bestand: docs domeinmodel.md (upgrade-sectie + ontwerpbeslissing 35), termen.md (anonieme gebruiker), changes.md; backend Domain (migratie-methoden op Vraag/Opinie/Opdracht/Bericht), Application (poort IIdentiteitMigratie + aanroep in AccountUpgradeService), Infrastructure (EF-implementatie + DI); domeintests + e2e anoniem-upgrade.spec.ts (replay van het gemelde scenario).
[2026-07-10o] AI-opinies: herkomst zichtbaar + gevarieerde, persona-afhankelijke vangnet-scores (punt 11 Han)
Section titled “[2026-07-10o] AI-opinies: herkomst zichtbaar + gevarieerde, persona-afhankelijke vangnet-scores (punt 11 Han)”Aanleiding: Testsessie Han (9-7-2026), punt 11: “Het genereren met AI levert veel exact dezelfde waarden op (3); 1x ging het beter, weet niet waarom.” Oorzaakanalyse: (1) de deterministische terugval seedde op vraagId|sectie|attribuut — zonder persona — waardoor meerdere AI-opinies op dezelfde vraag exact identieke scores kregen; (2) “1x ging het beter” past bij een geslaagde vision-call — of de vision-route draait was per opinie nergens zichtbaar. Modelwijziging: Opinie krijgt een veld → modelversie-bump.
Wijzigingen:
Opinie.aiHerkomst(nieuw, optioneel):"vision"(OpenAI-vision-route) of"vangnet"(deterministische terugval); null bij een menselijke opinie. Zichtbaar in het beheer-vraagdetail (badge “AI · vision” / “AI · vangnet”) en de beheer-respons; niet in de user-facing uitslag (de maker ziet mens/AI-herkomst sowieso niet, per2026-07-04h). De poortIAiPersonaServicegeeft de herkomst mee inAiBeoordeling.- Vangnet-scores gevarieerd en persona-afhankelijk: de deterministische terugval scoort per persona met een mild/streng-karakter (hash van persona + vraag) plus per-attribuut-variatie (hash van persona + vraag + sectie + attribuut), bereik 2–5 rond een licht positief midden. Deterministisch gebleven (zelfde invoer = zelfde uitslag — testbaar), maar twee persona’s of twee attributen scoren niet meer stelselmatig gelijk. Ook de generieke vangnet-reactie varieert per persona/vraag.
- EF-migratie
OpinieAiHerkomst(nullable kolom; bestaande AI-opinies houden null = “onbekend/historisch”). - Oorzaak “1x ging het beter” gevonden (productie-logs): de vision-route wérkt op productie (echte camera-opnames slaagden), maar de SVG-placeholders van de testvragen-generator worden door het vision-model geweigerd (“unsupported image … png/jpeg/gif/webp”) → bij álle testvragen viel de dienst stilletjes terug op het (toen nog vlakke) vangnet. Nieuw: een mime-guard in
OpenAiPersonaServiceslaat de gedoemde API-call over bij een niet-ondersteund beeldformaat en logt de reden expliciet; de nieuweaiHerkomst-badge maakt zulke gevallen voortaan direct zichtbaar in het beheerportaal.
Wijzigingen per bestand: docs domeinmodel.md (Opinie-diagram), termen.md (AI-opinie), changes.md; backend Domain/Beoordeling/Aggregates.cs (veld + factory), Application/Abstractions/Ports.cs (AiBeoordeling.Herkomst), Infrastructure/Services/InfraServices.cs (variatie + herkomst), Application/Services.cs (doorvoer vangnet/handmatig), Api/Endpoints.cs (beheer-veld), migratie; beheer tabs/Vragen.tsx; shared types.ts; tests (variatie/determinisme + e2e-steekproef).
[2026-07-10n] Beheer: testtools voor het stersysteem + definitieve ster in de uitslag na finalisatie
Section titled “[2026-07-10n] Beheer: testtools voor het stersysteem + definitieve ster in de uitslag na finalisatie”Aanleiding: Vervolg op 2026-07-10m (punt 12 Han), expliciet gevraagd door de opdrachtgever (10-7-2026): het stersysteem is zonder hulpmiddelen lastig te testen — de consensus-ranking vergt minstens minBeoordelaarsVoorRanking menselijke account-beoordelaars per vraag. Beheer-/test-tooling + een kleine read-model-verfijning; geen modelversie-bump.
Wijzigingen:
- Beheer, vraagdetail — drie testfuncties (Testfunctie-label): (1) test-opinies genereren — wegwerp-testgebruikers (echte
Gebruiker-accounts, prefixtestrev_, profiel binnen de doelgroep) vullen open slots met gevarieerde scores; een vrije-reactie-slot krijgt een testgebruiker met het vereiste niveau + geblurde tekstreactie (POST /api/beheer/vragen/{id}/test-opinies); (2) ranglijst-inzage — de consensus-ranglijst met per deelnemer de opbouw C/K/W + gewogen totaal, de gate-status en de (voorlopige of definitieve) ster (GET /api/beheer/vragen/{id}/ranglijst); (3) finaliseer nu — de retentie-finalisatie direct en idempotent uitvoeren (POST /api/beheer/vragen/{id}/finaliseer). - Domein:
ConsensusRanking.BepaalScoresstelt de componentscores (C, K, W, totaal) per deelnemer beschikbaar;BepaalRanglijstgebruikt intern dezelfde berekening (gedrag ongewijzigd). - Uitslag-verfijning: is een vraag al gefinaliseerd, dan markeert
GET /api/vragen/{id}/uitslagde opinie van de definitieve winnaar (de toegekende “beste van de vraag”-ster) in plaats van de live herberekende voorlopige ster.
Wijzigingen per bestand: docs functionele-beschrijving/systeembeheer.md; backend Domain/Beloning/Beloning.cs (BepaalScores), Application/Services.cs (TestOpinieService, SterToekenningService.BepaalPositiesVoor, definitieve ster in OpinieService.UitslagAsync), Api/Endpoints.cs (drie beheer-endpoints), repositories (ster-voor-vraag-lookup), domeintests; beheer tabs/Vragen.tsx (stersysteem-paneel), api.ts; shared types.ts.
[2026-07-10m] Resultaatscherm: voorlopige ster bij de best gerankte beoordeling (punt 12 Han)
Section titled “[2026-07-10m] Resultaatscherm: voorlopige ster bij de best gerankte beoordeling (punt 12 Han)”Aanleiding: Testsessie Han (9-7-2026), punt 12: “graag in het resultaatscherm een ster achter de juiste beoordelaar plaatsen … misschien dat de vragensteller het dan weet en bewust kan afwijken of niet (jouw optie graag).” Han delegeerde de timing-keuze expliciet aan ons. Presentatie-/read-model-verfijning; geen aggregate-wijziging → geen modelversie-bump. Wél een vastgelegde beleidsbeslissing (timing).
Beslissingen:
- Timing (vastgelegd, conform onze aan Han voorgelegde aanbeveling): de ster in het resultaatscherm is voorlopig (indicatief) en verschijnt pas zodra de maker geen waardeer-werk meer open heeft staan — alle beoordelingen met leesbare tekst zijn gewaardeerd (geen waardeerbare beoordelingen → direct). Reden: de maker-waardering weegt mee in de consensus-ranking; een eerder zichtbare ster zou de waarderingen sturen (zichzelf versterkende bias). Ontgrendelt de maker daarna nog een betaald advies, dan is er weer een onbeoordeelde tekst-beoordeling en verdwijnt de ster tot ook die gewaardeerd is.
- Voorlopig karakter: de getoonde ster is een indicatie op basis van de waarderingen tot nu toe. De echte ster wordt ongewijzigd pas toegekend bij de finalisatie (einde retentievenster of eerder verwijderen van de vraag) met exact dezelfde berekening — dan inclusief de definitieve waarderingen.
- Ranking-drempel blijft gelden: bij minder dan
minBeoordelaarsVoorRanking(standaard 3) menselijke account-beoordelaars is er geen ranglijst en dus geen ster. - Anonimiteit: de ster staat alleen bij de beoordeling; naam of profiel van de beoordelaar blijven onherleidbaar.
- Engagement-nudge (verbetering): zolang er nog waardeerbare beoordelingen niet gewaardeerd zijn, nodigt het scherm expliciet uit om alles te waarderen (“waardeer alles → zie de beste”). Meer waarderingen = betekenisvollere ranking, sterren en adviesniveaus — en daarmee een gezondere betaald-advies-economie.
Wijzigingen:
- API:
GET /api/vragen/{id}/uitslagkrijgt per beoordeling een veldster(bool; hooguit ééntrue): de voorlopig best gerankte beoordeling, alleen gevuld wanneer de timing-voorwaarde hierboven is vervuld én er een ranglijst is. - Applicatie: de ranglijst-berekening uit de finalisatie is herbruikbaar gemaakt (
SterToekenningService.BepaalRanglijst+VoorlopigeBesteOpinie, pure functies over de opinies + instellingen) — finalisatie en voorlopige ster gebruiken één en dezelfde berekening. - Gebruikersportaal: ★-badge “Beste beoordeling” op de best gerankte beoordeling, een uitlegregel over het voorlopige karakter, en de waardeer-nudge zolang niet alles gewaardeerd is; na elke waardering wordt de uitslag ververst.
Wijzigingen per bestand: docs scenarios/ster-verdienen.md, functionele-beschrijving/resultaat-terugzien.md, termen.md (consensus-ranking); backend Application/Services.cs (SterToekenningService + OpinieService), Api/Endpoints.cs (uitslag-veld ster), domeintests; frontend shared/src/types.ts, screens/MijnVraagDetail.tsx, i18n/nl.ts, e2e sterren.spec.ts.
[2026-07-10l] Fix: SchaalVijf-datamigratie schreef null-sectiegemiddelden voor tekst-only-opinies
Section titled “[2026-07-10l] Fix: SchaalVijf-datamigratie schreef null-sectiegemiddelden voor tekst-only-opinies”Aanleiding: Tijdens de live-verificatie van bundel B bleek GET /api/beheer/vragen/{id} met HTTP 500 te crashen op vragen met alleen-vrije-tekst-opinies van vóór 2026-07-10h. Oorzaak: de SchaalVijf-datamigratie (2026-07-10f) herrekende de sectiegemiddelden met een LEFT JOIN LATERAL die over een lege secties-array één NULL-rij opleverde → {"sectie": null, "gemiddelde": null} in de jsonb, waar de System.Text.Json-deserialisatie op stukloopt (raakte ook de retentie-finalisatie van zulke vragen).
Fix: correctie-datamigratie SchaalVijfNulSectiesFix filtert de null-entries uit Opinies.SectieGemiddelden (→ lege lijst). Lokaal geverifieerd: 21 corrupte rijen hersteld, alle 83 vraagdetails weer 200. Geen modelwijziging.
[2026-07-10k] Resultaatscherm: waarderen alleen bij (ontgrendelde) tekst-beoordelingen, met voortgang
Section titled “[2026-07-10k] Resultaatscherm: waarderen alleen bij (ontgrendelde) tekst-beoordelingen, met voortgang”Aanleiding: Testsessie Han (9-7-2026), issue #13: “Nu voor alle 10 de opinies een oordeel geven; alle niveaus met 0 eruit filteren” — door Han verduidelijkt: een beoordeling van niveau 0 (alleen smileys) hoef je niet te waarderen; waarderen is alleen relevant op beoordelingen met tekst, en een geblurd advies kun je pas beoordelen als je ervoor betaald hebt. Presentatie-/regelverfijning in het gebruikersportaal; geen modelwijziging (het waardering-endpoint blijft ongewijzigd).
Wijzigingen:
- Mijn vragen → resultaat: de smiley-waardering verschijnt alleen bij beoordelingen met een leesbare tekstreactie; alleen-smiley-beoordelingen (niveau 0) tonen geen beoordeeloptie; een geblurd betaald advies is pas te waarderen na het ontgrendelen (dan wordt de tekst leesbaar).
- Voortgang: het scherm toont “x van y gewaardeerd” over de waardeerbare beoordelingen.
- Met het slot-model van
2026-07-10hheeft elke vraag hooguitmaxVrijeReactiesZonderBetaald(of de niveaueisen-aantallen) waardeerbare tekst-beoordelingen — precies het “niveaus met 0 eruit filteren” van Han.
Wijzigingen per bestand: docs functionele-beschrijving/resultaat-terugzien.md; frontend screens/MijnVraagDetail.tsx, i18n/nl.ts; e2e-dekking in vrije-reacties.spec.ts (geblurde teksten) + bestaande waardering-specs.
[2026-07-10j] Ongewenst markeren: meldknop, reservering-vrijgave, configureerbare uitleg en maker-teller
Section titled “[2026-07-10j] Ongewenst markeren: meldknop, reservering-vrijgave, configureerbare uitleg en maker-teller”Aanleiding: Testsessie Han (9-7-2026), issue #12: “Filmpje of foto markeren als ongewenst … er moet een via het beheerportaal te configureren tekst aan de gebruiker getoond worden en we moeten bijhouden hoeveel filmpjes van een gebruiker als ongewenst gemarkeerd zijn.” Plus: bij vervallen moet de saldo-blokkade (reservering, issue #16) vervallen. Kolom-toevoeging + ingebruikname van het bestaande Bericht-aggregate; geen aggregate-structuurwijziging → geen modelversie-bump.
Wijzigingen:
- Meldknop in het beoordeelscherm: “Markeer als ongewenst” naast de opname (
POST /api/vragen/{id}/meldbestond al); na melden gaat de beoordeling terug naar de pool. - Reservering-vrijgave bij vervallen: de moderatie-verwijdering liep al via
VraagOpruimer.VerwijderVolledigAsync, die sinds issue #16 het onbevestigde deel van de betaald-advies-reservering vrijgeeft — nu expliciet vastgelegd én per e2e getest. - Configureerbare tekst: bij het vervallen ontstaat een
Berichtaan de maker met de tekst uit keuzelijstuitleg, sleutelvraag-vervallen(geseed; beheerbaar via het beheerportaal). Nieuwe endpointsGET /api/berichten(eigen ongelezen berichten) enPOST /api/berichten/{id}/gelezen; “Mijn vragen” toont het bericht als wegklikbare melding. - Teller per maker:
Gebruiker.AantalOngewenstVervallen(EF-migratieOngewenstTeller), opgehoogd bij vervallen door de drempel; zichtbaar in het beheer-gebruikersoverzicht. ⚠️ Vorm van verdere waarschuwing/blokkade: voorstel in de docs, ter verificatie bij Han.
Wijzigingen per bestand: docs functionele-beschrijving/moderatie.md (eisen 6–9), scenarios/melden-en-verwijderen.md (stap 9); backend Domain/Gebruikers/Gebruikers.cs (teller), Application (ModeratieService + IBerichtRepository), Api/Endpoints.cs (berichten-endpoints, beheer-gebruikers), Infrastructure (BerichtRepository, migratie OngewenstTeller, DbSeeder vraag-vervallen); shared types.ts; frontend api.ts, screens/Geef.tsx (meldknop), screens/MijnVragen.tsx (berichten-banner), i18n/nl.ts; beheer tabs/Gebruikers.tsx, tabs/Keuzelijsten.tsx (hint); e2e ongewenst.spec.ts.
[2026-07-10i] Anoniem account opwaarderen naar een vast account (modelwijziging)
Section titled “[2026-07-10i] Anoniem account opwaarderen naar een vast account (modelwijziging)”Aanleiding: Testsessie Han (9-7-2026), issue #9: “Wanneer je een ster hebt gekregen moet je de keuze krijgen om een profiel te maken” — plus de aanvulling dat de upgrade ook altijd zelf gestart moet kunnen worden. Anonieme sessies dragen geen sterren en verlopen inclusief credits; opgebouwde waarde ging verloren.
Genomen beslissingen:
AnoniemeGebruiker.totaalVerdiendeCredits(levensloop-teller, alleen met beoordelen verdiende credits; EF-migratieAnoniemUpgrade) — de basis voor het stermoment.POST /api/anoniem/upgrade(beveiligd; alleen voor een anonieme actor): registreert eenGebruikermet hetzelfde profiel, neemt het credit-saldo mee als startsaldo, kent per gepasseerdesterCreditsMijlpaaleen ster toe (bronCreditsMijlpaal), verwijdert de anonieme sessie en geeft een nieuw actor-token + saldo terug.- Wat niet overgaat: waarderingsstatistieken/beoordelingshistorie; bestaande vragen blijven op de oude actor-ref.
- UI: upgrade-kaart in Mijn profiel (alleen anoniem) + een wegklikbare ster-uitnodiging in het opinie-scherm zodra
totaalVerdiendeCredits ≥ sterCreditsMijlpaal;GET /api/profiel(anoniem) geefttotaalVerdiendeCreditsmee. - Ontwerpbeslissing 35.
Wijzigingen per bestand: docs domeinmodel.md (AnoniemeGebruiker + upgrade-flow + beslissing 35), termen.md (Anonieme gebruiker), functionele-beschrijving/aanmelden-registratie.md (upgrade-sectie); backend Domain/Gebruikers/Gebruikers.cs (teller + RegistreerVerdiendeCredits), Application/Services.cs (AccountUpgradeService; OpinieService boekt anonieme verdiensten), Api/Endpoints.cs (upgrade-endpoint, profiel-respons), migratie AnoniemUpgrade; shared types.ts; frontend api.ts (upgradeAnoniem), screens/MijnProfiel.tsx (upgrade-kaart), screens/Geef.tsx (ster-uitnodiging), i18n/nl.ts; e2e anoniem-upgrade.spec.ts.
[2026-07-10h] Vrije reacties alleen van niveau-beoordelaars, via vrije-reactie-slots (modelwijziging)
Section titled “[2026-07-10h] Vrije reacties alleen van niveau-beoordelaars, via vrije-reactie-slots (modelwijziging)”Aanleiding: Testsessie Han (9-7-2026), issue #17: “Vrije reacties ontvangen kan alleen door beoordelaars met niveau 1 t/m 4. Hiervan zitten er maximaal 3 bij een vraag waaraan vooraf geen betaald advies is gekoppeld, anders is dat leidend.” Aanvulling: de keuze “vrije tekst gevraagd” bij het stellen vervalt; vanaf niveau 1 is tekst verplicht, niveau 0 levert geen tekst; het maximum (3) en het aantal beoordelaars (10) zijn instelbaar met validatie 3 < 10; of het AI-vangnet ook de teaser-slots vult is een beheerinstelling.
Genomen beslissingen:
- Slot-typen:
Opdracht.minimumNiveau(Adviesniveau, defaultGeen; EF-migratieVrijeReactieSlots). Zonder betaald advies krijgen max.maxVrijeReactiesZonderBetaald(nieuw, default 3) slotsNiveau1; mét betaald advies zijn deniveaueisenleidend (per eisaantalslots op dat niveau, geen extra teaser-slots). - Verdeling: de keuzeset biedt een niveau-slot alleen aan beoordelaars met minstens dat adviesniveau aan; gewone slots aan iedereen.
- Reactie slot-gedreven: verplicht op een niveau-slot (betaald/geblurd; bij een
niveaueisen-slot de bevroren eis-prijs, bij een teaser-slot de prijs van het eigen niveau); op een gewoon slot wordt géén tekst gevraagd of opgeslagen — ook niet van een niveau-beoordelaar (diens opinie is daar gratis,adviesniveauSnapshot = Geen). - Maker-keuze vervalt:
Beoordelingsselectie.vrijeTekstGevraagdfunctioneel vervallen (jsonb-veld blijft leesbaar voor bestaande data); een vraag is geldig met minstens één sectie (alleen-vrije-tekst-vragen vervallen); de checkbox “Vrije reactie ontvangen” is uit het vraagformulier. - AI-vangnet: nieuwe bool
aiVangnetVultVrijeReactieSlots(default true) — true: AI levert op niveau-slots een (gratis, direct leesbare) tekst; false: alleen scores. Op gewone slots levert de AI geen tekst. De vraag rondt altijd af. - Validatie:
maxVrijeReactiesZonderBetaald < aantalOpiniegevers, afgedwongen inPUT /api/beheer/instellingenén de beheer-UI. - Ontwerpbeslissing 34; ADR-025 (maker-keuze-deel) herzien; instelling
minTeaserNiveauBeoordelaarsvervallen.
Wijzigingen per bestand: docs domeinmodel.md (Opdracht slot-typen, Beoordelingsselectie, Opinie.reactie/betaald advies, Systeeminstellingen, AI-vangnet, beslissing 34, klassendiagram), termen.md (nieuwe term Vrije-reactie-slot; Beoordelingsselectie, Reactie, Opinie, Opdracht, Adviesniveau, Teaser, Systeeminstellingen), functionele-beschrijving/vraag-stellen.md, opinie-geven.md, betaald-advies.md, systeembeheer.md, scenarios/matching-opiniegevers.md, architectuur/backend-en-api.md, architectuur/beslissingen.md (ADR-025-notitie); backend Domain/Beoordeling (Opdracht.MinimumNiveau, SlotPlanner, Vraag-validatie), Application/Services.cs (VraagService, VerdeelService, OpinieService, OnderhoudService, AiOpinieService), Domain/Beheer/Beheer.cs (instellingen), Api/Endpoints.cs (slot-detail vereistReactie, instellingen-validatie), Infrastructure (migratie VrijeReactieSlots, AI-diensten); shared types.ts; frontend screens/StelVraag.tsx (checkbox weg), screens/Geef.tsx (reactieveld slot-gedreven), screens/MijnVraagDetail.tsx, i18n/nl.ts; beheer tabs/Instellingen.tsx (nieuwe velden + validatie, bool-support), tabs/Vragen.tsx; e2e vrije-reacties.spec.ts.
[2026-07-10g] Saldo-reservering voor betaald advies
Section titled “[2026-07-10g] Saldo-reservering voor betaald advies”Aanleiding: Testsessie Han (9-7-2026), issue #16: “Voor betaalde opinies moet vooraf voldoende saldo zijn.” Aanvulling: je mag niet meer betaald advies vastleggen dan je saldo dekt — bij het insturen een reservering, bij opvragen bevestigd, bij verwijderen geannuleerd; duidelijke foutmeldingen. Financiële regel; geen aggregate-structuurwijziging, wel een kolom → geen modelversie-bump.
Wijzigingen:
Gebruiker.GereserveerdEuro(+ afgeleideBeschikbaarEuro = EuroSaldo − GereserveerdEuro) metReserveerEuro/BevestigGereserveerdEuro/GeefReserveringVrij;HeeftEuro/BesteedEurochecken nu op het beschikbare saldo. EF-migratieGereserveerdEuro(numeric(10,2), default 0).- Insturen (
VraagService): een vraag met niveaueisen reserveert de bevroren totaalprijs; onvoldoende beschikbaar saldo →DomainExceptionmet bedrag + hint. Vereist een geregistreerd account. - Ontgrendelen (
OntgrendelTeaserAsync): bij een vooraf gevraagde (niveaueisen) vraag bevestigt het ontgrendelen de reservering (echte afschrijving + beoordelaars-aandeel); de max-1-teaser-regel geldt daar niet. - Vrijgeven (
VraagOpruimer): bij verwijderen/vervallen/retentie-opruiming komt het nog niet bevestigde deel van de reservering weer vrij (grondslag voor issue #12).VraagOpruimerkreegIGebruikerRepository. - Zichtbaarheid:
GET /api/eurosaldo/huidigen/api/profielgevengereserveerdEuro/beschikbaarEuro; Mijn profiel toont gereserveerd + vrij beschikbaar. - Nieuwe e2e-spec
saldo-reservering.spec.ts(reserveren + vrijgeven bij verwijderen; weigeren bij tekort).
Wijzigingen per bestand: backend Domain/Gebruikers/Gebruikers.cs, Application/Services.cs (VraagService, OntgrendelTeaserAsync, VraagOpruimer), Api/Endpoints.cs, Infrastructure/Persistence/AppDbContext.cs + migratie GereserveerdEuro; shared types.ts; frontend screens/MijnProfiel.tsx, i18n/nl.ts; docs functionele-beschrijving/betaald-advies.md; e2e saldo-reservering.spec.ts.
[2026-07-10f] Smiley-schaal overal 5 treden + beheerbare smiley-reeks (modelwijziging)
Section titled “[2026-07-10f] Smiley-schaal overal 5 treden + beheerbare smiley-reeks (modelwijziging)”Aanleiding: Testsessie Han (9-7-2026), issue #15: “Emojis wat meer positief maken: ☹️ 😕 😐 🙂 😊” + aanvulling: “Maak de smiley-beoordeling overal 5 treden en maak de smileys in het beheerportaal te configureren; de 6-puntsschaal moet vertaald worden naar 5, ook de berekeningen.”
Genomen beslissingen:
- Score-schaal 1..5 (was 1..6): domeininvariant
AttribuutScore, het scoorscherm (5 smileys), de AI-dienst (prompt + clamp) en de teksten. De waardering door de maker was al 5 treden (1–5; “wissen” zet terug naar niet-gewaardeerd). - Berekeningen mee: de consensus-component van de ranking normaliseert op de nieuwe maximale afwijking (÷4 i.p.v. ÷5).
- Datamigratie
SchaalVijf: bestaande opinie-scores met waarde 6 → 5 (jsonb-rewrite) en de opgeslagen sectiegemiddelden per opinie herrekend. - Smiley-reeks beheerbaar: keuzelijst-type
smiley(sleutelss1..s5, omschrijving = emoji), geseed met Hans reeks ☹️ 😕 😐 🙂 😊; het gebruikersportaal laadt de reeks bij het opstarten met de ingebouwde reeks als fallback. Nieuwe positievere standaard (geen extreem negatieve gezichten meer). - Kleurmapping per trede: 1 coral · 2 amber-diep · 3 amber · 4 leaf · 5 brand.
Wijzigingen per bestand: backend Domain/Beoordeling/ValueObjects.cs (AttribuutScore 1..5), Domain/Beloning/Beloning.cs (consensus ÷4), Infrastructure/Services/InfraServices.cs (AI-prompt/clamp), migratie SchaalVijf, DbSeeder.cs (smiley-seed); frontend data.ts (SCHAAL_MAX, beheerbare SMILEYS + laadSmileys, kleurmapping), ui.tsx (ScoreScale 1..5), App.tsx (laadSmileys), i18n/nl.ts, data.test.ts; beheer tabs/Keuzelijsten.tsx (smiley-type + hint), tabs/Instellingen.tsx (tekst); docs: alle levende 1–6-vermeldingen naar 1–5 (termen, scenario’s, functionele beschrijvingen, architectuur, domeinmodel).
[2026-07-10e] Profiel wijzigen door de gebruiker
Section titled “[2026-07-10e] Profiel wijzigen door de gebruiker”Aanleiding: Testsessie Han (9-7-2026), issue #11: “Profiel wijzigen.” Het scherm “Mijn profiel” toonde de gegevens maar had geen bewerkfunctie.
Wijzigingen:
Gebruiker.WijzigProfiel(Profiel)in het domein + nieuw endpointPUT /api/profiel(alleen geregistreerde gebruikers; profielvelden incl. regio + het optionele herstel-e-mailadres).- Mijn profiel → Bewerken: bewerkmodus met dezelfde
ProfielVeldenals bij registratie (incl. het regio-veld per land) en het e-mailveld. - Regel vastgelegd (aanmelden-registratie): wijzigingen werken direct door in de matching van nieuwe vragen; bevroren
ProfielSnapshots op bestaande vragen/beoordelingen blijven staan. Geen wijziging aan aggregates-structuur → modelversie ongewijzigd.
Wijzigingen per bestand: backend Domain/Gebruikers/Gebruikers.cs (WijzigProfiel), Api/Endpoints.cs (PUT /api/profiel); frontend api.ts (wijzigProfiel), screens/MijnProfiel.tsx (ProfielKaart met bewerkmodus), i18n/nl.ts; docs functionele-beschrijving/aanmelden-registratie.md.
[2026-07-10d] Regio per land als profielveld en doelgroep-criterium (modelwijziging)
Section titled “[2026-07-10d] Regio per land als profielveld en doelgroep-criterium (modelwijziging)”Aanleiding: Testsessie Han (9-7-2026), issue #19: “iemand uit Oost-Nederland wil niet door iemand uit West-Nederland beoordeeld worden.” Aangescherpt (10-7-2026): regio’s zijn per land instelbaar in het beheerportaal — NL met provincies, andere landen voorlopig grover.
Genomen beslissingen:
Profiel/ProfielSnapshotenSelectieCriteriakrijgen een optioneelregio-veld (string-sleutel;null= niet opgegeven/geen filter). Alle snapshots zijn jsonb — bestaande data deserialiseert metregio = null, dus géén schema-migratie.- Regio-indeling is per land beheerbare stamdata: keuzelijst-type
regiomet samengestelde sleutel"<landcode>.<regiosleutel>"(zelfde patroon alsattribuut). Seed: Nederland → de 12 provincies; België/Duitsland/VK → Noord/Oost/Zuid/West als grover voorbeeld. Per land te verfijnen zonder codewijziging. - Matching: het regio-criterium filtert binnen het gekozen land en verbreedt niet mee naar de UN M49-verbreding (die blijft alleen voor land → subregio → werelddeel).
- UI: registratie/anonieme sessie tonen alleen de regio’s van het gekozen land (geen regio’s → geen veld); “Stel een vraag” krijgt een regio-doelgroepfilter dat pas kiesbaar is nadat een land is gekozen, met default uit het profiel van de steller (issue #18).
- Testvragen-generator: regio als criterium + maker-profiel binnen de criteria.
Wijzigingen per bestand: docs domeinmodel.md (ProfielSnapshot/SelectieCriteria/Profiel + regio), termen.md (nieuwe term Regio + SelectieCriteria), scenarios/matching-opiniegevers.md (stap 2), functionele-beschrijving/systeembeheer.md (keuzelijst-type regio); backend Domain (Profiel/ProfielSnapshot/SelectieCriteria), Application (ProfielInvoer/CriteriaInvoer, Matcht, generator), Api/Endpoints.cs (profiel-responses), DbSeeder.cs (regio-seed); shared types.ts; frontend Profiel.tsx (regio-veld), screens/StelVraag.tsx (regio-filter + default), i18n/nl.ts; beheer tabs/Keuzelijsten.tsx (regio-type + hint); e2e regio.spec.ts.
[2026-07-10c] Beheer: AI-persona’s bewerken + vangnet-guard afgedwongen
Section titled “[2026-07-10c] Beheer: AI-persona’s bewerken + vangnet-guard afgedwongen”Aanleiding: Testsessie Han (9-7-2026), issue #14: “AI-persona’s aan kunnen passen.” Het beheerportaal kon persona’s alleen toevoegen en (de)activeren.
Wijzigingen:
AIPersona.Wijzig(naam, profiel, instructie)in het domein + nieuw endpointPUT /api/beheer/personas/{id}. Bestaande AI-opinies wijzigen niet mee; alleen nieuwe opinies gebruiken de bijgewerkte persona.- Beheer-UI: klik op een persona opent een detailpaneel (deep-linkbaar,
#/personas/<id>) met naam, instructie en het demografische profiel (leeftijd/gender/land/achtergrond uit de keuzelijsten). - Vangnet-regel nu afgedwongen: de laatste actieve persona kan niet worden gedeactiveerd (die regel stond al in de docs maar werd niet gecontroleerd door het toggle-endpoint).
Wijzigingen per bestand: backend Domain/Beheer/Beheer.cs (Wijzig), Api/Endpoints.cs (PUT + toggle-guard); shared types.ts (Persona.profiel); beheer api.ts (wijzigPersona), tabs/Personas.tsx (detailpaneel), App.tsx (sub-route).
[2026-07-10b] Fix: maximaal één opinie per gebruiker per vraag
Section titled “[2026-07-10b] Fix: maximaal één opinie per gebruiker per vraag”Aanleiding: Testsessie Han (9-7-2026), issue #2: “Ik kan met gebruiker Sam 10× een opinie geven op dezelfde vraag. Mag maar 1× per gebruiker.” De keuzeset sloot alleen de eigen vraag, anti-thrash en dubbele slots binnen één set uit — niet vragen die de actor al beoordeelde.
Wijzigingen:
- Keuzeset-uitsluiting — de keuzeset bevat nooit meer een vraag waarop de actor al een opinie gaf óf waarvan hij op dit moment een slot vasthoudt (voorgesteld aan hem/door hem gekozen). Nieuwe repository-queries
VraagIdsMetOpinieVanAsyncenVraagIdsActiefBijAsync. - Vangnet in het opinie-endpoint — een tweede opinie van dezelfde persoon op dezelfde vraag wordt geweigerd (“Je hebt deze vraag al beoordeeld”), ook bij races of oude vastgehouden slots.
- Regel geëxpliciteerd in het scenario matching-opiniegevers (stap 4 + beslissing 1b); geen wijziging aan aggregates → domeinmodelversie ongewijzigd.
- Nieuwe e2e-spec
max-een-opinie.spec.ts(uitsluiting + vangnet; een ándere gebruiker krijgt de vraag wél).
Wijzigingen per bestand: backend Application/Abstractions/Repositories.cs, Application/Services.cs (VerdeelService + OpinieService), Infrastructure/Persistence/Repositories.cs; e2e frontend/e2e/max-een-opinie.spec.ts; docs scenarios/matching-opiniegevers.md.
[2026-07-10a] Testfeedback-bundel A: wachtwoord-oogje, uitleg-(i), beheerbare uitlegteksten, doelgroep-defaults, Expert-aanrader weg
Section titled “[2026-07-10a] Testfeedback-bundel A: wachtwoord-oogje, uitleg-(i), beheerbare uitlegteksten, doelgroep-defaults, Expert-aanrader weg”Aanleiding: Testsessie Han (9-7-2026), issues #4, #5, #6, #7, #10 en #18. Presentatie + stamdata; geen modelwijziging.
Wijzigingen:
- #4 Wachtwoord tonen — toon/verberg-oogje op het wachtwoordveld bij registratie én login (gebruikersportaal,
WachtwoordVeldinui.tsx) en op de beheer-login. Toegankelijk (aria-label/aria-pressed). - #5 Uitleg-(i) bij Achtergrond — klikbaar i-tje naast het profielveld Achtergrond (registratie, anonieme sessie en overal waar
ProfielVeldenwordt gebruikt);Fieldkreeg eeninfo-variant die de label-associatie behoudt (sr-only label in het<label>, i-knop erbuiten). - #6 + #7 Beheerbare uitlegteksten — nieuw keuzelijst-type
uitleg(beheerportaal → Keuzelijsten → Uitlegteksten) met vaste sleutelsdoelgroep,seintjeenachtergrond; geseed met de door Han aangedragen teksten; het gebruikersportaal leest ze met i18n-fallback. Formaat-hint in het beheerportaal. - #10 Expert niet als aanrader — de “Aanrader”-balk/chip bij Expert is verwijderd; alle adviesniveaus tonen gelijkwaardig (naam + prijs + stepper); Expert houdt alleen de amber niveau-meter.
- #18 Doelgroep-defaults uit profiel — gender/land/achtergrond in “Wie mag beoordelen” staan standaard op de profielwaarden van de steller (“Maakt niet uit” blijft kiesbaar); regio volgt zodra die bestaat (issue #19).
Wijzigingen per bestand: frontend ui.tsx (Field-info, WachtwoordVeld), Auth.tsx, Profiel.tsx (uitleg-lijst + i-tje), screens/StelVraag.tsx (defaults, beheerbare teksten, Niveaukaart zonder aanrader), i18n/nl.ts; beheer Login.tsx (oogje), tabs/Keuzelijsten.tsx (uitleg-type + hint); backend DbSeeder.cs (uitleg-seed); docs systeembeheer.md (uitleg-type).
[2026-07-09d] Fix testvragen-generator: maker-profiel respecteert nu de opgegeven criteria
Section titled “[2026-07-09d] Fix testvragen-generator: maker-profiel respecteert nu de opgegeven criteria”Aanleiding: Testfeedback — bij “Testvragen genereren” leken de opgegeven criteria genegeerd: met leeftijd 35–55 en achtergrond Nederlands ontstonden makers van 20/34/64 jaar en o.a. Turks. Bugfix in de test-tooling; geen modelwijziging.
Oorzaak: De criteria werden wél correct toegepast op de doelgroep (SelectieCriteria: wie mag beoordelen) van de gegenereerde vraag en de slots, maar het maker-profiel — dat in het overzicht/detail zichtbaar is — werd volledig willekeurig gevuld (leeftijd 18–70 + random gender/land/achtergrond), los van de criteria.
Fix: Het maker-profiel valt nu binnen de opgegeven criteria: leeftijd willekeurig in de range [min, max], en gender/land/achtergrond = de gekozen waarde (of willekeurig wanneer het veld op “Alle” staat). De doelgroep blijft dezelfde criteria, dus vraag en maker zijn nu consistent met wat je opgeeft.
Wijzigingen per bestand: backend Application/Services.cs (TestdataService.GenereerAsync — maker-profiel uit de criteria) + doc-comment, Application/Contracts.cs + Api/Endpoints.cs (comment bijgewerkt), beheerportaal beheer/src/tabs/Vragen.tsx (uitleg-tekst van de testzone).
[2026-07-09c] Beheer vragen: detailscherm met opname-inzage (testfunctie), opinies en de acties
Section titled “[2026-07-09c] Beheer vragen: detailscherm met opname-inzage (testfunctie), opinies en de acties”Aanleiding: Het vragenoverzicht in het beheerportaal toonde alleen een regel per vraag met inline-acties. Gevraagd is een volwaardig detailscherm per vraag met alle acties (verwijderen, AI-opinie toevoegen), inzage in de foto/video en de overige gegevens. Presentatie-iteratie op 2026-07-08e; geen wijziging aan het domeinmodel.
Privacy-afweging (docs-first): de docs beloven dat een beheerder géén toegang heeft tot opnames — een kern-anonimiteitsbelofte. In overleg is gekozen om opname-inzage toe te voegen als uitsluitend gemarkeerde testfunctie (amber zone), waarbij de productiebelofte overeind blijft. systeembeheer.md kreeg hiervoor een expliciete Test/QA-uitzondering.
Genomen beslissingen:
- Vraag-detailpaneel (klik op een rij; deep-linkbaar via
#/vragen/<id>, Escape/backdrop sluit): metadata (maker + profiel, doelgroep/selectiecriteria, te beoordelen secties/attributen, betaald-advies-eisen, toelichting, tijdstippen, status), álle ontvangen opinies (mét herkomst mens/AI, sectiegemiddelden + per-attribuut-scores, reactie, maker-waardering, adviesniveau/prijs), en de acties. - Acties in het detail: AI-opinie toevoegen (beheerfunctie; vult één open slot met een gekozen persona) en verwijderen (moderatie; twee-staps-bevestiging). Mutaties werken de datacache gericht bij (lijst-teller + detail), geen volledige herlaad.
- Opname-inzage als testfunctie: foto (
<img>) of video (<video controls>) via het bestaande publieke media-endpoint; de vraaglijst is nu een schone klikbare lijst (inline-acties verhuisd naar het detail). - Nieuw backend-endpoint
GET /api/beheer/vragen/{id}(volledige detail + opinies +mediaSleutel); de herkomst mens/AI blijft — zoals eerder — in beheer zichtbaar.
Wijzigingen per bestand: backend Api/Endpoints.cs (detail-endpoint), gedeeld shared/src/types.ts + index.ts (BeheerVraagDetail, BeheerOpinie), beheerportaal beheer/src/api.ts (vraag(id), mediaUrl), beheer/src/ui.tsx (ZijPaneel breed-variant), beheer/src/tabs/Vragen.tsx (herschreven met detailpaneel), beheer/src/App.tsx (sub-route naar Vragen), docs functionele-beschrijving/systeembeheer.md (presentatie-noot + Test/QA-uitzondering).
[2026-07-09b] Beheer gebruikers: compacte tabel zonder horizontale scroll + detailpaneel met de acties
Section titled “[2026-07-09b] Beheer gebruikers: compacte tabel zonder horizontale scroll + detailpaneel met de acties”Aanleiding: Het gebruikersoverzicht in het beheerportaal scrolde horizontaal (te veel kolommen, met de inline-acties als breedste), en de aanmaakdatum toonde alleen de datum — voor test/QA is ook het tijdstip relevant. Presentatie-iteratie op 2026-07-08e; geen modelwijziging.
Wijzigingen:
- Compacte tabel (Gebruiker · Profiel · Aangemaakt · Credits · Sterren · Adviesniveau): de kolommen Beoordelingen/Waardering en de inline-acties zijn eruit → geen horizontale scroll meer op desktopbreedtes.
- Aangemaakt toont datum mét tijd (gedeelde
datum()-helper, bv. “8 jul 2026, 23:05”). - Detailpaneel per gebruiker (klik op een rij; deep-linkbaar via
#/gebruikers/<naam>, Escape/backdrop sluit): kerncijfers (credits, sterren, beoordelingen, waardering), gegevens (adviesniveau, profiel, aangemaakt en laatste bezoek mét tijd) en de acties — credits toekennen (beheeractie) en sterren zetten (testfunctie, in de amber testzone). Mutaties werken de cache gericht bij, zodat tabel én paneel meebewegen zonder herladen. - Nieuw
ZijPaneel-bouwblok inbeheer/src/ui.tsx(drawer met dialog-semantiek en animatie dieprefers-reduced-motionrespecteert).
Wijzigingen per bestand: beheer/src/tabs/Gebruikers.tsx (herschreven), beheer/src/ui.tsx (ZijPaneel, IcChevron), beheer/src/index.css (paneel-animaties), beheer/src/App.tsx (sub-route naar Gebruikers), functionele-beschrijving/systeembeheer.md (presentatie-noot aangevuld).
[2026-07-09] Implementatie modelversie 2026-07-08f + frontend-upgrade: workspaces/shared, i18n-klaar, typed API, a11y/performance, CI
Section titled “[2026-07-09] Implementatie modelversie 2026-07-08f + frontend-upgrade: workspaces/shared, i18n-klaar, typed API, a11y/performance, CI”Aanleiding: De modelwijziging uit 2026-07-08f (secties/attributen als stamdata) moest worden geïmplementeerd, en de frontend kreeg het afgesproken verbeterpakket (tooling, ontdubbeling, UX/a11y/performance, opschoning) plus — op verzoek — meertalig-klaar worden. Geen modelwijziging; de modelversie blijft 2026-07-08f.
Wijzigingen:
- Secties/attributen uit stamdata (implementatie van
2026-07-08f): deDbSeederseedt de keuzelijst-typensectieenattribuut(sleutel"<sectie>.<attribuut>", apart geguard zodat bestaande databases ze ook krijgen); de testvragen-generator leest de secties/attributen uit de stamdata (nieuweIKeuzelijstRepository, hardcoded lijst alleen nog als fallback); het gebruikersportaal laadt ze viakeuzelijst('sectie')+keuzelijst('attribuut')(bouwSecties, fallback naar de hardcoded lijst) en gebruikt ze in Stel een vraag, Geef opinies en Mijn vragen; het beheerportaal toont het type Beoordelingsattribuut met een sleutelformaat-hint. Nieuwe e2e-specsecties-stamdata.spec.tsbewijst de doorwerking beheer → portaal. - npm-workspaces-monorepo +
@iseeyou/shared:app/heeft één root-package.json(workspacesshared/frontend/beheer, metlint/typecheck/test:unit/build-scripts). Het gedeelde package bevat de Toegangspoort (voorheen byte-identiek gedupliceerd), de api-basis (fetch-wrapper, tokenbeheer, uniform 401-gedrag metNietGeautoriseerd+ window-event), design-tokens (Tailwind v4@theme, incl. tokens voor de score-/tekstkleuren die eerst als losse hexen in componenten stonden), gedeelde API-response-types, helpers en de ErrorBoundary. Beide api-lagen zijn dunne, getypeerde endpoint-lagen (req<T>) — deuseState<any[]>-plekken zijn getypeerd. - i18n-klaar (gebruikersportaal): alle UI-teksten via i18next (catalogus
frontend/src/i18n/nl.ts; interpolatie, meervoud,<Trans>); nl is default, extra talen = catalogus + registratie; taalkeuze persistent; taalkiezer verschijnt automatisch bij >1 taal. Serverfoutteksten blijven Nederlands (follow-up). - UX/a11y/performance/opschoning: stille
.catch(() => {})-plekken vervangen door zichtbare meldingen (role="alert"); echtetablist/tab/aria-selectedop de portaal-navigatie, de auth-moduskiezer en de beheer-thema-/typekiezers; label op de Toegangspoort-invoer; ErrorBoundary om beide app-schillen; schermen + dummy-iDeal lazy (React.lazy/Suspense); font-imports beperkt tot latin-subsets; “Mijn vragen”-polling pauzeert bij een verborgen tabblad (Page Visibility). Componenten gesplitst:Auth,OpwaardeerModal,CameraOpname+useCameraOpname-hook,MijnVraagDetail+ pureaggregeer. Opgeschoond: vestigiale?actor=/Actor-parameters uit de frontend-api en ongebruikteActor-velden uit request-DTO’s (de actor komt overal uit het token, ADR-031), dodeProfiel/SelectieCriteria/GeoIP-restanten en ongebruikte UI-componenten, leegUnitTest1.cs, localStorage-sleutels gecentraliseerd (SLEUTELS). - Tooling & tests: ESLint (flat) + Prettier +
.editorconfig;build=tsc --noEmit && vite build;vite-env.d.ts; vitest-unittests vooraggregeer,scoreKleur/smileyVoor/niveauLabel,normaliseerProfiel/profielNaarInvoerenbouwSecties(16 tests). E2e-suite geconsolideerd: gedeeldee2e/helpers.ts(o.a. het hardcoded screenshot-pad vervangen doorSHOTS_DIR/default), docshots herhaalbaar gemaakt tegen een niet-verse database (registreer-of-login, relatieve saldo-asserties), selectors volgen de nieuwetab-rollen. 33 e2e-tests groen (was 31), plus 25 backend-domeintests. - CI:
.github/workflows/ci.ymlmet drie jobs — docs-build, app (lint/typecheck/unit/build via workspaces), backend (dotnet test). Geen deploy-stappen.
Wijzigingen per bestand (hoofdlijnen): backend DbSeeder.cs, Services.cs (TestdataService), Repositories.cs/Abstractions/Repositories.cs/DependencyInjection.cs (IKeuzelijstRepository), Endpoints.cs (DTO’s zonder Actor); app/package.json + eslint.config.js + .prettierrc.json + .editorconfig; nieuw app/shared/; frontend volledig langs de i18n-catalogus + splitsingen (Auth.tsx, OpwaardeerModal.tsx, CameraOpname.tsx, useCameraOpname.ts, MijnVraagDetail.tsx, aggregeer.ts, secties.ts, i18n/); beheer api.ts op de shared basis + Keuzelijsten.tsx (attribuut-type) + token-kleuren; e2e helpers.ts + alle specs + nieuw secties-stamdata.spec.ts; docs tech-stack.md en frontend.md (gebouwde monorepo-structuur + i18n-stand) en deze entry.
[2026-07-08f] Secties/attributen als beheerbare keuzelijsten (nieuw type attribuut) + docs↔code-audit: documentatie-correcties
Section titled “[2026-07-08f] Secties/attributen als beheerbare keuzelijsten (nieuw type attribuut) + docs↔code-audit: documentatie-correcties”Aanleiding: Een docs↔code-audit legde bloot dat de documentatie op een reeks punten achterliep op de gebouwde implementatie (stale caveats over “0–6”-scores en “nog niet gebouwd” betaald advies, kapotte links naar nooit-aangemaakte pagina’s, een verouderde push-beschrijving van de matching, en afwijkende veldnamen). Daarnaast bleek een modelgat: het domeinmodel noemt secties én attributen “beheerbare stamdata”, maar alleen de secties bestaan als keuzelijst-type — de attributen stonden hardcoded in de frontend (app/frontend/src/data.ts, const SECTIES).
Genomen beslissingen:
- Modelwijziging — attributen als keuzelijst (docs-first; implementatie volgt). Naast het bestaande keuzelijst-type
sectiekomt er een typeattribuut, met een samengestelde sleutel"<sectie>.<attribuut>"(bijv.gezicht.ogen,kleding.pasvorm,accessoires.bril). Zo passen de attributen in de bestaande platteKeuzelijst-structuur (taal/type/sleutel/omschrijving/volgorde): geen schema-migratie, alleen seed + frontend. De seed-inhoud volgt de huidige hardcoded secties/attributen uitdata.ts(Gezicht: ogen, mond, haar, baard, wenkbrauwen, make-up/stijl, algeheel; Kleding: hoofd, nek, bovenstuk, taille, onderstuk, combinatie, kleuren, pasvorm, kwaliteit, algeheel; Accessoires: bril, gehoorapparaat, oorbellen, nek, pols, riem, piercing, tattoo, schoenen, algeheel). - Docs volgen de code-naam
Vraag.aantalOpiniegevers(correctie): het bevroren aantal beoordelingen op de vraag heet in de codeAantalOpiniegevers(zelfde naam als de instelling), nietaantalBeoordelingen;Gebruiker.aantalBeoordelingen(reviewer-statistiek) blijft ongewijzigd bestaan. - Stale caveats opgeruimd (correctie): de verplichte 1–6-scoreschaal en betaald advies/euro-saldo/uitbetaling (met gesimuleerde dummy-iDeal) zijn gebouwd — alle “de code hanteert nog 0–6”- en “nog niet gebouwd”-caveats daarover zijn verwijderd of herschreven (o.a. ADR-027/ADR-030-notities, Beloning & Financieel-endpoints in Backend & API).
- Eerlijk over wat níét gebouwd is (correctie):
Berichtis gemodelleerd maar heeft geen endpoints of schermen (note op de Berichten-pagina), en de frontend-SignalR-client bestaat nog niet — de portalen pollen; server-side bestaat éénBeoordelingHubmet precies de eventsOpdrachtBeschikbaarenVraagBeoordeeld(Backend & API en tech-stack zijn hierop gelijkgetrokken). EenAanwezigheidHubis doelbeeld. - Matching-beschrijving gecorrigeerd (correctie):
functionele-beschrijving/opiniegevers-selecteren.mdbeschreef nog het oude push-model met de niet-bestaande statussenPending/Completed/Verlopen; herschreven naar het pull/Keuzeset-model metOpdrachtStatus { Open, Voorgesteld, Gekozen, Voltooid, AIVoltooid }. Ookbron = AIPersona→bron = AIen de laatste “status Verlopen”-restanten gefixt; het AI-vangnet in het matching-scenario levert (conform model) scores + reactie. - Backend & API geactualiseerd (correctie): auth conform ADR-031 (admin-login
POST /api/beheer/login, actor-tokens,Authorization-bearer), de beheer-tooling-endpoints (POST /api/beheer/gebruikers/sterren,POST /api/beheer/gebruikers/opschonen, credits/expert/testvragen), de werkelijke keuzelijst-/instellingen-routes en de nieuwe instellingensterrenPerNiveau,beoordelaarAandeelProcent,uitbetaalDrempelEuro. - Navigatie/config (correctie): kapotte links naar
scenarios/reserveren-en-kiezen,scenarios/vraag-melden,architectuur/auth,architectuur/persistentie,architectuur/realtimeenarchitectuur/testsherwezen naar bestaande pagina’s; het scenario Credits verdienen en besteden aan de sidebar toegevoegd; sidebar-label ”⭐ Voorstel ter verificatie” → “Voorstel (geverifieerd)”; de ongebruikteen-locale verwijderd; de beslissingen-tabel in het domeinmodel op volgorde gezet.
Alleen de secties/attributen-wijziging is een echte modelwijziging (vandaar de bump naar 2026-07-08f); alle overige punten zijn correcties die de documentatie gelijktrekken met de al gebouwde code. Verder vermelden README/deployment nu de CI-workflow (.github/workflows/ci.yml: docs-build; app-lint/typecheck/unit/build via npm workspaces; dotnet test) en de werkelijke Azure-uitrol (App Service + Static Web Apps, dummy-iDeal).
Wijzigingen per bestand: domeinmodel.md (Keuzelijst: sleutel/volgorde in het klassendiagram, typen incl. attribuut + seed-inhoud; Vraag.aantalOpiniegevers in diagram en prose; stale 0–6/AIPersona-restanten weg; beslissingen-tabel geordend en 29–33 aangehecht; beslissing 33 als gebouwd), termen.md (Keuzelijst + Sectie met attribuut-type; Score/AI-persona zonder 0–6-caveat; aantalOpiniegevers-naamgeving; links naar matching-scenario), functionele-beschrijving/systeembeheer.md (keuzelijst-typen + sleutel/volgorde-velden + seed-attributen; 1–6 gebouwd; link-fix), functionele-beschrijving/opiniegevers-selecteren.md (herschreven naar pull/Keuzeset), functionele-beschrijving/berichten.md (note “gemodelleerd, nog niet gebouwd”), functionele-beschrijving/credits.md + opinie-geven.md + vraag-stellen.md (gebouwd-status, 1–6, tips-vensters, bron = AI, geen “Verlopen”), functionele-beschrijving/moderatie.md (links naar melden-en-verwijderen), scenarios/matching-opiniegevers.md (AI-reactie consistent) + scenarios/opinie-geven-en-waardering.md (link + 1–6), architectuur/backend-en-api.md (ADR-031-auth, beheer-tooling-endpoints, Beloning & Financieel → gebouwd/dummy-iDeal, SignalR-events eerlijk, keuzelijst-/instellingen-routes), architectuur/tech-stack.md (links, SignalR, PSP-rij, 1–6), architectuur/index.md + architectuur/frontend.md + architectuur/beveiliging-en-privacy.md (gebouwd-status, links), architectuur/deployment.md (links, huidige-situatie-note, CI-workflow), architectuur/beslissingen.md (ADR-027/030-notities, ADR-017-tekst, score-invariant 1..6, anchor-fix), astro.config.mjs (sidebar + locale), root-README.md (CI + werkelijke deploy) en changes.md (deze entry + versieregel + verduidelijking versiebeleid).
[2026-07-08e] Beheerportaal-herontwerp: SPA-schil met zijbalk, overzichtsdashboard en uniforme testfunctie-markering
Section titled “[2026-07-08e] Beheerportaal-herontwerp: SPA-schil met zijbalk, overzichtsdashboard en uniforme testfunctie-markering”Aanleiding: Het beheerportaal oogde ad hoc: vijf losse tabs zonder URL, bij elke klik werd álles opnieuw geladen (zichtbare “Laden…”-flitsen), statusmeldingen stonden als losse tekstregels verspreid over de schermen, en test-tooling (testvragen genereren, sterren zetten, opschonen) was visueel niet te onderscheiden van echte beheerfuncties.
Genomen beslissingen (presentatie/architectuur van het beheerportaal; géén modelwijziging — de domeinmodelversie blijft 2026-07-08b):
- SPA-schil met zijbalk: gegroepeerde navigatie (Platform / Inhoud / Mensen) met iconen; hash-routing maakt elke pagina deep-linkbaar (
#/gebruikers,#/keuzelijsten/achtergrond,#/instellingen/<thema>) — refresh en back/forward werken; zelfde router-loze aanpak als het gebruikersportaal. - Overzicht-dashboard als startpagina: stat-tegels (open vragen incl. wachtend-op-opinies en meldingen, gebruikers, actieve AI-persona’s, keuzelijst-opties) plus “nieuwste vragen/gebruikers” — samengesteld uit bestaande beheer-endpoints, geen nieuwe API.
- Stale-while-revalidate datacache (
store.ts): terugkeren naar een pagina toont direct de gecachte data en ververst stil op de achtergrond; mutaties werken de cache gericht bij (credits/sterren per rij, vraag verwijderen, persona-toggle optimistisch) i.p.v. alles opnieuw op te halen. - Toasts (
toast.tsx) vervangen de losse statusregels; laad-skeletten vervangen de “Laden…”-teksten; instellingen tonen niet-opgeslagen wijzigingen (teller + herstel-knop + amber markering per veld en per thema). - Uniforme testfunctie-markering: test-/QA-tooling staat in een gestreepte amber Testfunctie-zone met erlenmeyer-label (testvragen genereren, sterren direct zetten, oude gebruikers opschonen) + een legenda in de zijbalk. De handmatige AI-opinie blijft ongemarkeerd — dat is een volwaardige beheerfunctie (zie systeembeheer).
Wijzigingen per bestand: beheerportaal beheer/src/ (nieuw: router.ts, store.ts, toast.tsx, ui.tsx, tabs/Overzicht.tsx; herschreven: App.tsx, Login.tsx, index.css en alle tabs), e2e-specs frontend/e2e/{credits,docshots,sessie-volledig}.spec.ts (expliciete klik naar Instellingen — het portaal opent nu op het Overzicht), functionele-beschrijving/systeembeheer.md (presentatie-noot: dashboard + testfunctie-markering).
[2026-07-08d] Beheer-tooling: aanmaakdatum, sterren instelbaar, opschonen op aanmaakdatum
Section titled “[2026-07-08d] Beheer-tooling: aanmaakdatum, sterren instelbaar, opschonen op aanmaakdatum”Aanleiding: Bij het testen bleek het beheer-gebruikersscherm onhandig: de “oude gebruikers opschonen”-knop verwijderde niets (hij filterde op laatste bezoek én klemde de drempel op minimaal 1 dag, terwijl alle testaccounts recent zijn), er was geen zicht op wanneer een account is aangemaakt, en er was geen manier om voor een test snel een adviesniveau te forceren.
Genomen beslissingen:
Gebruiker.aangemaaktOp(nieuw veld, gezet bij registratie) — zichtbaar in het beheer-gebruikersoverzicht. EF-migratieGebruikerAangemaaktOp(bestaande rijen krijgen de migratietijd als default).- Opschonen op aanmaakdatum:
POST /api/beheer/gebruikers/opschonenfiltert nu opaangemaaktOpi.p.v. laatste bezoek en staat 0 dagen = alles toe (drempel niet meer op minimaal 1 geklemd). Verwijdert per gebruiker ook diens eigen vragen, sessies en sterren. - Sterren direct instelbaar (beheer/test):
Gebruiker.zetSterren(n)+POST /api/beheer/gebruikers/sterren; het adviesniveau volgt automatisch uit de sterren-drempel (sterrenPerNiveau). Bedoeld om adviesniveaus te testen zonder de hele ranking te doorlopen. - Beheer-gebruikersscherm: zoeken (naam/e-mail), filter op adviesniveau, paging (25/pagina), aanmaakdatum-kolom en een per-rij sterren-setter.
Implementatie-/beheer-tooling; de domeinmodelversie blijft 2026-07-08b. Wijzigingen per bestand: domeinmodel.md (Gebruiker.aangemaaktOp), functionele-beschrijving/systeembeheer.md (beheer-gebruikersfuncties: zoeken/filter/paging, aanmaakdatum, sterren zetten, opschonen), backend Domain/Gebruikers/Gebruikers.cs (AangemaaktOp, ZetSterren), migratie …_GebruikerAangemaaktOp.cs, Api/Endpoints.cs (opschonen op aanmaakdatum + 0=alles, sterren-endpoint, aanmaakdatum in de lijst), Infrastructure/Persistence/Repositories.cs (opinie-verwijdering via ExecuteDeleteAsync — geen crash meer op legacy score-0-data), beheerportaal beheer/src/api.ts + beheer/src/tabs/Gebruikers.tsx.
[2026-07-08b] Verificatie Han verwerkt: sterrendrempels per niveau, bonus €5, verdeelsleutel 50/50, uitbetaaldrempel €10, per-niveau beoordelaaraantallen, max. 1 teaser
Section titled “[2026-07-08b] Verificatie Han verwerkt: sterrendrempels per niveau, bonus €5, verdeelsleutel 50/50, uitbetaaldrempel €10, per-niveau beoordelaaraantallen, max. 1 teaser”Aanleiding: Han heeft het voorstel 2026-07-05 beoordeeld (transcript “Notities beoordelings- en beloningssysteem”, 8-7-2026). Zijn antwoorden bevestigen het merendeel van de open vragen (formule 50/25/25, neutrale waarden, min. 3 deelnemers, doorlopende tellers 5×2e/10×3e, mijlpaal elke 200, sterren vervallen nooit, 10 beoordelaars, 5 credits/beoordeling, 10 credits/vraag, AI-vangnet, geen wisselkoers) en wijzigen een aantal parameters die inmiddels in docs én code stonden. Dit is een modelwijziging: de openstaande interpretaties zijn beslecht en de bijbehorende beheerparameters zijn vastgesteld. De implementatie in app/ volgt het model (geïmplementeerd op 2026-07-08): backend, beide portalen en een EF-migratie (Vraag.Niveaueisen als jsonb) zijn aangepast; de iDeal-betalingen blijven gesimuleerd (dummy PSP).
Genomen beslissingen:
- A · Sterrendrempels per adviesniveau. Het niveau volgt uit de sterren-reputatie via vaste, instelbare drempels: nieuwe instelling
sterrenPerNiveau(standaard 30) → 30★ → niveau 1, 60★ → 2, 90★ → 3, 120★ → 4. ⚠️ “Expert = 5” gelezen als Expert = niveau 5 = 150★ (interpretatie, ter finale check), automatisch via reputatie én handmatig/op aanvraag toekenbaar (ExpertHandmatigblijft als override). (Was: alleen kwalitatief “meer sterren = hoger niveau” in de docs.) - B · Ster zonder directe geldwaarde. Een ster heeft geen directe euro-waarde; hij is een kwaliteits-/schaalaanduiding en uitsluitend de sleutel tot een hoger adviesniveau. De uitbetaling per beoordeling is 1-op-1 gekoppeld aan het behaalde niveau (hoger niveau = hogere uitbetaling). De open vraag over een directe ster-bonus in euro’s vervalt; sterren vervallen nooit. (Was: “echt geld waard” met open vraag §5.)
- C · Welkomstbonus max €5. Correctie van Han: de bonus bedraagt maximaal €5 (niet €10), zodat het totaal maximaal €10 is — €5 van de gebruiker + €5 van I See You.
welkomstbonusMaxEurostandaard 10 → 5. - D · Verdeelsleutel 50/50. De verdeling beoordelaar/platform is 50/50 (
beoordelaarAandeelProcentstandaard 50). Met die verdeling kost het als bonus verdubbelen van de inleg het platform per saldo niets. (Was: open beheerinstelling zonder waarde.) - E · Uitbetaling via het profielscherm, drempel €10. Een uitbetaling wordt aangevraagd vanaf het profielscherm en is alleen mogelijk bij een saldo ≥ €10 (nieuwe instelling
uitbetaalDrempelEuro, standaard 10). De frequentie blijft ongeregeld. (Was: drempel en frequentie open.) - F · Per-niveau beoordelaaraantallen bij de vraag. De vraagsteller kiest één of meerdere niveaus en geeft per niveau aan hoeveel van de tien beoordelaars daaraan moeten voldoen — op de
Vraagvastgelegd alsniveaueisen(een verzamelingNiveauEis={ niveau, aantal }); de veldengewenstAdviesniveau+aantalBetaaldeBeoordelingenvervallen. Totaalprijs = som van (aantal × niveauprijs), bevroren. ⚠️ Som van de aantallen ≤ 10; de resterende slots zijn gratis/AI (interpretatie). (Was: één gewenst niveau + één totaalaantal.) - G · Teaser: max. 1 koopbaar. De vraagsteller koopt hoogstens één teaser; een niveau-beoordelaar krijgt alleen betaald wanneer zijn teaser wordt gekocht (bevestiging van “betaling volgt de ontgrendeling”). (Was: ontgrendelen per beoordeling zonder bovengrens.)
- H · Bevestigd zonder wijziging. De 8 §1d-vragen zijn met ja beantwoord (met twee verduidelijkingen): de nummer 1 krijgt altijd direct een ster (géén 0,7-drempel), en de ranglijst wordt definitief aan het einde van het retentievenster zodat alles wat de ranking beïnvloedt meeweegt. Het voorstel-document is bijgewerkt: §1d/§9 afgevinkt, per-sectie-antwoorden verwerkt, status “geverifieerd + verwerkt”.
Nieuwe modelparameters: sterrenPerNiveau (30), uitbetaalDrempelEuro (10), beoordelaarAandeelProcent (50); gewijzigd: welkomstbonusMaxEuro (10 → 5). Geen nieuwe ADR — dit vult de open parameters van ADR-027 t/m ADR-030 in; die staan nu op Vastgesteld (2026-07-08b).
Wijzigingen per bestand: voorstel-2026-07-05.md (statusblok → “geverifieerd + verwerkt”, nieuwe sectie “Beslissingen van Han (2026-07-08)” met was→wordt-tabel, antwoorden bij §1d, ✅-beslist-blokken bij §2–§5, §9-checklist afgevinkt, §10-statustabel/afsluiting bijgewerkt), domeinmodel.md (context-caution → “vastgesteld”; Vraag → niveaueisen + nieuw VO NiveauEis in het klassendiagram; ster-sectie zonder directe geldwaarde; adviesniveau-drempels 30/60/90/120★ + ⚠️ Expert 150★; teaser max 1 + verdeelsleutel 50/50; welkomstbonus €5; uitbetaling €10 via profielscherm; Systeeminstellingen + sterrenPerNiveau/beoordelaarAandeelProcent/uitbetaalDrempelEuro en welkomstbonusMaxEuro = 5), termen.md (Adviesniveau, Ster, Teaser, Welkomstbonus, Euro-saldo, Uitbetaling, Systeeminstellingen), functionele-beschrijving/betaald-advies.md (drempels, per-niveau eisen, verdeelsleutel 50/50, max 1 teaser, bonus €5, uitbetaling; note-block + eisen-tabel), functionele-beschrijving/systeembeheer.md (instellingstabel: sterrenPerNiveau/beoordelaarAandeelProcent/uitbetaalDrempelEuro, bonus €5, verdeelsleutel niet langer “open”), functionele-beschrijving/vraag-stellen.md (per-niveau eisen + eisen-tabel), functionele-beschrijving/aanmelden-registratie.md (welkomstbonus €5), scenarios/betaald-advies-en-teaser.md (max 1 teaser, verdeelsleutel 50/50, bonus €5, flow-diagram) en scenarios/ster-verdienen.md (drempels, ster zonder geldwaarde), architectuur/beslissingen.md (ADR-027 t/m ADR-030 → Vastgesteld 2026-07-08b; ADR-028/029 parameters ingevuld), en changes.md (deze entry + versieregel). Implementatie (app/, geïmplementeerd 2026-07-08): backend Domain/Beheer/Beheer.cs (SterrenPerNiveau=30, UitbetaalDrempelEuro=10, WelkomstbonusMaxEuro=5, BeoordelaarAandeelProcent=50), Domain/Gebruikers/Gebruikers.cs (AdviesniveauBij(sterrenPerNiveau)), Domain/Beoordeling/Aggregates.cs (NiveauEis + Vraag.Niveaueisen i.p.v. GewenstAdviesniveau/AantalBetaaldeBeoordelingen, som≤aantal-bewaking), Application/Contracts.cs + Services.cs (prijs = som over eisen; teaser max 1; uitbetaaldrempel), Infrastructure/Persistence/AppDbContext.cs (Niveaueisen als jsonb) + migratie NiveaueisenEnVastgesteldeParameters, Api/Endpoints.cs (adviesniveau instelbaar in profiel/beheer); gebruikersportaal screens/StelVraag.tsx (per-niveau aantallen) + screens/MijnProfiel.tsx (euro-saldo + uitbetaling aanvragen); beheerportaal tabs/Instellingen.tsx (nieuwe velden). De defaults gelden voor nieuwe installaties; bestaande databases behouden hun (admin-instelbare) opgeslagen waarden.
[2026-07-08] Autorisatie afgedwongen: admin-guard + ondertekende bearer-tokens (ADR-031)
Section titled “[2026-07-08] Autorisatie afgedwongen: admin-guard + ondertekende bearer-tokens (ADR-031)”Aanleiding: Een review (2026-07-08) van de implementatie legde een kritiek beveiligingsgat bloot: de beheer-endpoints (/api/beheer/*) waren volledig onbeschermd — zonder enige authenticatie waren credits toe te kennen, Expert-status te zetten, de volledige gebruikerslijst inclusief e-mailadressen op te vragen, systeeminstellingen te wijzigen en vragen te verwijderen. Daarnaast vertrouwden gebruikers-endpoints een als tekst meegegeven actor, waardoor andermans profiel/saldo opvraagbaar was. Dit is geen modelwijziging: het autorisatiemodel (IsBeheerder) stond al in de docs; de code liep erop achter.
Genomen beslissingen:
- Beheer-autorisatie afgedwongen. Nieuw endpoint
POST /api/beheer/loginverifieert de admin-credentials uit hetSysteem-aggregate (constant-time, PBKDF2) en geeft een ondertekend admin-token. Alle wijzigende/gevoelige/api/beheer/*-endpoints zitten achter een admin-guard (endpoint-filter) → zonder geldig token401. Publiek blijven: de login zelf en de read-onlyGET /api/beheer/instellingen(het gebruikersportaal heeft de niet-gevoelige prijzen/aantallen nodig). - Bearer-tokens i.p.v. cookies (cross-origin). De portalen (Static Web Apps) draaien cross-site t.o.v. de API (App Service), waar
SameSite-cookies niet meegestuurd worden. De sessie gaat daarom als HMAC-SHA256-ondertekend token in deAuthorization-header (type-tag admin vs. actor, vervaltijd, servergeheimAuth:Secret). Nieuwe applicatie-poortITokenDienst, infra-implementatieHmacTokenDienst. - Actor-token. Login/anonieme sessie geven een actor-token dat de actor-referentie draagt; alle gebruikers-endpoints (profiel, saldo, mijn vragen, vraag stellen/ontgrendelen/verwijderen/melden, keuzeset/kiezen/opinie, waardering, betalingen) zitten achter een actor-guard en leiden de actor server-side uit het token af i.p.v. een meegegeven
actorte vertrouwen. Daarmee is het/api/profiel-datalek (andermans profiel/saldo opvraagbaar met alleen de gebruikersnaam) gedicht en is impersonatie uitgesloten. Het gebruikersportaal bewaart en stuurt het token mee; bij een 401 valt het terug op aanmelden. - Beheerportaal-login. Het beheerportaal heeft nu een loginscherm (het leunde eerder enkel op de gedeelde toegangscode-gate) dat het admin-token bewaart en meestuurt; bij een
401valt de UI terug op login. - Nieuwe ADR-031. Legt de bearer-tokenkeuze vast als pragmatische, cross-origin-bestendige invulling van ADR-007/ADR-011; ASP.NET Core Identity + Argon2id + cookies blijven het bredere doelbeeld.
Dit is een implementatie-/beveiligingsalignering, geen domeinmodel-wijziging — de modelversie blijft 2026-07-05.
Wijzigingen per bestand: architectuur/beslissingen.md (ADR-031 + overzichtsrij), architectuur/beveiliging-en-privacy.md (notitie “huidige implementatie” bij Authenticatie), backend: Application/Abstractions/Ports.cs (ITokenDienst), Application/AuthServices.cs (AdminAuthService, nieuw), Infrastructure/Services/HmacTokenDienst.cs (nieuw), Infrastructure/DependencyInjection.cs, Api/Program.cs (Auth:Secret), Api/Endpoints.cs (beschermde beheer-groep + login), Api/AdminAuthFilter.cs (nieuw); beheerportaal: beheer/src/api.ts (token opslaan/meesturen), beheer/src/App.tsx (loginscherm + uitloggen); actor-token (A2): Api/Endpoints.cs (beschermde beveiligd-groep, actor uit token via http.Actor(), token-uitgifte bij login/anoniem), Api/AdminAuthFilter.cs (VereisActorFilter + http.Actor()), gebruikersportaal frontend/src/api.ts (token opslaan/meesturen + 401-afhandeling) en frontend/src/App.tsx (uitloggen wist token; 401 → aanmelden); e2e-suite: gedeelde e2e/_setup-admin-auth.ts hangt automatisch admin-/actor-tokens aan de testrequests.
[2026-07-05] Sterren als verdiende reputatie; betaald advies met adviesniveaus en teaser-flow; euro-saldo met iDeal en welkomstbonus; uitbetaling terug; onboarding — ter verificatie
Section titled “[2026-07-05] Sterren als verdiende reputatie; betaald advies met adviesniveaus en teaser-flow; euro-saldo met iDeal en welkomstbonus; uitbetaling terug; onboarding — ter verificatie”Aanleiding: De handgeschreven notities van Han (3-7-2026) — “Wanneer krijg je een ster” plus de welkomsttekst van blad 2. Die notities zijn de kern van waarheid en overrulen eerdere beslissingen (o.a. het “geen geld”-besluit van 2026-07-03/ADR-019 en de default aantalOpiniegevers = 1 van 2026-07-04h). Het volledige, gezaghebbende verificatiedocument — inclusief de gemarkeerde interpretaties (⚠️) en open vragen (❓) — is voorstel-2026-07-05. Status: de documentatie is volledig bijgewerkt naar deze versie. (Bijgewerkt 2026-07-08: de implementatie in app/ is inmiddels wél naar 2026-07-05 gebouwd — verplicht 1–6 scoren, onboarding, sterren + consensus-ranking, euro-saldo/betaald advies/teaser met gesimuleerde (dummy) iDeal — consistent met de versieregel bovenaan en voorstel §10. De met ⚠️/❓ gemarkeerde interpretaties en open vragen blijven ter verificatie door Han; een echte PSP en de definitieve uitbetaling-/ster→geld-mechaniek volgen nog. De oorspronkelijke entrytekst hieronder — “nog niet gebouwd / code op 2026-07-04h” — dateert van vóór die bouwronde en is dus achterhaald.)
Genomen beslissingen:
- Sterren keren terug als verdiende kwaliteitsreputatie (géén loterij). Geherintroduceerde bounded context Beloning & Financieel met de
Steren vier verdien-routes: (a) beste opiniegever per vraag via de nieuwe consensus-ranking (consensus + correlatie-controle; de bestaande 0–5-waardering van de maker blijft bestaan en weegt mee met het instelbare gewichtgewichtMakerWaardering; pas ranken vanaf ≥3 beoordelaars,minBeoordelaarsVoorRanking— interpretatie); (b) 5× als 2e geëindigd; (c) 10× als 3e geëindigd; (d) elke 200 verdiende credits. Drempels en gewichten zijn beheerinstellingen; sterren zijn blijvend opgeslagen op het account (Gebruiker.aantalSterren). AI-opinies dingen niet mee. - Betaald advies met adviesniveaus. Opiniegevers kunnen een
Adviesniveauhebben (1–4 + Expert; prijzen per beoordeling €0,20/0,40/0,60/0,80/1,00 — beheerinstellingniveauPrijzen; niveau ontsloten door de sterren-reputatie — voorstel; Expert handmatig/op aanvraag). Vanaf niveau 1 is het tekstvak in het opiniescherm vrijgegeven én verplicht. De vraagsteller stelt bij zijn Vraag het gewenste niveau + aantal betaalde beoordelingen in (interpretatie); totaalkosten vooraf getoond (bv. €1,20) en bevroren (gewenstAdviesniveau,aantalBetaaldeBeoordelingen,adviesTotaalprijs). De gratis route (smiley-scores + door de maker gevraagde gratis reactie) blijft onveranderd ernaast bestaan. Verdeelsleutel beoordelaar/platform = beheerinstelling (open). - Teaser-flow. Zonder account/saldo/niveau-instelling zorgt het systeem tóch voor minimaal 2 opiniegevers met niveau > 0 (
minTeaserNiveauBeoordelaars, best effort); hun tekst verschijnt geblurd/onleesbaar in de uitslag mét niveau + prijs. Ter plekke betalen ontgrendelt per beoordeling (Opinie.ontgrendeld, met snapshotsadviesniveauSnapshot/prijsSnapshot) → creatie van het simpelste (anonieme) account + iDeal-betaling. Betaling aan de beoordelaar volgt de ontgrendeling (voorstel). De geblurde tekst is al gemodereerd (vóór opslag). - Euro-saldo + iDeal + welkomstbonus. Naast credits komt een euro-saldo (“inleg”,
Gebruiker.euroSaldo), opladen via iDeal (Betalingvia een PSP, webhook-bevestigd, idempotent). Eenmalige welkomstbonus bij accountcreatie: eerste inleg verdubbeld tot max. €10 bonus (welkomstbonusMaxEuro; interpretatie). Credits en euro’s strikt gescheiden — geen omwisselkoers. ADR-019 (“geen geld”) wordt hiermee overruled. - Uitbetaling keert terug; anonimiteit geherformuleerd. Sterren worden opgeslagen en zijn “echt geld waard” — mechaniek open (voorstel: sterren ontsluiten vooral het adviesniveau; zie voorstel §5). De anonimiteitsbelofte luidt voortaan: anoniem richting andere gebruikers; betaalgegevens bestaan bij de payment provider; het platform bewaart alleen de minimaal noodzakelijke transactiedata (referentie + saldo). KYC-noot bij uitbetalen.
- Onboarding. Welkomsttekst (blad 2, redactioneel bijgeschaafd; beheerbare content) bij het eerste bezoek; de eerste vraag van een nieuwe gebruiker gaat pas de verdeling in nadat hij 2 vragen van anderen beantwoordde (
onboardingAantalBeoordelingen; 2 × 5 = 10 credits = de inzage-prijs bij 10) — mét cold-start-vangrail (geen beoordeelbaar werk beschikbaar → poort vervalt tijdelijk; interpretatie). Tips-vensters: klikken op de tekst van een deelvraag in het opiniescherm opent een venster met tips/uitleg (beheerbare content per deelvraag en taal). - Default
aantalOpiniegeversterug naar 10 (was 1 sinds 3-7-2026); de tarieven blijven verdienen-5/inzage-10 (met de defaults kost een uitslag inzien weer 10 credits; twee beoordelingen betalen één inzage). - Scoren is verplicht — “geen mening” vervalt (aanscherping 3-7-2026). Beslissing van de product owner (Clermond, 3-7-2026), toegevoegd als aanscherping binnen deze modelversie. De vraagsteller bepaalt via de Beoordelingsselectie waarop geantwoord moet worden; de opiniegever moet op élk gevraagd attribuut een mening geven. De scoreschaal wordt daarom 1–6, verplicht per gevraagd attribuut (1 = slecht/kan beter, 6 = uitstekend); score 0 = “geen mening” vervalt. Alle afgeleide “alleen scores > 0”-regels vervallen mee: SectieGemiddelden en per-attribuut-gemiddelden gaan over álle scores, en de consensus-ranking kent geen uitzonderingen meer voor niet-gescoorde attributen. Ook een AI-opinie volgt de verplichting (elk gevraagd attribuut 1–6; de deterministische terugval sowieso). UI: de score-invoer heeft geen “geen mening”-optie meer en het opinieformulier kan pas verstuurd worden als alle gevraagde attributen gescoord zijn. Nieuwe ontwerpbeslissing 33; ontwerpbeslissing 5 vervalt. (Inmiddels gebouwd per
2026-07-08: de code hanteert de verplichte 1–6-schaal; de eerdere “0–6 met 0 = geen mening”-notitie is achterhaald.) - Nieuwe ADR-027 t/m ADR-030 (sterren + consensus-ranking; betaald advies + teaser; euro-saldo/iDeal/welkomstbonus — overrulet ADR-019; onboarding-poort met vangrail + default 10) en ontwerpbeslissingen 29–32; ontwerpbeslissing 21 en ADR-019 expliciet deels overruled per
2026-07-05, ontwerpbeslissing 17 genuanceerd, ontwerpbeslissing 20 (scheiding valuta) herleefd voor credits ↔ euro’s, en het default-deel van ADR-026/ontwerpbeslissing 28 teruggedraaid.
Wijzigingen per bestand: voorstel-2026-07-05.md (het gezaghebbende verificatiedocument — reeds aanwezig, ongewijzigd), nieuw: functionele-beschrijving/betaald-advies.md (adviesniveaus + prijzen, verplichte tekst vanaf niveau 1, gewenst niveau bij de vraag, teaser/blur-flow, euro-saldo + iDeal + welkomstbonus, betaling-bij-ontgrendeling, verhouding tot de gratis route), nieuw: functionele-beschrijving/onboarding.md (welkomsttekst, eerst-2-vragen-poort mét vangrail, tips-vensters, account/anonimiteit), nieuw: scenarios/ster-verdienen.md (end-to-end: consensus/correlatie → beste → ster; 5×2e/10×3e/200-credits-routes; sterren → niveau), nieuw: scenarios/betaald-advies-en-teaser.md (geblurde teasers → iDeal-betaling met on-the-fly account → ontgrendeling per beoordeling → betaling beoordelaar), astro.config.mjs (sidebar-entries voor de vier nieuwe pagina’s), domeinmodel.md (context Beloning & Financieel geherintroduceerd met klassendiagram — Ster/SterBron, Adviesniveau, ConsensusRanking, Betaling/typen/statussen, Uitbetaling; context-overzichtstabel + context-map bijgewerkt; Gebruiker + euroSaldo/welkomstbonusGebruikt/aantalSterren/adviesniveau; Vraag + gewenstAdviesniveau/aantalBetaaldeBeoordelingen/adviesTotaalprijs; Opinie + adviesniveauSnapshot/prijsSnapshot/ontgrendeld; waardering-sectie + ranking-functie; Systeeminstellingen + negen nieuwe instellingen en default 10; ontwerpbeslissingen 17/20/21/28 bijgewerkt/gemarkeerd, 29–32 toegevoegd), termen.md (nieuw: Ster, Adviesniveau, Euro-saldo (Inleg), Welkomstbonus, Teaser (geblurde reactie), iDeal / Betaling, Uitbetaling (terug, was verwijderd-stub), Onboarding, Tips-venster, Consensus-ranking; bijgewerkt: Anonimiteit, Bounded context, Credits, Creditkosten-omgeving, Gebruiker, Opinie, Reactie, Resultaat, Systeeminstellingen, SysteemWaarden, Vraag, Waardering), functionele-beschrijving/index.md (uitgangspunten + opinie-cyclus + onderdelen-tabel), functionele-beschrijving/credits.md (default 10 in de rekenvoorbeelden; sectie “Credits zijn iets anders dan het euro-saldo”; eisen), functionele-beschrijving/waardering-en-reviews.md (titel verbreed; sterren-sectie met toekenningsregels + consensus-ranking; eisen), functionele-beschrijving/opinie-geven.md (verplicht tekstvak vanaf niveau 1; tips-vensters-sectie; eisen), functionele-beschrijving/vraag-stellen.md (subsectie gewenst adviesniveau + totaalkosten; onboarding-poort; default 10), functionele-beschrijving/resultaat-terugzien.md (sectie geblurde teasers ontgrendelen; resultaat-tabel; eisen), functionele-beschrijving/aanmelden-registratie.md (euro-saldo + welkomstbonus; simpelste account bij teaser-betaling; anonimiteitsnuance; financiële-gegevens-noot), functionele-beschrijving/privacy-en-anonimiteit.md (herformulering anonimiteitsbelofte; betaaldata bij de PSP; communicatie/voorwaarden), functionele-beschrijving/systeembeheer.md (tabel met alle nieuwe instellingen; default 10; tips-content + Expert-toekenning; financiële totalen-noot), functionele-beschrijving/berichten.md en functionele-beschrijving/opiniegevers-selecteren.md (default 10 / betaalverkeer-nuance), scenarios/index.md (twee nieuwe scenario’s in de tabel), scenarios/credits-verdienen-en-besteden.md (rekenvoorbeeld terug naar default 10: inzage 10, 2 beoordelingen à 5; scheiding euro’s), scenarios/vraag-stellen-met-foto.md/-video.md (10 beoordelaars; gewenst niveau optioneel + teaser-verwijzing), scenarios/opinie-geven-en-waardering.md (verplichte tekst bij niveau; tips-vensters; ranking-doorwerking), scenarios/matching-opiniegevers.md (default 10), architectuur/backend-en-api.md (nieuwe sectie Beloning & Financieel met indicatieve endpoints — POST /api/betalingen/ideal + webhook, POST /api/opinies/{id}/ontgrendel-teaser, GET /api/adviesniveaus, GET /api/eurosaldo/huidig, beheer-sterren — plus de server-side ster-/rankingberekening, alles gemarkeerd “voorgesteld, nog niet gebouwd”; systeeminstellingen-endpoint bijgewerkt), architectuur/tech-stack.md (PSP/iDeal-rij achter een poort; financiële-store-noot), architectuur/beveiliging-en-privacy.md (betaaldata bij de PSP, geverifieerde idempotente webhook, KYC-noot bij uitbetaling, anonimiteitsnuance; maatregelen-rijen), architectuur/beslissingen.md (ADR-019 en ADR-026 in de overzichtstabel expliciet deels overruled/teruggedraaid per 2026-07-05 conform de bestaande herzien/ingetrokken-stijl; ADR-018-notitie; drijfveren-diagram + volledige ADR-027 t/m ADR-030; prototype-verschillen-rijen), architectuur/index.md (overzichtszin), index.md (inleiding, bounded contexts, kernconcepten) en changes.md (deze entry + versieregel). Aanscherping 3-7-2026 (binnen deze versie): de verplichte scoreschaal 1–6 (“geen mening” vervallen; gemiddelden over álle scores) is doorgevoerd in domeinmodel.md (Opinie, Scores/SectieGemiddelden, Score-enum, consensus-ranking, AI-persona’s, ontwerpbeslissing 5 vervallen + nieuwe 33), termen.md (Score, SectieGemiddelden, Opinie, Beoordelingsselectie, AI-persona, Gezicht/Kleding/Accessoires), functionele-beschrijving/opinie-geven.md (schaal + verplichting + eisen), functionele-beschrijving/index.md/vraag-stellen.md/resultaat-terugzien.md/systeembeheer.md, scenarios/opinie-geven-en-waardering.md/vraag-stellen-met-foto.md, architectuur/index.md/frontend.md/backend-en-api.md/tech-stack.md en index.md; het voorstel-document bevatte de aanscherping al. Implementatie (app/): inmiddels gebouwd naar 2026-07-05 (met dummy-iDeal) — zie de versieregel bovenaan en voorstel §10; de ⚠️/❓-punten blijven ter verificatie door Han.
[2026-07-04h] Standaard 1 beoordeling; AI-herkomst verborgen voor de gebruiker; elke beoordeling waardeerbaar; beheer-filter
Section titled “[2026-07-04h] Standaard 1 beoordeling; AI-herkomst verborgen voor de gebruiker; elke beoordeling waardeerbaar; beheer-filter”Aanleiding: Vier samenhangende punten, besloten met de gebruiker (3-7-2026). Eén vaste beoordeling per vraag is een eenvoudiger, sneller af te ronden default dan tien. Daarnaast wil de gebruiker dat de maker in zijn uitslag niet ziet welke beoordeling van een mens en welke van een AI-persona komt — het onderscheid blijft intern (en voor de beheerder), maar wordt niet aan de eindgebruiker getoond. Als de maker de herkomst niet ziet, mag de eerdere regel “AI-beoordelingen worden niet gewaardeerd” niet blijven bestaan (dat zou het AI-onderscheid impliciet verklappen): hij moet elke ontvangen beoordeling kunnen waarderen. Tot slot wil de beheerder trage/onvolledige vragen kunnen terugvinden, en zijn de stel-vraag-defaults functioneel bijgesteld.
Genomen beslissingen:
- Standaard 1 beoordeling per vraag. De
Systeeminstellingen-defaultaantalOpiniegeversgaat van 10 → 1. Blijft instelbaar via Beheer; de betekenis is ongewijzigd (het vaste aantal beoordelingen per vraag, bij het stellen alsaantalBeoordelingenop deVraagbevroren). Met de defaultscreditkostenPerBeoordeling = 1encreditsPerBeoordeling = 5kost een uitslag inzien nu 1 credit (1 × 1). - AI-herkomst niet zichtbaar voor de maker. Het veld
bron(mens/AI) is verwijderd uit de user-facing uitslag-respons (GET /api/vragen/{id}/uitslag): de maker ziet in “Mijn vragen” niet meer of een beoordeling van een mens of een AI-persona komt. De beheerder ziet de herkomst nog wél (beheer-endpoints).bron/OpinieBronblijft gewoon in het datamodel bestaan; alleen de user-facing respons toont het niet. - Elke ontvangen beoordeling waardeerbaar (0–5), óók een AI-opinie. De regel “AI-beoordelingen worden niet gewaardeerd” vervalt. De maker kan elke ontvangen beoordeling waarderen; het waardering-endpoint werkt op elke opinie. Een waardering op een menselijke opinie werkt door in de reviewer-statistieken; een waardering op een AI-opinie wordt wél op de opinie vastgelegd maar beïnvloedt geen reviewer-profiel (een AI-persona heeft er geen).
- Beheer — filter en stel-vraag-defaults. In het vragen-overzicht kan de beheerder filteren op “vragen die nog niet alle opinies hebben” (
aantalOpinies < aantalOpiniegevers). En de stel-vraag-defaults zijn functioneel bijgesteld: de schakelaar “vrije reactie” staat standaard aan, en de categorieën staan standaard uit en dichtgeklapt. - Nieuwe ADR-026 en ontwerpbeslissing 28.
Wijzigingen per bestand: domeinmodel.md (Systeeminstellingen-/aantalOpiniegevers-default 1 in prose en Vraag-tekst; Opinie.bron — herkomst niet in de user-facing uitslag, wél voor beheer; waardering-sectie: elke beoordeling waardeerbaar, AI-waardering zonder reviewer-profiel-effect; Beoordeling-context-overzicht; AI-persona’s-sectie en handmatige-AI-opinie-subsectie; ontwerpbeslissing 28 toegevoegd), termen.md (Systeeminstellingen default 1, Vraag default 1, Waardering — elke beoordeling incl. AI, geen reviewer-profiel-effect bij AI, Opinie/AI-opinie/AI-persona — herkomst niet zichtbaar voor de maker + AI nu waardeerbaar, Resultaat), functionele-beschrijving/resultaat-terugzien.md (uitslag toont geen mens/AI-onderscheid; maker waardeert elke beoordeling; eisen-tabel), functionele-beschrijving/waardering-en-reviews.md (elke beoordeling waardeerbaar, AI-waardering zonder reputatie-effect; tabellen en eisen), functionele-beschrijving/systeembeheer.md (default 1; filter “alleen vragen zonder alle opinies”; beheer ziet herkomst wél; AI-opinie nu waardeerbaar), functionele-beschrijving/vraag-stellen.md (default 1; defaults vrije reactie aan / categorieën uit en ingeklapt), functionele-beschrijving/opinie-geven.md, functionele-beschrijving/opiniegevers-selecteren.md, functionele-beschrijving/credits.md (default 1) en functionele-beschrijving/index.md, index.md, scenarios/opinie-geven-en-waardering.md, scenarios/credits-verdienen-en-besteden.md (rekenvoorbeeld op default 1), scenarios/resultaat-terugzien-en-verwijderen.md, scenarios/vraag-stellen-met-foto.md/-video.md, scenarios/matching-opiniegevers.md, architectuur/backend-en-api.md (uitslag zonder bron; beheer-overzicht met herkomst + filter; default 1; waardering op elke opinie), architectuur/beslissingen.md (ADR-026: overzichtsrij, drijfveren-diagramnode, volledige ADR-sectie, prototype-verschillen-rij). Implementatie (app/) volgt het model.
[2026-07-04g] Maker bepaalt vrije reactie (verplicht wanneer gevraagd; alleen-vrije-tekst mag); AI geeft nu een reactie + krijgt de toelichting
Section titled “[2026-07-04g] Maker bepaalt vrije reactie (verplicht wanneer gevraagd; alleen-vrije-tekst mag); AI geeft nu een reactie + krijgt de toelichting”Aanleiding: Twee samenhangende wijzigingen, besloten met de gebruiker (3-7-2026). (1) De vrije reactie was altijd optioneel en een vraag moest minstens één sectie hebben; een maker die (vooral of uitsluitend) een geschreven reactie wil, kon dat niet afdwingen en een puur-tekstuele vraag zonder smiley-scores kon niet. (2) De AI-persona leverde geen vrije tekst — waardeloos voor een alleen-vrije-tekst-vraag en weinig behulpzaam elders — en kreeg de toelichting van de maker niet in de prompt.
Genomen beslissingen:
- Maker bepaalt of er een vrije reactie gevraagd wordt; die is dan verplicht.
Beoordelingsselectiekrijgt naastSectiesde vlagVrijeTekstGevraagd(bool). Een vraag is geldig zodra er minstens één sectie is OFVrijeTekstGevraagd= true — dus alleen categorieën, alleen een vrije reactie, of beide mag. IsVrijeTekstGevraagd= true, dan is deOpinie.reactieverplicht voor de opiniegever (was voorheen altijd optioneel; lege reactie geweigerd, geen credit); is die false, dan wordt er geen reactieveld gevraagd. De reactie gaat nog steeds door de tekstmoderatie. - AI-persona geeft nu een echte, onderbouwde beoordeelde reactie. De poort
IAiPersonaServicekrijgt nu óók deVraag.toelichtingmee (deed ‘ie niet) en produceert naast scores een korte vrijereactie(constructieve tekst), zodat een AI-opinie geen “geen reactie” meer oplevert en de AI ook een alleen-vrije-tekst-vraag kan beantwoorden. De poort geeft voortaan scores + optionele reactie terug (niet alleen scores). De prompt is herkaderd om binnen het contentbeleid tóch bruikbare feedback over echte mensen te geven: expliciet dat de persoon zélf, vrijwillig (opt-in), om opbouwende stijl-/uiterlijkfeedback vroeg, met de vraag om constructieve feedback i.p.v. een “aantrekkelijkheidsoordeel”. (Kanttekening: dit verhoogt de bereidheid maar is geen 100%-garantie; het model kan bij echte personen nog terughoudend zijn — een ander model is de mogelijke vervolgstap.) De AI-reactie hoeft niet door de tekstmoderatie (ze komt van het model). - API.
POST /api/vragenkrijgtVrijeTekstGevraagd(bool) enSelectiemag leeg zijn; het slot-detail (GET /api/opdrachten/{id}) en de beoordelingsselectie exposenvrijeTekstGevraagd; bijPOST /api/opdrachten/{id}/opiniewordt een lege reactie geweigerd als de vraag er een vereist. - UI. In het gebruikersportaal bij “3 · Wat moet beoordeeld worden” een schakelaar “Vrije reactie ontvangen (verplicht voor de beoordelaar)”; de opiniegever ziet dan een verplicht reactieveld en, bij een alleen-vrije-tekst-vraag, geen smiley-scores.
- Nieuwe ADR-025 (maker bepaalt de vrije reactie, verplicht wanneer gevraagd; AI geeft nu een reactie + krijgt de toelichting) en ontwerpbeslissing 27.
Wijzigingen per bestand: domeinmodel.md (context-overzicht en Beoordeling-verantwoordelijkheid; Beoordelingsselectie +vrijeTekstGevraagd in klassendiagram en value-objects-tekst; geldigheidsregel “≥1 sectie OF vrije tekst gevraagd” op Vraag; Opinie.reactie verplicht-wanneer-gevraagd; toestandsmachine-rijen “Vraag geplaatst”/“Opinie opgeslagen”/“AI-vangnet”; de IAiPersonaService-poort — krijgt toelichting, geeft scores + reactie, herkaderde prompt — in de AI-persona’s-sectie en de handmatige-AI-opinie-subsectie; ontwerpbeslissing 27 toegevoegd, 26 bijgewerkt), termen.md (Beoordelingsselectie +VrijeTekstGevraagd, Reactie verplicht-wanneer-gevraagd, Opinie, AI-opinie en AI-persona — AI levert nu een reactie + krijgt de toelichting — bijgewerkt), functionele-beschrijving/vraag-stellen.md (schakelaar vrije reactie bij de beoordelingsselectie; geldig met ≥1 sectie óf gevraagde reactie; alleen-vrije-tekst mag; eisen-tabel), functionele-beschrijving/opinie-geven.md (reactie verplicht wanneer gevraagd; bij alleen-vrije-tekst geen scores), functionele-beschrijving/index.md (opinie-cyclus + AI-vangnet kort), functionele-beschrijving/systeembeheer.md en functionele-beschrijving/opiniegevers-selecteren.md (AI levert nu een reactie), index.md (kernconcept Opinie), scenarios/opinie-geven-en-waardering.md (reactie verplicht-wanneer-gevraagd, alleen-vrije-tekst geen scores, beslissing 4, flow, modelondersteuning), scenarios/vraag-stellen-met-foto.md/-video.md (schakelaar vrije reactie, verplichte/optionele reactie, AI-reactie, flow-diagrammen en modelondersteuning), architectuur/backend-en-api.md (POST /api/vragen met VrijeTekstGevraagd + Selectie optioneel; opinie-endpoint weigert lege verplichte reactie; persistentie-rij; IAiPersonaService geeft AiBeoordeling(Scores, Reactie?) en krijgt de toelichting; tekstmoderatie-note), architectuur/beslissingen.md (ADR-025: overzichtsrij, drijfveren-diagramnode, volledige ADR-sectie, prototype-verschillen-rijen). Implementatie (app/) volgt het model.
[2026-07-04f] Handmatige AI-persona-opinie via het beheerportaal (vision-model + FFmpeg)
Section titled “[2026-07-04f] Handmatige AI-persona-opinie via het beheerportaal (vision-model + FFmpeg)”Aanleiding: Twee samenhangende wensen rond het AI-vangnet. (1) Tot nu vulde een AI-persona alleen automatisch open slots ná de AI-deadline; de beheerder had geen manier om een trage of vastgelopen vraag direct af te vullen. (2) De AI-invulling was een open ontwerppunt: de persona gaf slechts een deterministische (regelgebaseerde) score, blind voor de opname — weinig geloofwaardig voor een oordeel over uiterlijk/kleding/accessoires. Een goedkoop vision-model maakt een echte, op de opname gebaseerde beoordeling nu betaalbaar.
Genomen beslissingen:
- Handmatige AI-opinie vanuit het beheerportaal. De beheerder kan bij een vraag met een open slot handmatig, met een gekozen AI-persona, direct één AI-opinie laten genereren. Nieuw endpoint
POST /api/beheer/vragen/{id}/ai-opiniemet body{ personaId }(beheerder-only); vult éénOpenslot (Opinie.bron = AI,personaId), 404/400 als er geen open slot is. In het Vragen-overzicht een knop “AI-opinie toevoegen” met persona-keuze, zinvol zolang er open slots zijn. Vult alleenOpenslots — nooit een door een mens vastgehouden slot. - Echte, vision-gebaseerde
IAiPersonaService. De opinie-generatie loopt via de poortIAiPersonaService(die de eerdere, nooit ingevuldeIAIPersona/IOpinieGeneratorvervangt), met een adapter op OpenAIgpt-4o-mini(goedkoop vision-model). De persona-instructiestuurt de persoonlijkheid; het model scoort alleen de door de maker gekozen secties/attributen (0–6). De AI produceert geen vrije tekst — een AI-opinie heeft geenreactie(dus geen tekstmoderatie), levert geen credits op en is niet waardeerbaar. - Foto direct, video via FFmpeg-frames. Een foto gaat rechtstreeks als beeld mee; bij een video (max. 10s) haalt de backend met FFmpeg ~4 frames uit de opname en stuurt die als beelden mee. FFmpeg zit in het API-Docker-image; de frames zijn vluchtig (in-memory) en worden niet apart opgeslagen.
- Eén sleutel, extra permissie; fail-graceful. Dezelfde app-setting
OpenAI__ApiKeybedient nu twee diensten: de Moderations-permissie (tekstmoderatie) én de chat/completions-permissie (“Model capabilities: write”) voor de vision-AI-persona. Ontbreekt de sleutel/chat-permissie of faalt de aanroep, dan valt de dienst fail-graceful terug op de bestaande deterministische scores, zodat er altijd een opinie ontstaat en de app blijft werken. Kosten: ordegrootte < $0,01 per AI-opinie. - Nieuwe ADR-024 (vision-gebaseerde AI-persona op
gpt-4o-mini+ FFmpeg + handmatige AI-opinie, fail-graceful) en ontwerpbeslissing 26.
Wijzigingen per bestand: domeinmodel.md (Opdracht AI-vangnet-bullet: automatisch + handmatig; bepaalAIvangnet-rij; toestandsmachine-rij “AI-vangnet triggert” met handmatige variant; AI-persona’s-sectie herschreven naar de vision-IAiPersonaService op gpt-4o-mini met foto/FFmpeg-video en fail-graceful terugval; nieuwe subsectie Handmatige AI-opinie vanuit het beheerportaal; ontwerpbeslissing 26 toegevoegd), termen.md (nieuwe term AI-opinie; AI-persona bijgewerkt naar IAiPersonaService/vision/FFmpeg/fail-graceful; AI-vangnet uitgebreid met de handmatige beheer-actie), functionele-beschrijving/systeembeheer.md (overzichtsrij “AI-opinie toevoegen”; AI-persona’s-note vision + fail-graceful; nieuwe sectie Handmatig een AI-opinie toevoegen), functionele-beschrijving/opiniegevers-selecteren.md (doel-bullet + note: automatisch én handmatig vangnet, vision-gebaseerd), functionele-beschrijving/index.md (kort uitgangspunt “AI als vangnet”), scenarios/matching-opiniegevers.md (stap 10 vision + handmatig; beslissing 8 toegevoegd; modelondersteuning-rij 9), architectuur/backend-en-api.md (endpoint POST /api/beheer/vragen/{id}/ai-opinie; nieuwe sectie AI-persona-vangnet (IAiPersonaService); poort in de Application-laag; note “één sleutel, twee OpenAI-permissies”), architectuur/tech-stack.md (IAIPersona → IAiPersonaService in de poort-lijsten; AI-persona-rij vision + FFmpeg; nieuwe rij “Video-frames voor AI (FFmpeg)”; moderatie-rij permissie-splitsing; Samenhang-diagramlabel), architectuur/media-en-opname.md (nieuwe sectie Frame-extractie voor de AI-persona (FFmpeg)), architectuur/beslissingen.md (ADR-024: overzichtsrij, drijfveren-diagramnode, volledige ADR-sectie, prototype-verschillen-rij). Implementatie (app/) volgt het model.
[2026-07-04e] E-mailnotificatie met link zodra de uitslag klaar is
Section titled “[2026-07-04e] E-mailnotificatie met link zodra de uitslag klaar is”Aanleiding: Stakeholder-review met Han (2-7-2026): “een mailtje met een link is prima — dan bezoeken ze ook de site opnieuw.” Een maker moet nu handmatig terugkomen om te kijken of zijn uitslag al klaar is. Een kort seintje per e-mail lost dat op, mits het de anonimiteit/dataminimalisatie niet ondergraaft: geen gevoelige beoordelingsinhoud in de mail en geen blijvende mailinglijst.
Genomen beslissingen:
- Optioneel
notificatieEmailop deVraag. De maker kan bij het stellen een e-mailadres achterlaten (nullable string) om een seintje te krijgen zodra zijn uitslag klaar is. Staat volledig los van het optionele account-e-mailadres voor wachtwoordherstel op deGebruiker; ook een anonieme maker kan het opgeven. - Eén mail met alleen een link. Zodra alle opinies binnen zijn (
beoordeeldOpwordt gezet), verstuurt het systeem één korte notificatiemail met alleen een link naar het gebruikersportaal (“je uitslag staat klaar”) — bewust géén inhoud van de beoordeling. Dat is robuust tegen aflever-/spamproblemen; de mail is enkel een trigger, de site is de bron (de uitslag zit achter de credits-poort). - Privacy: adres eenmalig. Het adres wordt direct na de verzendpoging gewist (
notificatieEmail→ null), of de verzending nu slaagde of niet; er blijft geen mailinglijst achter, en het zou hoe dan ook met de vraag verdwijnen (retentie). - Poort + ACS-implementatie, fail-graceful. Nieuwe applicatie-poort
IUitslagNotificaties(UitslagKlaarAsync(email)), infra-implementatie op Azure Communication Services Email (afzenderDoNotReply@…azurecomm.net), via de app-settingsEmail__ConnectionString,Email__AfzenderenApp__PortaalUrl. Zonder configuratie of bij een fout blijft de app werken (de uitslag is altijd in de app te zien; de mail is best-effort). - API & UI.
POST /api/vragenkrijgt een optioneel veldNotificatieEmail(simpele formaatvalidatie); het vraagformulier krijgt een optioneel e-mailveld. - Nieuwe ADR-023 (uitslagnotificatie met alleen een link via ACS, fail-graceful, adres wissen na verzending) en ontwerpbeslissing 25.
Wijzigingen per bestand: domeinmodel.md (Vraag +notificatieEmail in klassendiagram en aggregate-tekst; levenscyclus + toestandsmachine-rij “vraag beoordeeld”: eenmalig seintje en adres wissen; nieuwe sectie Uitslagnotificatie (IUitslagNotificaties) in Moderatie & Retentie; ontwerpbeslissing 25 toegevoegd), termen.md (nieuwe term Uitslagnotificatie onder U; Vraag bijgewerkt met het veld en het seintje; bij Gebruiker en Wachtwoordherstel het onderscheid met het account-e-mailadres benoemd), functionele-beschrijving/vraag-stellen.md (optioneel e-mailveld in de flow + subsectie “een seintje per e-mail” + eisen-tabel), functionele-beschrijving/resultaat-terugzien.md (het seintje verwijst naar Mijn vragen), functionele-beschrijving/privacy-en-anonimiteit.md (adres direct gewist; geen beoordelingsinhoud; los van herstel-e-mail; “Communicatie naar buiten” en registratie-voorwaarden bijgewerkt), functionele-beschrijving/aanmelden-registratie.md (kort onderscheid herstel- vs. notificatie-adres), functionele-beschrijving/index.md (opinie-cyclus + uitgangspunt, kort), scenarios/vraag-stellen-met-foto.md en scenarios/vraag-stellen-met-video.md (optionele stap, flow-diagram en modelondersteuning-tabel), scenarios/resultaat-terugzien-en-verwijderen.md (notificatiemoment), scenarios/anonimiteit-en-retentie.md (adres-wissen: beslissing + modelondersteuning), architectuur/backend-en-api.md (veld NotificatieEmail bij POST /api/vragen, persistentie-rij, nieuwe sectie Uitslagnotificatie (IUitslagNotificaties), poort in de Application-laag), architectuur/tech-stack.md (ACS Email-poort/-implementatie + tech-stack-rij), architectuur/beveiliging-en-privacy.md (bewust geen inhoud in de mail; adres eenmalig; nieuwe subsectie + maatregelen-rijen), architectuur/frontend.md (optioneel e-mailveld in de opname-flow + formulier-validatie), architectuur/beslissingen.md (ADR-023 toegevoegd: overzichtstabel-rij, drijfveren-diagram, volledige ADR-sectie, prototype-verschillen-rij). Implementatie (app/) volgt het model.
[2026-07-04d] Eén begeleidende tekst (maker) en één reactie (opiniegever); open vragen vervallen
Section titled “[2026-07-04d] Eén begeleidende tekst (maker) en één reactie (opiniegever); open vragen vervallen”Aanleiding: Stakeholder-review met Han (2-7-2026): “de vrager kan 1 tekst in zijn totaliteit meegeven; zelfde voor opiniegevers, ook 1 tekst.” Eén tekst per kant is eenvoudiger uit te leggen én er hoeft maar één tekst per kant gemodereerd te worden; de reviewer kan zich met een goede reactie onderscheiden voor een hoge waardering.
Genomen beslissingen:
- Open vragen en open antwoorden vervallen volledig.
Beoordelingsselectiebevat alleen nogSecties(het veldOpenVragenvervalt); een vraag is geldig met minstens één sectie (de oude regel “minstens één sectie óf minstens één open vraag” vervalt). Het value objectOpenAntwoordis verwijderd. - De maker houdt één vrije tekst: de bestaande
Vraag.toelichting. Eén optionele begeleidende tekst bij de foto/video, waarin hij in één keer meegeeft waar hij gerichte feedback op wil; door de OpenAI-tekstmoderatie. - De opiniegever krijgt één vrije tekst:
Opinie.reactie(nieuw). Naast de smiley-scores optioneel één tekstreactie (nullable string), i.p.v.Opinie.OpenAntwoorden. Door de tekstmoderatie: afgekeurd = beoordeling niet opgeslagen, geen credit. Iedereen mag reageren (geen drempel). - API.
POST /api/vragenheeft geenOpenVragen-veld meer;POST /api/vragen/{id}/opiniesheeftReactie(string, optioneel) i.p.v.OpenAntwoorden;GET /api/vragen/{id}/uitslagenGET /api/vragen/mijntonen per beoordelingreactiei.p.v.openAntwoorden. - UI. Het open-vragen-blok verdwijnt uit het vraagformulier; de opiniegever krijgt één “Jouw reactie (optioneel)“-tekstveld; de uitslag toont per beoordeling die ene reactie; de “Jouw aanvraag”-weergave in Mijn vragen toont geen open vragen meer. De waardering van elke ontvangen beoordeling door de maker (0–5, smiley-schaal) blijft ongewijzigd.
- Ontwerpbeslissing 23 herzien (de open-vragen/antwoorden-variant van
2026-07-03teruggedraaid) en ADR-021 herzien; ontwerpbeslissing 22 (tekstmoderatie-doelen) bijgewerkt.
Wijzigingen per bestand: domeinmodel.md (context-overzicht en Beoordeling-verantwoordelijkheid; klassendiagram: Opinie.reactie i.p.v. openAntwoorden, VO OpenAntwoord weg, Beoordelingsselectie zonder openVragen; Vraag-/Opinie-aggregate-tekst, toestandsmachine-rij “Vraag geplaatst”, value-objects-tabel, tekstmoderatie-sectie, ontwerpbeslissingen 22 bijgewerkt en 23 herzien), termen.md (nieuwe term Reactie; Open vraag/Open antwoord als vervallen-stub; Beoordelingsselectie, Opinie, Resultaat, Tekstmoderatie, Toelichting en Vraag bijgewerkt), functionele-beschrijving/vraag-stellen.md (geen open-vragen-stap; geldigheidsregel ≥1 sectie), functionele-beschrijving/opinie-geven.md (sectie “Jouw reactie (optioneel)” i.p.v. “Optionele open antwoorden”), functionele-beschrijving/moderatie.md (moderatie-doelen: toelichting + reactie), functionele-beschrijving/resultaat-terugzien.md (“Jouw aanvraag” en uitslag zonder open vragen), functionele-beschrijving/index.md (opinie-cyclus), functionele-beschrijving/opiniegevers-selecteren.md, functionele-beschrijving/privacy-en-anonimiteit.md, index.md, scenarios/opinie-geven-en-waardering.md (reactie i.p.v. open antwoorden), scenarios/vraag-stellen-met-foto.md/-video.md, scenarios/credits-verdienen-en-besteden.md (randgeval “afgekeurde reactie”), scenarios/resultaat-terugzien-en-verwijderen.md, scenarios/index.md, architectuur/backend-en-api.md (endpointvelden, persistentie-rij Reactie), architectuur/frontend.md (opname-flow zonder open-vragen-blok; reactieveld), architectuur/index.md, architectuur/media-en-opname.md, architectuur/tech-stack.md, architectuur/beveiliging-en-privacy.md en architectuur/beslissingen.md (ADR-021 herzien; ADR-017-tekst bijgewerkt). Implementatie (app/) volgt het model.
[2026-07-04c] Waardering op de smiley-schaal (presentatie-/terminologiewijziging)
Section titled “[2026-07-04c] Waardering op de smiley-schaal (presentatie-/terminologiewijziging)”Aanleiding: Stakeholder-review met Han (2-7-2026): gebruikers zijn al vertrouwd met de emoji’s/smileys waarmee ze zelf scoren; gebruik diezelfde schaal ook voor de waardering die de maker aan een ontvangen beoordeling geeft. Bovendien voorkomt het de dubbelzinnigheid met de (geschrapte) loterij-”sterren”.
Genomen beslissingen:
- Presentatie, geen modelwijziging.
Opinie.waarderingblijft een geheel getal 0–5 (0 = slechtste, 5 = beste), inclusief herwaardering en de reviewer-statistieken (aantalBeoordelingen,gemiddeldeWaardering). Alleen de UI toont nu een smiley-rij (klik = score; nogmaals klikken = terug naar 0) in plaats van sterren. - Terminologie. “0–5 sterren” is in alle levende teksten vervangen door “0–5 op de smiley-schaal”; het woord “sterren” komt alleen nog voor in de changelog en in de als vervallen/ingetrokken gemarkeerde historie.
Wijzigingen per bestand: index.md, domeinmodel.md (waardering-sectie), functionele-beschrijving/index.md, functionele-beschrijving/waardering-en-reviews.md, functionele-beschrijving/opinie-geven.md, functionele-beschrijving/resultaat-terugzien.md, scenarios/opinie-geven-en-waardering.md, scenarios/vraag-stellen-met-foto.md/-video.md, scenarios/credits-verdienen-en-besteden.md, architectuur/index.md, architectuur/frontend.md, architectuur/beslissingen.md (prototype-vergelijkingstabel). Implementatie (app/): smiley-waarderingscomponent in “Mijn vragen”; beheerportaal toont “x,x / 5 (n)”.
[2026-07-04b] Credits-tarieven: 5 verdienen per beoordeling, inzage 10; “eenmalig” expliciet in de UI
Section titled “[2026-07-04b] Credits-tarieven: 5 verdienen per beoordeling, inzage 10; “eenmalig” expliciet in de UI”Aanleiding: Stakeholder-review met Han (2-7-2026): “5 voor elke beoordeling; twee beoordelingen geeft dan 10 credits en dus goed voor het krijgen van een antwoord op hun vraag.” Plus: expliciet maken dat de inzage-prijs eenmalig per vraag is, hoe vaak de uitslag daarna ook wordt bekeken.
Genomen beslissingen:
creditsPerBeoordelingstandaard 1 → 5. Met de defaultsaantalOpiniegevers = 10encreditkostenPerBeoordeling = 1kost een uitslag 10 credits: twee gegeven beoordelingen (2 × 5) betalen precies één inzage. Alle drie de waarden blijven via Beheer instelbaar.- “Eenmalig per vraag” expliciet. Het ontgrendelen was al idempotent; de UI (ontgrendel-kaart, prijsregel en bevestiging bij het stellen) en de docs benoemen nu expliciet dat de prijs eenmalig is en herbekijken gratis.
Wijzigingen per bestand: functionele-beschrijving/credits.md, functionele-beschrijving/systeembeheer.md, functionele-beschrijving/opinie-geven.md, functionele-beschrijving/vraag-stellen.md, functionele-beschrijving/index.md, functionele-beschrijving/resultaat-terugzien.md (eenmalig-passage), domeinmodel.md (Systeeminstellingen), termen.md, scenarios/opinie-geven-en-waardering.md, scenarios/credits-verdienen-en-besteden.md (rekenvoorbeeld op de nieuwe defaults), scenarios/vraag-stellen-met-foto.md/-video.md, architectuur/beslissingen.md (ADR-017-standaarden). Implementatie (app/): default in Systeeminstellingen (domein), dynamische “+N credits verdiend”-melding in het gebruikersportaal, eenmalig-teksten.
[2026-07-04] Vast aantal beoordelingen per vraag (keuze door de maker vervallen)
Section titled “[2026-07-04] Vast aantal beoordelingen per vraag (keuze door de maker vervallen)”Aanleiding: Stakeholder-review met Han (2-7-2026). Het zelf kiezen van het aantal beoordelingen (1 t/m het maximum, met een meeschalende inzage-prijs) voelt commercieel en is onduidelijk voor nieuwe gebruikers, die het onderscheid tussen credits en kosten nog niet kennen. Een beheerinstelling volstaat: het platform bepaalt hoeveel beoordelingen elke vraag krijgt.
Genomen beslissingen:
- Vast aantal beoordelingen uit de instellingen. Elke vraag krijgt het aantal beoordelingen uit de bestaande instelling
aantalOpiniegevers(standaard 10), die weer gewoon “aantal beoordelingen per vraag” betekent — niet langer “maximum … dat de maker kan kiezen”. Er ontstaan evenveelOpenslots. Vraag.aantalBeoordelingenblijft bestaan, maar als snapshot. Het veld is voortaan een bevroren kopie van de instelling op het moment van stellen — geen keuze van de maker. Een latere wijziging van de instelling raakt bestaande vragen niet.- Inzage-prijs ongewijzigd van vorm. De bevroren
creditkosten = aantalBeoordelingen × creditkostenPerBeoordeling; alleen komtaantalBeoordelingennu uit de instellingen. De vaste inzage-prijs wordt vóór het versturen getoond. - API & UI. Het veld
AantalBeoordelingenvervalt uitPOST /api/vragen(de server neemt de instelling en bevriest die op de vraag); de kaart/stap “Aantal beoordelingen” verdwijnt uit het vraagformulier. - Ontwerpbeslissing 24 herzien (de “maker kiest”-variant van
2026-07-03teruggedraaid) en ADR-022 herzien.
Wijzigingen per bestand: domeinmodel.md (context-overzicht en Beoordeling-verantwoordelijkheid, Vraag-/Opdracht-tekst, toestandsmachine-rij “Vraag geplaatst”, maakSlots, credits-poort-prijsuitleg, Systeeminstellingen-/aantalOpiniegevers-beschrijving, ontwerpbeslissingen 8 en 24 herzien), termen.md (Credits, Creditkosten, Opdracht, OpdrachtStatus, Slot, Systeeminstellingen, Vraag), functionele-beschrijving/vraag-stellen.md (keuze-stap vervallen; vaste inzage-prijs vóór versturen), functionele-beschrijving/credits.md, functionele-beschrijving/systeembeheer.md, functionele-beschrijving/opiniegevers-selecteren.md, functionele-beschrijving/resultaat-terugzien.md, functionele-beschrijving/index.md, index.md, scenarios/matching-opiniegevers.md, scenarios/credits-verdienen-en-besteden.md, scenarios/vraag-stellen-met-video.md/-foto.md, scenarios/opinie-geven-en-waardering.md, scenarios/index.md, architectuur/backend-en-api.md (veld AantalBeoordelingen vervalt uit POST /api/vragen), architectuur/frontend.md (opname-flow zonder aantal-stap), architectuur/media-en-opname.md, architectuur/index.md en architectuur/beslissingen.md (ADR-017-tekst bijgewerkt; ADR-022 herzien).
[2026-07-03] Waardering (0–5) vervangt loterij-ster + uitbetaling; tekstmoderatie; open vragen; instelbaar aantal beoordelingen; toelichting
Section titled “[2026-07-03] Waardering (0–5) vervangt loterij-ster + uitbetaling; tekstmoderatie; open vragen; instelbaar aantal beoordelingen; toelichting”Aanleiding: De code is vooruitgelopen op het model met een reeks samenhangende wijzigingen. (1) De loterij-Ster, het Sterrensaldo en de Uitbetaling (geld) bleken meer complexiteit en risico (financiële administratie/compliance) op te leveren dan waarde. (2) De maker wil de kwaliteit van elke ontvangen beoordeling kunnen uiten. (3) Vrije tekst moet veilig voor iedereen open kunnen staan zonder een sterhouder-drempel. (4) De maker wil gericht sturen wát er beoordeeld wordt en hoeveel beoordelingen hij krijgt.
Genomen beslissingen:
- Loterij-ster én uitbetaling volledig verwijderd.
Ster,Sterrensaldo,Uitbetaling,UitbetalingStatus,FinanciëleGegevens, de instellingsterrenDrempelOpmerkingenen de hele bounded context “Beloning & Financieel” vervallen. Er is geen geld/uitbetaling meer in het systeem; deSysteemWaarden-veldenontvangen/betaaldvervallen mee. - “Sterren” = kwaliteitswaardering 0–5 door de maker. De maker waardeert elke ontvangen beoordeling met
0..5(0 = slechtste, 5 = beste); dit meet de kwaliteit van de beoordeling, niet de opname.OpiniekrijgtWaardering(nullable int, 0..5) +Waardeer(score)(geeft de vorige waarde terug voor herwaardering). Alleen menselijke opinies worden gewaardeerd. Op de reviewer (Gebruiker):AantalBeoordelingen+WaarderingSom/WaarderingAantalmet afgeleideGemiddeldeWaardering(som/aantal, 2 decimalen, 0 als leeg), plusRegistreerBeoordeling()enOntvangWaardering(score, vorige?). Vooruitblik (nog niet geïmplementeerd): een hoog aantal/hoge gemiddelde reviews geeft later extra privileges. - OpenAI-tekstmoderatie op alle vrije tekst (nieuw). Toelichting, open vragen en open antwoorden worden vóór opslaan gecontroleerd via de OpenAI Moderation API (
POST /v1/moderations, modelomni-moderation-latest, gratis). PoortIModeratieDienstmet resultaatModeratieUitslag(Toegestaan, Reden?); infra-impl.OpenAiModeratieDienst. Afgekeurde tekst wordt geweigerd (de gebruiker hoort dat bij het indienen); een afgekeurde beoordeling levert geen credit op. Zonder sleutel (OpenAI__ApiKey) draait de moderatie fail-open. De oude regel “vrije opmerkingen alleen voor sterhouders” vervalt — iedereen mag vrije tekst geven, mits die door de check komt. De bestaande, reactieve melding-moderatie blijft bestaan; de moderatie-pagina dekt nu beide. - Instelbaar aantal beoordelingen; kosten schalen mee. De maker kiest
aantalBeoordelingen(1 t/m het maximumaantalOpiniegevers, standaard 10); er ontstaan evenveel slots. De inzage-prijscreditkosten = aantalBeoordelingen × creditkostenPerBeoordeling. InstellingcreditkostenPerVraagvervangen doorcreditkostenPerBeoordeling(standaard 1).aantalOpiniegeversbetekent nu “maximum beoordelingen per vraag”. - Toelichting bij de vraag (nieuw).
Vraag.Toelichting(nullable string): begeleidende tekst naast de opname; gemodereerd. - Open vragen i.p.v. algemeen opmerkingenveld.
Beoordelingsselectiekrijgt naastSectieseen lijstOpenVragen(vraagteksten). Het vrijeOpmerkingen-veld opOpinieis vervangen doorOpenAntwoorden: een lijstOpenAntwoord(Vraag, Antwoord)-paren die de reviewer optioneel invult. Een vraag is geldig zodra er minstens één sectie of minstens één open vraag is. Open antwoorden worden gemodereerd.
Wijzigingen per bestand: domeinmodel.md (context-overzicht + context-map zonder Beloning & Financieel; Beoordeling-context: Opinie +Waardering/OpenAntwoorden −opmerkingen, nieuw VO OpenAntwoord, Beoordelingsselectie +OpenVragen, Vraag +Toelichting/aantalBeoordelingen, waardering-sectie; Gebruiker-context: waarderingsstatistieken, FinanciëleGegevens weg; Moderatie-context: IModeratieDienst/tekstmoderatie; Beheer: Systeeminstellingen en SysteemWaarden bijgewerkt; ontwerpbeslissingen 12/20 ingetrokken, 8 herzien, 21–24 toegevoegd), termen.md (Ster/Sterrensaldo/Uitbetaling/FinanciëleGegevens verwijderd; Waardering, Open vraag, Open antwoord, Toelichting, Tekstmoderatie, Creditkosten toegevoegd; Credits/Systeeminstellingen/Gebruiker/Opinie/Beoordelingsselectie herzien), architectuur/beslissingen.md (ADR-018 ingetrokken, ADR-017 herzien, ADR-019–022 toegevoegd), hernoemde pagina’s functionele-beschrijving/waardering-en-reviews.md (was sterren-en-uitbetaling.md) en scenarios/opinie-geven-en-waardering.md (was opinie-geven-en-ster.md), verwijderd scenarios/uitbetaling.md, uitgebreide functionele-beschrijving/moderatie.md (melding + tekstmoderatie), en propagatie door de functionele beschrijving, de scenario’s, de architectuur, de index-pagina’s en de sidebar (astro.config.mjs).
[2026-07-02] Credits-poort verplaatst: van insturen → uitslag inzien (cold-start deadlock opgelost)
Section titled “[2026-07-02] Credits-poort verplaatst: van insturen → uitslag inzien (cold-start deadlock opgelost)”Aanleiding: De credits-economie kende een cold-start deadlock: iedereen start op 0 credits, een vraag stellen kostte credits, en credits verdien je alleen door andermans vragen te beoordelen — maar bij 0 saldo ontstaan er nooit vragen om te beoordelen. Niemand kan beginnen. De oorzaak was niet “credits” maar de plek van de poort: bij het insturen laat je mensen betalen om werk (verdien-kansen) voor anderen te creëren, terwijl beoordeel-inspanning het schaarse goed is.
Genomen beslissingen / wijzigingen:
- Vraag stellen is gratis. De credit-afschrijving bij het insturen vervalt; de opname gaat direct de pool in en levert meteen beoordeelbaar werk op. De slots ontstaan
Openzoals voorheen. - Je uitslag inzien kost credits. Zodra een vraag beoordeeld is, ontgrendelt de maker de uitslag door eenmalig
creditkostenPerVraagaf te schrijven (de op deVraagbevrorencreditkosten). Te weinig → eerst verdienen (beoordelen). Idempotent: een al ontgrendelde uitslag kost niets meer. - Deadlock per constructie weg. Omdat posten gratis is, is er altijd werk om aan te verdienen; de economie bootstrapt zichzelf. Dit is de credit-vorm van de oorspronkelijke wederkerigheid (“beoordeel een ander om je eigen antwoord te krijgen”).
VraagkrijgtresultaatOntgrendeld(bool) +ontgrendelResultaat();creditkostenblijft maar betekent nu de bevroren inzage-prijs.Gebruiker/AnoniemeGebruikerblijven op 0 starten (geen welkomstsaldo nodig). ADR-17 herzien.
Wijzigingen per bestand: functionele-beschrijving/credits.md (verdien-/betaal-tabel, flow, eisen), domeinmodel.md (credits-poort-sectie, Vraag +resultaatOntgrendeld, toestandsmachine-rijen, Systeeminstellingen-prose, ADR-17), architectuur/beslissingen.md (ADR-017 herzien), termen.md (Credits/Creditkosten/Vraag), en bijwerkingen in vraag-stellen, resultaat-terugzien, systeembeheer, index/functionele-beschrijving/index, scenarios/credits-verdienen-en-besteden, scenarios/vraag-stellen-met-foto/-video, architectuur/backend-en-api. Ook doorgevoerd in de implementatie (app/): Vraag.ResultaatOntgrendeld/OntgrendelResultaat, VraagService (geen poort bij insturen), MijnVragenService.OntgrendelResultaatAsync, endpoint POST /api/vragen/{id}/ontgrendel, EF-migratie + frontend (gratis versturen; ontgrendel-CTA in “Mijn vragen”).
[2026-07-01] Resultaat terugzien + resultaat-retentievenster (opname niet meer direct weg)
Section titled “[2026-07-01] Resultaat terugzien + resultaat-retentievenster (opname niet meer direct weg)”Aanleiding: Nieuw requirement van de gebruiker: er is geen manier om het resultaat van een gestelde vraag terug te zien, en bij dat resultaat wil de maker de foto/video nog kunnen bekijken en die daarna zelf verwijderen — met automatische verwijdering na een periode. Dit botste frontaal met de bestaande kernregel “de opname verdwijnt direct zodra de beoordeling klaar is”: op het moment dat de maker het resultaat wil zien, bestond de opname niet meer.
Genomen beslissingen / wijzigingen:
- Opname verdwijnt niet meer direct bij afronding. Zodra een vraag
isBeoordeeld()is, wordtbeoordeeldOpgezet en blijft het resultaat + de opname bewaard in een resultaat-retentievenster. De directeverwijderen-bij-afrondingvervalt. - Maker ziet zijn eigen resultaat terug. Nieuwe maker-weergave “Mijn vragen”: lijst van eigen vragen met status (in behandeling / beoordeeld), en per beoordeelde vraag het resultaat (
SectieGemiddelden, aantal opinies, opmerkingen van sterhouders) plus de opname (foto/video). De opname is na afronding uitsluitend voor de maker zichtbaar — opiniegevers zagen die alleen tijdens hun beoordeling. - Handmatig + automatisch verwijderen. De maker kan zijn opname/vraag zelf verwijderen (directe actie). Automatisch verdwijnt de vraag+opname zodra
now - beoordeeldOp >= resultaatRetentie— een nieuwe, via Beheer instelbareresultaatRetentieUren(standaard 168 = 7 dagen). De retentie-sweep in de onderhoudsdienst dwingt dit af. - Anonimiteit blijft de belofte. De opname verdwijnt nog steeds altijd — nu uiterlijk aan het einde van het retentievenster, of eerder als de maker zelf verwijdert. Veiligheids-timeout (nooit-afgeronde vragen) en meldingen-drempel (moderatie) blijven ongewijzigd. Kanttekening: een anonieme maker kan alleen terugzien zolang zijn sessie leeft; de vraag verdwijnt hoe dan ook automatisch na het venster.
Retentiebeleid—verwijderDirectNaBeoordeling(bool) vervangen doorresultaatRetentieUren(int);VraagkrijgtbeoordeeldOp(nullable). ADR toegevoegd: resultaat-retentievenster i.p.v. directe verwijdering.
Wijzigingen per bestand: domeinmodel.md (Vraag +beoordeeldOp + levenscyclus, toestandsmachine-noot, Retentiebeleid, Systeeminstellingen +resultaatRetentieUren, Verdeelstatistieken-noot), termen.md (Retentiebeleid/Vraag/Beoordeling/Systeeminstellingen herzien; nieuwe termen Resultaat & Resultaat-retentie), architectuur/media-en-opname.md (cleanup/retentie herschreven: retentievenster + maker-view + handmatig verwijderen; endpoints), architectuur/backend-en-api.md (endpoints GET /api/vragen/mijn, DELETE /api/vragen/{id}), nieuwe pagina functionele-beschrijving/resultaat-terugzien.md + scenario scenarios/resultaat-terugzien-en-verwijderen.md, en bijwerkingen in vraag-stellen, privacy-en-anonimiteit, systeembeheer, opinie-geven, melden-en-verwijderen, scenarios/anonimiteit-en-retentie, scenarios/vraag-stellen-met-foto/-video, de index-pagina’s. Ook doorgevoerd in de implementatie (app/): Vraag.BeoordeeldOp/MarkeerBeoordeeld, BeoordelingAfronder (geen directe media-delete), Systeeminstellingen.ResultaatRetentieUren, retentie-sweep in OnderhoudService, endpoints + MijnVragen-scherm + EF-migratie.
[2026-06-30b] Credits-economie (vervangt de “eerst beoordelen”-poorten)
Section titled “[2026-06-30b] Credits-economie (vervangt de “eerst beoordelen”-poorten)”Aanleiding: Nieuw requirement: je bouwt credits op door vragen te beoordelen (los van het stellen van een eigen vraag), je ziet je saldo, en bij het stellen van een vraag zie je de creditkosten. Heb je te weinig, dan moet je eerst verdienen. Dit generaliseert en vervangt de twee overlappende poorten aantalEerstZelfBeoordelen en reciprociteitDrempel.
Genomen beslissingen / wijzigingen:
- Credits = de enige deelname-poort.
GebruikerenAnoniemeGebruikerkrijgen eencredits-saldo. VerdiencreditsPerBeoordelingper voltooide menselijke opinie; betaalcreditkostenPerVraagom een vraag te stellen. De poort zit bij het insturen: te weinig credits → de vraag kan niet worden verstuurd (eerst verdienen). - Twee poorten vervallen.
aantalEerstZelfBeoordelenénreciprociteitDrempelzijn verwijderd; deGeblokkeerd-OpdrachtStatusenVraag.reciprociteitDoelvervallen — slots ontstaan altijdOpen. ADR-17 herzien tot de credits-poort; ADR-20 toegevoegd (credits ≠ sterren); ADR-8/12 bijgesteld. - Credits ≠ sterren. Credits = mogen vragen (élke beoordeling verdient). Sterren blijven een aparte loterij-beloning (één per vraag) die bij een drempel privileges geeft — vrije
opmerkingen(sterrenDrempelOpmerkingen) — en tot uitbetaling leidt. - Anoniem = sessie-credits. De
AnoniemeGebruikeris nu de anonieme-sessie-identiteit met een sessie-gebondencredits-saldo +verlooptOp(anoniemeSessieMin); credits vervallen met de sessie. Een nieuw geregistreerd account start op 0. - Nieuwe
Systeeminstellingen:creditkostenPerVraag(standaard 3),creditsPerBeoordeling(1),anoniemeSessieMin;aantalEerstZelfBeoordelenenreciprociteitDrempelverwijderd.
Wijzigingen per bestand: domeinmodel.md (Gebruiker/AnoniemeGebruiker/Systeeminstellingen/Vraag/OpdrachtStatus, toestandsmachine, Opdrachtverdeler, “reciprociteits-poort” → “credits-poort”, ADR 8/12/17 herzien + 20), termen.md (nieuwe term Credits; Reciprociteitsdrempel verwijderd; Gebruiker/AnoniemeGebruiker/OpdrachtStatus/Opmerkingen/Sterrensaldo/Systeeminstellingen herzien), nieuwe pagina functionele-beschrijving/credits.md + scenario scenarios/credits-verdienen-en-besteden.md, en bijwerkingen in vraag-stellen, sterren-en-uitbetaling, systeembeheer, aanmelden-registratie, de scenario’s en architectuur/*. Ook doorgevoerd in de implementatie (app/).
[2026-06-30a] Reviewronde verwerkt: instelbaarheid, retentie, online, AI-vangnet, beoordelingsselectie, opmerkingen, land-keuzelijst, e-mailherstel
Section titled “[2026-06-30a] Reviewronde verwerkt: instelbaarheid, retentie, online, AI-vangnet, beoordelingsselectie, opmerkingen, land-keuzelijst, e-mailherstel”Aanleiding: Reviewcommentaar op de docs-site (Hypothesis-annotaties, account Clermond). Meerdere samenhangende punten over hardcoded aantallen, retentietiming, het online-principe, te weinig opiniegevers, configureerbare beoordeling, opmerkingen, locatie en wachtwoordherstel.
Genomen beslissingen / wijzigingen:
- Instelbare parameters — nieuw value object
SysteeminstellingenopSysteem(Beheer):aantalOpiniegevers,antwoordtermijnMin,aantalEerstZelfBeoordelen,onlineVensterMin,veiligheidsTimeoutMin,meldingenDrempel,sterrenDrempelOpmerkingen. Vaste getallen (“10 opiniegevers”, “30 min”) zijn vervangen door verwijzingen naar deze instellingen. - Retentie event-gedreven — een vraag/opname wordt direct verwijderd zodra de beoordeling klaar is; de vaste 30-minuten-vervaltijd is geschrapt. Alleen een instelbare veiligheids-timeout ruimt nooit-afgeronde vragen op.
Vraag.isVerlopen(maxLeeftijdMin)→isBeoordeeld();Retentiebeleidherzien. - Online = voorrang, geen filter —
onlineverwijderd uitSelectieCriteria; het “Online-venster” is geen selectiecriterium meer maar een voorrangsvenster (onlineVensterMin). Presence blijft bestaan om voorrang te geven. - AI-persona-vangnet + antwoordtermijn —
OpdrachtkrijgtverlooptOpen statusVerlopen(niet op tijd beantwoord → herverdelen); is er geen mens, dan levert een AI-persona de opinie. Nieuwe enumOpinieBron { Mens, AIPersona }opOpinie. Concrete AI-invulling = open punt. - Beoordelingsselectie door de vrager —
VraagkrijgtBeoordelingsselectie(gekozen secties/attributen ná de opname);Opinie.scoresdekt alleen die. Secties/attributen worden beheerbare stamdata; de drie vaste secties (Gezicht/Kleding/Accessoires) zijn nog slechts de standaard. - Opmerkingen alleen voor sterhouders — vrije
opmerkingenop eenOpiniezijn beperkt tot opiniegevers metSterrensaldo≥sterrenDrempelOpmerkingen(standaard 4). Land= keuzelijst —Land(ISO 3166-1) wordt weer gekozen uit eenKeuzelijst; GeoIP/automatische bepaling en de term “Locatiebepaling” vervallen. DeRegioM49blijft automatisch afgeleid via UN M49 (werelddeel + subregio) voor verbredende matching.- Optioneel e-mailadres —
Gebruikerkrijgt een optioneelemail-veld, uitsluitend voor wachtwoordherstel; anonieme gebruikers hebben geen herstel.
Wijzigingen per bestand: domeinmodel.md (klassendiagrammen Beoordeling/Gebruiker/Moderatie/Beheer, aggregates, value objects, enums, ontwerpbeslissingen 4 & 7 herzien + 8–13 toegevoegd), termen.md (nieuwe termen AI-persona, Antwoordtermijn, Beoordelingsselectie, Opmerkingen, Sectie, Systeeminstellingen, Wachtwoordherstel; Locatiebepaling verwijderd; vele termen herzien), de functionele beschrijving, de scenario’s en de architectuur. (Al eerder uit dezelfde review verwerkt: Inzending → Vraag/Vraagtype en de twee gebruikerstypen.)
[2026-06-29f] Anoniem-aggregate hernoemd: TijdelijkeGebruiker → AnoniemeGebruiker
Section titled “[2026-06-29f] Anoniem-aggregate hernoemd: TijdelijkeGebruiker → AnoniemeGebruiker”Aanleiding: Op de docs-site bleef “anonieme gebruiker” (het begrip) náást TijdelijkeGebruiker (het record) bestaan. Zinnen als “een anonieme gebruiker … er ontstaat dan een TijdelijkeGebruiker” lazen daardoor alsof het twéé gebruikerstypen waren — precies de verwarring die e wilde wegnemen.
Wijziging: Het aggregate is hernoemd naar AnoniemeGebruiker, zodat begrip én record één naam delen. “Tijdelijk” verdwijnt als entiteitsnaam; de vluchtige, op-te-ruimen aard blijft als eigenschap beschreven. Bijgewerkt in domeinmodel.md (overzichtstabel, klassendiagram, aggregate-tekst, ontwerpbeslissing 3), termen.md (de term Tijdelijke gebruiker is opgegaan in Anonieme gebruiker), de functionele beschrijving, de scenario’s en de architectuur. Puur een terminologie-/labelwijziging — geen structurele wijziging aan aggregates, velden of relaties.
[2026-06-29e] Gebruikerstypen vereenvoudigd: twee i.p.v. drie
Section titled “[2026-06-29e] Gebruikerstypen vereenvoudigd: twee i.p.v. drie”Aanleiding: Vraag bij de review: waarom drie gebruikerstypen (geregistreerd, ongeregistreerd, tijdelijk), en waarom zijn “ongeregistreerd” en “tijdelijk” niet hetzelfde? Ze beschreven grotendeels dezelfde groep vanuit twee invalshoeken (een toestand-vlag op Gebruiker vs. een apart vluchtig record) — een redundantie uit het prototype.
Wijziging: Nog twee gebruikerstypen:
- Geregistreerde gebruiker =
Gebruiker(blijvend account). DeisGeregistreerd-vlag is verwijderd — eenGebruikeris per definitie geregistreerd. - Anonieme gebruiker = vastgelegd als een vluchtig
TijdelijkeGebruiker-profiel (geen account; alleen demografie voor de matching; opgeruimd met de vraag).
Het begrip “ongeregistreerde gebruiker” vervalt; “tijdelijk” blijft enkel als de aggregate-naam (TijdelijkeGebruiker) van de anonieme gebruiker. Bijgewerkt in domeinmodel.md, termen.md (term Anonieme gebruiker toegevoegd, Ongeregistreerde gebruiker verwijderd), de functionele beschrijving, de scenario’s en de architectuur.
[2026-06-29d] Kern-aggregate Inzending hernoemd naar Vraag
Section titled “[2026-06-29d] Kern-aggregate Inzending hernoemd naar Vraag”Aanleiding: Reviewcommentaar (Hypothesis-annotaties op de docs-site): het kernbegrip Inzending sluit niet aan op de gebruikerstaal — een gebruiker “stelt een vraag”. Vraag is consistenter met de UI (“Stel je vraag”) en het prototype.
Wijziging: Het aggregate Inzending → Vraag hernoemd door het hele model: InzendingType → Vraagtype, het veld inzendingId → vraagId, en alle proza/termen/anchors. Puur een terminologie-/labelwijziging — geen structurele wijziging aan aggregates, velden of relaties. Bijgewerkt in domeinmodel.md, termen.md (entry verplaatst naar V), de functionele beschrijving, de scenario’s, de architectuur en het prototype (app/frontend).
[2026-06-29c] Gestandaardiseerde, automatisch bepaalde locatie (ISO 3166-1 + UN M49)
Section titled “[2026-06-29c] Gestandaardiseerde, automatisch bepaalde locatie (ISO 3166-1 + UN M49)”Aanleiding: De regio-keuzelijst was Nederland-centrisch (Noord/Zuid/Midden/Oost/West) en een handmatige landenlijst schaalt niet internationaal. Doel: een locatie-indeling die voor álle landen werkt, met zo min mogelijk handmatige invoer.
Genomen beslissingen:
Landvolgens ISO 3166-1 wordt het primaire locatieveld (vervangt het vrijenationaliteit/regio).RegioM49(werelddeel + subregio) wordt afgeleid uit het land via UN M49; matching kan verbreden van land → subregio → werelddeel.- Automatische bepaling van het land via GeoIP (server-side, geen permissieprompt), vooringevuld en corrigeerbaar; browsertaal/tijdzone als hint. Alleen het afgeleide/bevestigde land wordt opgeslagen — niet het IP. Geen Geolocation-API.
Keuzelijsthoudt noggenderen deachtergrond-opties; land/regio zijn géén keuzelijst meer.gender,leeftijdenachtergrondblijven keuzes van de gebruiker.
Wijzigingen:
domeinmodel.md:Profiel/ProfielSnapshot/SelectieCriteria/TijdelijkeGebruiker→Land; nieuwe value objectsLandenRegioM49;Keuzelijst-tekst; locatie-notitie; ontwerpbeslissing 7.termen.md: nieuwe termenLand,Geografische regio (UN M49),Locatiebepaling;Profiel/SelectieCriteria/Keuzelijst/TijdelijkeGebruikerbijgewerkt.- Architectuur (nieuwe ADR +
backend-en-api,tech-stack,frontend,beveiliging-en-privacy) en de functionele- en scenario-pagina’s bijgewerkt op de nieuwe locatie-aanpak.
[2026-06-29b] Context Beheer & Referentie hernoemd naar Beheer
Section titled “[2026-06-29b] Context Beheer & Referentie hernoemd naar Beheer”Aanleiding: De naam “Beheer & Referentie” was verwarrend: “referentie” werd gelezen als “het prototype / de oude applicatie”, terwijl het hier stamdata (keuzelijsten) bedoelde.
Wijziging: De bounded context is hernoemd naar Beheer en groepeert nog steeds Systeem, Keuzelijst en Bericht. Dit is puur een terminologie-/labelwijziging — geen structurele wijziging aan aggregates, velden of relaties. Bijgewerkt in domeinmodel.md (kop, overzichtstabel, context-map), termen.md, index.md en de anchor-links in de functionele- en scenario-pagina’s.
[2026-06-29a] Eerste vastlegging van het domeinmodel
Section titled “[2026-06-29a] Eerste vastlegging van het domeinmodel”Aanleiding: Opzet van de documentatiesite voor I See You naar het voorbeeld van Budgetbeheer. Het domeinmodel is afgeleid uit het functioneel ontwerp (bron/functioneel-ontwerp/) en de prototype-implementatie (Express-backend met JSON-stores). Aanvullende eis: het moet mogelijk zijn een foto te gebruiken in plaats van een video.
Genomen beslissingen:
- Vijf bounded contexts. Beoordeling (kern), Gebruiker & Toegang, Beloning & Financieel, Moderatie & Retentie, Beheer & Referentie.
Inzendingals kern-aggregate dat video én foto dekt viaMediaType { Video, Foto }; één opinie-cyclus voor beide media.- Opinie-model. Drie secties (Gezicht, Kleding, Accessoires), elk attribuut gescoord op 0–6 met
0= geen mening;SectieGemiddeldenafgeleid over scores > 0. - Matching via
SelectieCriteria(gender, nationaliteit, regio, achtergrond, leeftijdsrange, online) levert 10Opdrachten per inzending; één opiniegever krijgt automatisch eenSter. - Retentie als expliciet beleid (30 min inzending, 10 min online-venster, 3 meldingen → verwijderen). Opnames worden altijd verwijderd; tijdelijke/ongeregistreerde gebruikersdata blijft niet bewaard.
- Single-device sessies en snapshots (maker/reviewer/criteria) voor herleidbaarheid.
Wijzigingen:
domeinmodel.md: nieuw — context-overzicht, context map (Mermaid), klassendiagrammen (MermaidclassDiagram) per context, aggregates/value objects/enums en ontwerpbeslissingen.termen.md: nieuw — A–Z woordenlijst.functionele-beschrijving/*,architectuur/*,scenarios/*: nieuw — zie de betreffende secties.index.md(overzicht): nieuw.
Open punten (bewust nog niet vastgelegd): exacte uitbetaal-drempel/-frequentie; of er naast Gezicht/Kleding/Accessoires een Lichaam-sectie komt (in de UI-labels aanwezig, in de gevalideerde scores niet); definitieve huisstijl/branding.
Workflow (één branch)
Section titled “Workflow (één branch)”I See You houdt documentatie en implementatie op één branch (main) in aparte mappen (docs/ en app/). Daarom geen cross-repo model-change-issuebrug zoals bij Budgetbeheer, maar een lokale docs-first-discipline:
- Werk bij een modelwijziging eerst
docs/bij: het Mermaid-diagram +domeinmodel.md+termen.md+ het geraakte scenario. - Bump de modelversie bovenaan dit bestand en voeg een changelog-entry toe (aanleiding, beslissingen, wijzigingen per bestand).
- Bouw (
npm run build) en controleer dat diagrammen renderen en links kloppen. - Laat daarna de code in
app/het model volgen.
Deze workflow bouwen we stap voor stap verder uit (zie Architectuur → Beslissingen).