浏览代码

P2-6: de-risk-test op een kopie — destructieve bug reproduceert niet

Op HorecagelegenhedenOverzichtCopy een TextField toegevoegd aan de Column
van tab 0. Verse export erna: alle 6 tabs, 6 horcat-calls en 6
kaartbindingen intact, en de diff tegen de export van vlak ervoor is puur
additief (0 verwijderde regels). De live pagina is niet aangeraakt. De
"Column verliest zijn kinderen"-bug van 2026-08-25 trad dus niet op.

Wel een andere blokkade gevonden: hetzelfde op tab 1, waar de Column in
een ConditionalBuilder-If-tak zit, doet 3x niets — dialoog blijft open,
niets toegevoegd, niets kapot. Alleen tab 0 is een kale Column; tabs 1
t/m 5 zitten allemaal in een ConditionalBuilder, dus dat raakt stap 3/4/6
direct.

CLAUDE.md: recept voor de Insert-modal die soms wél werkt (meteen typen,
niet eerst in het zoekveld klikken; widget landt achteraan), plus de
constatering dat een mislukte export een transient serverfout kan zijn —
één keer opnieuw proberen voor je gaat diagnosticeren.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bob 1 周之前
父节点
当前提交
529033caf0
共有 2 个文件被更改,包括 227 次插入112 次删除
  1. 21 0
      CLAUDE.md
  2. 206 112
      TASKS.md

+ 21 - 0
CLAUDE.md

@@ -1122,6 +1122,16 @@ actie geselecteerd). Gebruik dit altijd vóór je zelf gaat zoeken.
 een geblokkeerde export is vaak niet jouw schuld — check eerst wíe de
 fout veroorzaakt voordat je in je eigen werk gaat spitten.
 
+**Een mislukte export kan ook gewoon een serverhikje zijn — probeer 'm
+één keer opnieuw vóór je gaat diagnosticeren.** Bevestigd 2026-09-04:
+`flutterflow export-code` gaf `Body: Error generating code for the
+project. Make sure there are no project errors.` + `Unexpected error
+from the server`, terwijl het Issues-paneel alleen de twee bekende
+niet-blokkerende `Property Override`-fouten toonde en een export van een
+halfuur eerder met exact dezelfde foutenlijst prima slaagde. Een
+identieke tweede aanroep, direct erna, gaf gewoon "All done!". Ga dus
+niet meteen op zoek naar wat je zojuist gewijzigd hebt.
+
 **Nuance (2026-09-03, gemeten): NIET elke error blokkeert de export —
 alleen sommige soorten.** De kop hierboven is te absoluut. Concreet
 gemeten op dit project met 3 errors in de lijst:
@@ -1751,6 +1761,17 @@ dezelfde inline-picker op een gewone body-`Column` wél meteen werkte —
 mogelijk is een AppBar-Row specifiek een moeilijkere insertie-plek,
 sowieso eerst de inline-variant op een `Column` proberen vóór de grote
 modal.
+- **Update 2026-09-04 (P2-6-test): rechtsklik → "Insert Widget" opent
+  hier tegenwoordig de grote centrale modal, en die werkt soms wél.**
+  Op een kale `TabBar Page > Column` lukte het in één keer: rechtsklik op
+  de tree-rij → "Insert Widget" → **meteen typen** (het zoekveld is al
+  gefocust, klik er niet eerst in — dat brak het juist) → kaartje
+  aanklikken. Het widget landt **achteraan** in de Column, niet vooraan
+  (anders dan bij slepen in de tree, dat altijd op positie 0 dropt).
+  Op een `Column` binnen een `ConditionalBuilder` → `If`-tak lukte
+  precies dezelfde reeks 3x níet: het kaartje aanklikken doet niets, de
+  dialoog blijft open, geen foutmelding — en ook niets kapot. Vermoeden,
+  niet bewezen: insertie in een ConditionalBuilder-tak is het probleem.
 - **Update 2026-08-07:** de compacte inline-dropdown is **niet
   betrouwbaar per se buiten AppBar-Row** zoals hierboven gesuggereerd —
   op `HorecagelegenhedenOverzicht` (een gewone `Column`, `TabBar`-node)

+ 206 - 112
TASKS.md

@@ -885,16 +885,15 @@ volstond. Uit deze lijst verwijderd.)*
 Volledige onderbouwing met screenshots: [Designbrug-rapport](https://claude.ai/code/artifact/29f3412a-305f-49f3-971c-cd9bd310beb1).
 Afvinkbare werklijst: [Designbrug werklijst](https://claude.ai/code/artifact/0c0c7347-d681-417d-83d7-7feb1de6457d).)*
 
-**P0-11 · Eigenaar: Bob.** Menu (`drawer_component_widget.dart`)
-scrollt praktisch niet: 21 items, past op geen telefoonscherm, en
-schuift alleen na een lange doelbewuste veeg — een korte veeg doet
-zichtbaar niets. Gevolg: "Thuis bezorgen", "Mijn Account" en de
-terugknop worden door een deel van de gebruikers nooit gezien.
-Vermoedelijke oorzaak: `SingleChildScrollView` binnen
-`SingleChildScrollView` (regel 97 en 124). Fix: buitenste scroll-wrapper
-weg, en Provincie/Gemeente inklapbaar maken of samenvoegen tot één
-lijst met een schakelaar erboven (halveert de lengte én maakt het
-verschil tussen die twee eindelijk zichtbaar).
+*(P0-11 afgerond 2026-09-04 — Bob, builder. Het drawer-menu had een
+`SingleChildScrollView` binnen een `SingleChildScrollView` (Column met
+"Scrollable" aan, twee niveaus genest), waardoor de binnenste oneindige
+hoogte kreeg en nooit scrollde. Binnenste Column op Scrollable UIT gezet;
+verse export bevestigt nog één `SingleChildScrollView` (regel 98).
+**Stap 2 (Gemeente/Provincie samenvoegen tot één lijst) is bewust
+afgewezen** — Bob 2026-09-04: "nee, dit is overzichtelijk, houden zo".
+Het menu blijft dus 21 items met twee gescheiden blokken. Uit deze lijst
+verwijderd.)*
 
 **P0-12 · Eigenaar: Bob — grotendeels AL OPGELOST, alleen de tekstregel
 rest.** Oorspronkelijk: twee identieke hartjes met verschillende
@@ -962,15 +961,27 @@ de "bezorgtijden"-Visibility-conditie op `HorecagelegenheidCurrent`
 verkeerde `Openingstijden`-veld. Bevestigd via verse export. Uit deze
 lijst verwijderd.)*
 
-**Minor-2 · Eigenaar: Bob.** `HorecagelegenheidCurrent`
-(`horecagelegenheid_current_widget.dart`), Bezorgen-tab: "afhaalopties"
-en "bezorgdin" hebben nog geen Visibility-conditie (kale
-`Text(getJsonField(..., r'''$[:].afhaalopties/bezorgdin''').toString())`)
-— tonen dus nog een letterlijke "null" bij een leeg veld. Bob moet
-eerst uitzoeken hoe deze twee velden uit de API komen (2026-08-14:
-"moet ik even kijken") voordat de standaard "Is Set and Not
-Empty"-fix (zelfde recept als de overige 15 velden van het
-oorspronkelijke P0-8) toegepast kan worden.
+*(Minor-2 afgerond 2026-09-04 — Bob, builder, met Claude's diagnose.
+Waren twee bugs. (1) Het JSON Path `$[:].bezorgdin` bestond niet: de
+respons heeft **`bezorgtin`** (met een t), afgeleid van het views-label
+`Bezorgtin` op `field_hor_bez_bezorgd_in_1` — dit veld toonde daardoor op
+**elke** horecagelegenheid de tekst "null", niet alleen bij lege data.
+(2) `afhaalopties` miste zijn Visibility-guard. Beide gefixt, plus een
+derde vondst: `afhaalopties`, `bezorgtin` én `thuisbezorgtbetaalopties`
+zijn alle drie **lists** (bevestigd op nid 157070, Lotus Enkhuizen:
+`["Afhaal","Bezorgen"]`), en `.toString()` op een Dart-list geeft
+blokhaken — de app toonde `[Afhaal, Bezorgen]`. Opgelost met de nieuwe
+custom function **`lijstAlsTekst(dynamic items)`** (join met ', ',
+plat geschreven zonder closures i.v.m. FlutterFlow's parser), toegepast
+op alle drie. Bevestigd via verse export. Uit deze lijst verwijderd.)*
+
+⚠️ **Herbruikbaar:** `lijstAlsTekst` staat nu in `custom_functions.dart`
+en is de standaardoplossing zodra een JSON-lijstveld als platte tekst
+getoond moet worden. Gecontroleerd dat er verder geen kale lijst-naar-
+tekst-plekken meer zijn: `categorie` (11 plekken) gaat overal correct via
+`.map<String>((e) => e.toString())` in een `List.generate`, en
+`cryptocoins`/`fotoos` bestaan alleen in `api_calls.dart` zonder
+tekstweergave.
 
 ## Drupal dingen — verzamellijst, batchen bij Bob's eigen Drupal-sessie
 
@@ -1086,10 +1097,55 @@ parameter op `HorecagelegenheidoverzichtCall` (voorheen
 '17967'` i.p.v. de crashende `horcat ??= null!;`. Uit deze lijst
 verwijderd.)*
 
-**P1-5 · Eigenaar: Bob — bezig (sessie 2026-09-04a, Drupal/infra-blok, Claude regisseert).** "Thuis bezorgen" koppelen aan een echt
-leverbaar-veld per horecagelegenheid. Wacht op Bob: eerst het
-API-endpoint aan Drupal-kant configureren. Daarna tonen/verbergen op
-basis van het echte veld i.p.v. de huidige dode tap.
+**P1-5 · Eigenaar: Bob (Drupal-werk vereist; geen quick win).** "Thuis
+bezorgen" in het menu (`drawer_component_widget.dart`, de `ListTile` met
+`Icons.delivery_dining`, ±regel 767) heeft **geen `onTap`** — bevestigd
+dode knop. **Uitgezocht 2026-09-04 (Claude, live `curl`):**
+- **De data bestáát wel.** Op nid 157070 (Lotus, Enkhuizen) zijn alle
+  bezorgvelden gevuld, en `afhaalopties` bevat letterlijk de waarde
+  `"Bezorgen"` (vocabulaire `thuisbezorgen_afhaalbezorgen`, waarden
+  "Afhaal"/"Bezorgen"). Dát is het leverbaar-veld waar deze taak om
+  vraagt — er hoeft geen nieuw veld bij. **Bob bevestigt (2026-09-04):**
+  `afhaalopties` is in Drupal een **multi-select** met o.a. de opties
+  "bezorgen" en "afhalen" — de views-filter is dus een simpele
+  term-match op die ene waarde.
+- **Maar het is niet op te halen met de bestaande call.**
+  `flutterflowmobiel_establishments` heeft maar **één display**
+  (`services_1`; services_2/3 bestaan niet) en geeft per zaak alleen
+  `nid, titel, adres, plaats, logo, categorie` terug — geen bezorg-info,
+  en er is geen exposed filter om op te filteren. De call kent alleen
+  `horcat`, `townid`, `display_id`, `page`.
+- **`townid` is verplicht:** zonder townid geeft élke `horcat` `[]`.
+- **Bekende horcat-waarden** (gemeten op Arnhem, townid 25434): `17963`
+  podia/theater/zaalverhuur, `17967` uitgaansgelegenheden, `17968` eten
+  (klein), `17969` bioscoop/entertainment, `34` eten (breed, 20 zaken),
+  `17965` leeg.
+
+**Dus de keuze is een Drupal-keuze, en die moet vóór het app-werk:**
+(a) een **extra display** op `flutterflowmobiel_establishments` met een
+filter op `field_hor_bez_afhaalbezorgen` = "Bezorgen" (schoonst; de app
+krijgt dan een lijst zoals de bestaande overzichten en de menuknop kan
+er gewoon naartoe navigeren), of (b) `afhaalopties` **toevoegen aan de
+bestaande display** en in de app filteren (meer data over de lijn, en
+de paginering klopt dan niet meer met wat je toont — afgeraden).
+
+**Eerst meten of het de moeite is:** tel hoeveel zaken daadwerkelijk
+bezorgen vóór je de view bouwt —
+`drush @<alias> sqlq "SELECT COUNT(DISTINCT entity_id) FROM field_data_field_hor_bez_afhaalbezorgen"`
+(veldnaam verifiëren in de veldenlijst). In een steekproef van 59
+gelegenheden mét evenementen was élk bezorgveld leeg — dat waren
+poppodia en cafés, dus geen representatieve steekproef, maar het maakt
+wel de vraag scherp of dit een handvol zaken betreft of honderden.
+
+**P1-6 restpunt (2026-09-04):** de drie Bezorgen-tab-velden van Minor-2
+kregen bij het binden automatisch een Default Variable Value met de
+**veldnaam** erin — `valueOrDefault<String>(..., 'afhaalopties')`,
+`'betaalopties'` en `'bezorgdin'` (die laatste nog met een d, terwijl het
+veld `bezorgtin` heet). De `!= null`-guard ervoor vangt het normale geval
+af, dus dit slaat alleen toe bij een lege array `[]` i.p.v. `null` — in
+een steekproef van 59 zaken nooit voorgekomen. Cosmetisch, laag risico:
+Default Variable Value leegmaken op die drie widgets in
+`horecagelegenheid_current_widget.dart` (regels ~1164, ~1384, ~1423).
 
 *(P1-6 grotendeels afgerond 2026-08-16 t/m 2026-08-18 — letterlijke
 veldnaam i.p.v. nette placeholder bij ontbrekende data
@@ -1264,42 +1320,27 @@ bewust uitgelaten, zie P2-10). Bevestigd via verse export:
 in `pubspec.yaml`, Firebase-init in `main.dart`. Uit deze lijst
 verwijderd.)*
 
-**P1-30 · Eigenaar: Bob — bezig (sessie 2026-09-04a, Drupal/infra-blok). (Cloudflare/infra-niveau, geen Claude-taak.)**
-API-misbruik beperken voor de publieke Drupal-backend. **Besproken
-2026-08-25 (chat, n.a.v. FlutterFlow's App Check-toggle):**
-- **Firebase App Check overgeslagen** — beschermt alleen Firebase-
-  diensten zelf (Firestore/Cloud Functions/Storage). Backend hier is
-  Drupal, geen Firebase-dienst, dus de toggle alleen doet niets; zou
-  alleen werken met extra custom Drupal-code die het App Check-token
-  verifieert (Play Integrity/DeviceCheck) — significante extra bouw aan
-  beide kanten (FlutterFlow custom action + Drupal custom module). Niet
-  nu de moeite waard, pas overwegen als misbruik een gemeten probleem
-  wordt.
-- **Kernbeperking, niet op te lossen:** alles wat de app meestuurt (URL,
-  headers, een eventuele "geheime" sleutel) staat letterlijk in de
-  gecompileerde app en is te achterhalen (APK decompileren, of het
-  netwerkverkeer onderscheppen met een proxy) — Cloudflare kan nooit
-  cryptografisch bewijzen "dit is echt de app", alleen filteren op
-  request-patronen.
-- **Aanbevolen, praktische aanpak (beste inspanning/opbrengst):**
-  1. **Rate limiting op Cloudflare** voor de API-paden (WAF → Rate
-     Limiting Rules, basisvarianten al op gratis/Pro-plan) — beperkt
-     volume per IP, ongeacht "wie" het is. Stopt het gros van scraping/
-     misbruik zonder identiteitsverificatie nodig te hebben.
-  2. Schrijf-acties (favorieten, inloggen, account verwijderen) blijven
-     achter de bestaande sessie+CSRF-auth — dat is al zo, geen actie
-     nodig. Alleen **lezen** (browse-endpoints) is bewust anoniem/
-     publiek (zie het geschrapte P0-5) en kan niet afgesloten worden
-     zonder de bedoelde openbare werking kapot te maken.
-  3. Optioneel, lage moeite: een vaste header-check als Cloudflare
-     Custom Rule (app stuurt altijd header X mee) — geen echte
-     beveiliging, maar filtert luie scrapers die alleen de kale URL
-     proberen.
-  4. De "echte" oplossing (App Check + Drupal-token-verificatie, zie
-     boven) pas bouwen als scraping/misbruik daadwerkelijk een probleem
-     blijkt, niet preventief.
-- **Volgende stap:** Bob zet rate limiting aan op Cloudflare voor de
-  API-paden (punt 1) — verder geen actie nu.
+**P1-30 · Eigenaar: Bob — klaar, aanzetten bij livegang.** Rate limiting
+op Cloudflare voor de API-paden. De regel is 2026-09-04 aangemaakt maar
+**bewust op Disabled gezet** (Bob: "is voor de live gang dit, nu niet").
+Bij livegang alleen nog op Active zetten.
+- Regel: `URI Path` starts_with `/nl/flutterdrup` **or** `/en/flutterdrup`,
+  300 requests per 1 minuut, gekenmerkt op IP. Niet lager zetten —
+  mobiele gebruikers delen IP's achter CGNAT.
+- ⚠️ **Blokkeert nu nog: de bestaande custom rule `flutterflow`** staat op
+  execution order **First** met actie **Skip** en heeft
+  **"All rate limiting rules"** (én "Rate limiting rules (Previous
+  version)") aangevinkt, op exact dezelfde twee paden. Custom rules
+  draaien vóór rate limiting rules, dus zolang die vinkjes aanstaan
+  slaat élk API-request de limiet over en doet deze regel niets. **Fix
+  bij het aanzetten: alleen die twee vinkjes weghalen**, de overige
+  skips (managed rules, Super Bot Fight Mode, Browser Integrity Check)
+  laten staan — die zijn er juist om de eigen app niet te hinderen.
+- Firebase App Check blijft bewust overgeslagen: dat beschermt alleen
+  Firebase-diensten, en de backend hier is Drupal — zou extra custom
+  Drupal-code vergen om het token te verifiëren. Pas overwegen als
+  misbruik een gemeten probleem wordt. Schrijf-acties zitten al achter
+  sessie+CSRF; lees-endpoints zijn bewust publiek.
 
 **P1-17 · Eigenaar: Bob (resterende 108 vertalingen, handmatig — zie
 controlelijst-artifact hieronder; de 7 gekoppelde-bug-sleutels en de
@@ -2064,53 +2105,43 @@ via verse export: alle 4 IconButtons in
 `header_buttons_component_widget.dart` gebruiken nu identiek
 `fillColor: Color(0xFFB50808)`. Uit deze lijst verwijderd.)*
 
-**P1-29 · Eigenaar: Bob — bezig (sessie 2026-09-04a, Drupal/infra-blok). (Drupal-content, geen Claude-taak — vermoedelijk
-al gefixt voor nieuwe content, oude content mogelijk permanent
-beschadigd).** Her-bevestigd 2026-08-20 (Claude, look&feel-review, live
-op zowel `emulator-5554` als `emulator-5556`): letterlijke `?`-tekens
-i.p.v. emoji in een evenementbeschrijving ("Mythic Fest II", Provincie
-→ Uitgaan-lijst) — bv. "??? ?????????  ?????? ????  Ontsnap aan het
-alledaagse, stap in het mythische!". Dit **zag er eerder al bekend uit**
-(P1-22, `decodeUtf8: true` op alle 11 live API-calls, afgerond
-2026-08-13) — maar staat dus nog steeds live, vandaag opnieuw gezien op
-een verse app-launch (niet een stale build zoals de eerdere
-"waarschijnlijk oude cache"-kanttekening bij P1-22). **Werkhypothese:**
-dit is geen teruggekeerde encoding-bug in de app, maar **al beschadigde
-content in de Drupal-database zelf** van vóór de decodeUtf8-fix — de
-fix voorkomt nieuwe corruptie bij het ophalen, maar herstelt geen
-tekst die al als kapotte `?`-tekens is opgeslagen. **Check bij Bob:**
-of het brontekstveld van dit specifieke evenement (of een steekproef van
-oudere evenementen) in Drupal zelf al `?`-tekens bevat i.p.v. emoji —
-zo ja, is dit alleen op te lossen door de content opnieuw in te voeren/
-te herstellen in Drupal, niet in de app.
-
-**Werkhypothese bevestigd (2026-08-23, Claude, losse `curl` op de live
-API, geen browser/builder nodig):** de **ruwe** Drupal-response zelf
-(`.../flutterflowmobiel1.json?display_id=services_1`, nid **`214370`**,
-"Mythic Fest II") bevat al letterlijke `?`-tekens **op byte-niveau**
-(`b'??? ????????? \xef\xb8\x8f ...'` — echte `0x3f`-bytes, geen
-UTF-8-multibyte-sequentie die de terminal verkeerd toont), gemengd met
-een overlevende `\xef\xb8\x8f` (de variatieselector-byte-sequentie van
-een emoji). Dit is **geen client-side decodefout** — `decodeUtf8: true`
-(P1-22) kan hier niets aan doen, de bron zelf is al kapot. **Concrete
-actie voor Bob:** het `inhoud`/`body`-veld van node **214370** in Drupal
-zelf bekijken/herstellen (handmatig de tekst opnieuw intypen/plakken met
-de bedoelde emoji) — geen app- of API-wijziging nodig.
-
-**Steekproef gedaan (2026-08-23, Claude, `curl` over alle 7
-`services_1`-t/m-`services_7`-displays × 4 pagina's, 700
-item-doorlopen, geen dubbele nid's):** dit is **geen eenmalig
-incident** — **7 nid's** bevatten hetzelfde kapotte-`?`-patroon
-(2+ opeenvolgende `?`, buiten URL's): `214370` (Mythic Fest II),
-`212672` (Danceworks Eindshow - locatie IJmuiden), `211780` (PubQuiz),
-`211789` (Slimste Team Quiz), `211798` (Disney Quiz), `211794`
-(PubQuiz), `211810` (Muziekbingo: ladies editie). **Puur een heuristiek**
-(kan enkele valse positieven/negatieven missen bij losse of andere
-`?`-patronen) — bedoeld als concrete startlijst voor handmatig herstel
-in Drupal, geen uitputtende audit.
-
-
----
+**P1-29 · Eigenaar: Bob — laag pitje (Bob's besluit 2026-09-04: "laat
+voor nu maar even liggen, ik denk ouwe meuk").** Letterlijke `?`-tekens
+i.p.v. emoji in evenementbeschrijvingen. **Root cause hard vastgesteld
+(2026-09-04, Claude, `curl` + codepoint-analyse over 235 live nodes van
+`flutterflow_events.json`):** dit is **geen app-bug** — de `?` staan als
+echte `0x3F`-bytes in de Drupal-database. Handtekening: bij nid `214370`
+staat `U+003F U+003F ... U+FE0F` en bij `214427` staat `?\u200D` — de
+emoji zelf is weg, maar de **variatieselector (U+FE0F) en ZWJ (U+200D)
+staan er nog**. Die zijn 3-byte en overleven; emoji zijn 4-byte en
+sneuvelen. Dat is exact het gedrag van **MySQL `utf8` (3-byte) i.p.v.
+`utf8mb4`**: 4-byte sequenties worden bij het *opslaan* door `?`
+vervangen. Geldt ook voor de "fancy" letters (`?????-???? ???????` =
+mathematical-bold, U+1D400-blok, eveneens 4-byte). `decodeUtf8` (P1-22)
+kan hier per definitie niets aan doen — er valt niets te decoderen, de
+bytes zijn weg.
+
+**De database is inmiddels in orde (Bob getest 2026-09-04):** een verse
+testnode met emoji behoudt zijn emoji. Bevestigd door nid `214439`
+("Brocante Markt Klein Frankrijk"), dat volledig intacte 4-byte emoji
+(`🗓📍🕘🎟🚗👧`) door de hele keten levert. Dus **geen charset-migratie
+meer nodig** — dit is historische schade.
+
+**Restpunt, alleen als het ooit terugkomt:** Bob's eigen vermoeden is dat
+de kapotte nodes via de **uksuite (Django) import** zijn binnengekomen
+i.p.v. via het website-formulier. Dat is een aparte databaseverbinding:
+schrijft Django zonder `charset=utf8mb4` in zijn connectie-opties, dan
+sneuvelen emoji alsnog ook al is de kolom mb4. **Zie je opnieuw `?`
+opduiken in geïmporteerde content: check daar eerst** (`DATABASES['default']['OPTIONS']`),
+niet in Drupal of de app.
+
+**Bekende beschadigde nodes** (niet herstelbaar — originele bytes zijn
+weg; enige route is de emoji met de hand opnieuw intypen): `214427`
+(Grindcore Inferno #4, 11-09-2026 — de enige nog actuele), `214370`
+(Mythic Fest II, 29-08-2026, verlopen), en uit een eerdere steekproef
+over `flutterflowmobiel1` services_1-7: `212672`, `211780`, `211789`,
+`211794`, `211798`, `211810` (quizzen, vermoedelijk alle verlopen).
+Omvang op de huidige agenda: 2 van 235 nodes.
 
 ## P2 — features & concept, na livegang
 
@@ -2224,8 +2255,53 @@ overgeslagen, Bob's eigen Sort/Datatype-experiment op die pagina loopt
 nog (zie P2-6); oppakken zodra dat is afgerond, zelfde recept
 (kopieer de AdBanner-node vanaf een van de 3 al werkende pagina's).
 
-**P2-6 · Eigenaar: Claude, akkoord van Bob (2026-08-25) om te
-starten.** Tekstzoeken op naam **én categorie**, per tab van
+**P2-6 · Eigenaar: Claude** (niet meer 'bezig' — de de-risk-test is
+gedaan, zie het kader hieronder; stappen 3/4/6 staan nog open).
+
+> **⚠️ RESULTAAT DE-RISK-TEST 2026-09-04 (Claude, op
+> `HorecagelegenhedenOverzichtCopy`, met Bob's akkoord). De destructieve
+> bug REPRODUCEERT NIET — maar er is wel een andere blokkade.**
+>
+> **Wat wél gewoon werkt:** een `TextField` toevoegen aan de `Column` van
+> **tab 0 (Activiteiten)**, via rechtsklik op de tree-rij → "Insert
+> Widget" → zoeken → kaartje aanklikken. Het veld landde als **sibling
+> ná** de `StaggeredView` (Insert Widget voegt dus achteraan toe, niet
+> vooraan zoals slepen dat doet). Verse export daarna: alle 6 tabs
+> aanwezig, alle 6 `horcat`-calls, alle 6 `HorecagelegenheidoverzichtKaartWidget`-
+> bindingen intact, en een `diff` tegen de export van vlak ervóór is
+> **puur additief: 0 verwijderde regels**. De live pagina is niet
+> aangeraakt (nog steeds 6 kaarten + zijn 2 al bestaande lege
+> `TextFormField`s). Er is dus **geen schade** en de "Column verliest
+> zijn kinderen"-bug van 2026-08-25 trad niet op.
+>
+> **Wat níet lukte:** hetzelfde doen op de `Column` van **tab 1
+> (Cultuur)**. Die zit binnen een `ConditionalBuilder` → `If`-tak, en
+> daar doet het aanklikken van het widget-kaartje in de Insert-dialoog
+> **niets**: de dialoog blijft gewoon openstaan, er wordt niets
+> toegevoegd, geen foutmelding. 3 pogingen, telkens met bevestigde
+> selectie van de juiste `Column` in het rechterpaneel. **Niet
+> destructief** — er gaat niets kapot, er gebeurt alleen niets.
+>
+> **Voorbehoud bij die tweede bevinding:** dezelfde grote Insert-modal
+> staat in `CLAUDE.md` al bekend als onbetrouwbaar voor
+> browser-automation, en de tree-klik-offset speelde tijdens deze
+> pogingen ook op (een rechtsklik landde 2x op de verkeerde rij). Het is
+> dus bevestigd "lukt Claude niet via automation", **niet** bewezen "kan
+> in FlutterFlow niet". In Bob's eigen browser is het het proberen waard.
+>
+> **Praktisch gevolg voor stap 3/4/6:** op deze pagina is **alleen tab 0
+> een kale `Column`**; tabs 1 t/m 5 zitten allemaal in een
+> `ConditionalBuilder`. Claude kan het zoekveld dus wel op tab 0
+> plaatsen, maar voor de andere vijf is óf Bob nodig, óf een andere
+> route. Widget-copy/paste is géén uitweg (bekend dood spoor, zie
+> `CLAUDE.md`).
+>
+> **Let op:** het testveld staat er nog. Er staat nu één losse,
+> ongebonden `TextField` onderaan tab 0 van
+> `HorecagelegenhedenOverzichtCopy`. Claude verwijdert niets (staande
+> regel), dus die laat ik staan — weghalen mag Bob doen, of hij blijft
+> gewoon staan want het is een dode kopie.
+ Tekstzoeken op naam **én categorie**, per tab van
 `horecagelegenheden_overzicht_widget.dart`/`..._provincie_page_widget.dart`
 (Bob's besluit: "op naam kunnen selecteren, tekstveld voor
 overeenkomstige namen", nodig op elke tab; uitgebreid met een
@@ -3830,10 +3906,28 @@ vervallen: Bob heeft `stadsactiviteitAanmakenCopy` en
 compilefouten uit het oude P2-23 verdwenen — die taak is geschrapt.)*
 
 
-**P2-18 · Eigenaar: Claude** (mechanisch, ±12 iconen; pas ná P0-11).
-Eén icoon per categorie in het menu: "Cultuur" en "Jeugd" hebben nu
-onder Provincie een ánder icoon dan onder Gemeente. Vervalt grotendeels
-als P0-11 de twee lijsten samenvoegt — daarom pas daarna oppakken.
+**P2-18 · Eigenaar: Claude** (mechanisch, klein). Menu-iconen
+gelijktrekken tussen het Gemeente- en het Provincie-blok in
+`drawer_component_widget.dart`. **Geïnventariseerd 2026-09-04 — het zijn
+3 verschillen, niet 2:**
+
+| Categorie | Gemeente-blok | Provincie-blok |
+|---|---|---|
+| Uitgaan | `music_note_outlined` | `music_note_outlined` ✅ |
+| Activiteiten | `flagCheckered` | `sports_score` ❌ |
+| Cultuur | `article_sharp` | **`piedPiperAlt`** ❌ |
+| Films | `movie_outlined` | `movie_outlined` ✅ |
+| Jeugd | `emoji_people` | `child` ❌ |
+| Horeca | `restaurant_sharp` | `restaurant_sharp` ✅ |
+
+`Icons.piedPiperAlt` is het Pied Piper-logo (FontAwesome, uit de serie
+Silicon Valley) — dat is los van consistentie gewoon fout en het meest
+urgente van de drie. Merk op dat `flagCheckered`, `piedPiperAlt` en
+`child` FontAwesome-namen zijn, de rest Material.
+
+**Stond eerder geparkeerd tot na P0-11** ("vervalt grotendeels als de
+twee lijsten samengevoegd worden") — die samenvoeging is 2026-09-04 door
+Bob afgewezen, dus deze taak blijft volledig staan.
 
 **P2-19 · Eigenaar: Bob.** `nav.dart`:129 stuurt een ingelogde
 gebruiker naar het provincie/gemeente-keuzescherm en een uitgelogde