CLAUDE.md 14 KB

Uitgaanskrant — werkinstructies voor Claude

Werkdirectory

Werk uitsluitend binnen deze projectdirectory (/home/bob/Projects/ff-app/uitgaanskrant-1qhvtd). Niet daarbuiten zoeken of scannen (bijvoorbeeld geen find / over het hele bestandssysteem) — scope alle bestandsoperaties tot dit project.

Sessiegeheugen — lees dit eerst

Bob bespreekt elke taak in een nieuwe, aparte chat. Dit bestand en TASKS.md zijn daarom het geheugen tussen sessies — er is geen eerdere conversatie om op terug te vallen.

Vaste afsluitroutine, aan het eind van elke sessie/taak:

  1. Werk TASKS.md bij: een afgeronde taak wordt volledig verwijderd uit de lijst (niet gearchiveerd in een "Afgerond"-sectie — dat is onnodige context/kosten voor toekomstige sessies). Zorg dat elke resterende open taak zelfstandig te begrijpen is (concreet, met bestandspad/context) zonder de chat gelezen te hebben waarin hij ontstond.
  2. Werk dit bestand (CLAUDE.md) bij: alleen toevoegen als het een blijvend herbruikbaar inzicht is (een conventie, een architectuurkeuze, een bekende valkuil/bug-patroon) dat een latere sessie anders opnieuw zou moeten uitzoeken. Geen sessieverslag, geen changelog van wat er gedaan is, geen herhaling van redeneringen. Ruim verouderde/dubbele info op in plaats van eronder te plakken — dit bestand moet klein en scanbaar blijven.
  3. Commit + push de gewijzigde .md-bestanden direct (geen FlutterFlow-export nodig voor pure documentatiewijzigingen — alleen de export/build-cyclus hieronder draaien als er ook echt app-code is gewijzigd).

FlutterFlow-workflow — belangrijk

Dit project wordt gebouwd via FlutterFlow (app.flutterflow.io). De FlutterFlow-cloudomgeving is de bron van waarheid, niet deze lokale code-export.

  • De gebruiker kan geen code rechtstreeks genereren of bewerken in dit repo. Alle wijzigingen aan pagina's, componenten en modellen moeten via de FlutterFlow-website gebeuren.
  • De enige uitzondering is custom functions/widgets (lib/custom_code/) — en ook die voert de gebruiker in via de Custom Code-editor op de FlutterFlow-website, niet door hier rechtstreeks bestanden te bewerken.
  • Gevolg: directe Edit/Write-wijzigingen aan gegenereerde bestanden (pagina's, blocks, components, models, lib/flutter_flow/* e.d.) worden bij de volgende export/sync vanuit FlutterFlow overschreven. Zulke bestanden hier aanpassen is dus niet duurzaam.
  • Werkwijze: gebruik deze lokale repo om code te lezen, logica te ontwerpen en te verifiëren (bijv. met flutter analyze) — maar vertaal het eindresultaat naar concrete, stapsgewijze instructies voor wat de gebruiker in de FlutterFlow-builder (of de Custom Code-editor) moet doen, of voer het zelf uit via browser-automation (zie hieronder). Ga er niet van uit dat een lokale bestandswijziging behouden blijft.
  • Als er via de Chrome-browser (Claude in Chrome) rechtstreeks in de FlutterFlow-builder wordt gewerkt: maak na elke afgeronde taak (niet na elke losse klik/handeling) een commit binnen FlutterFlow zelf (het eigen versiebeheer, te zien bovenin de builder als "main"/"Synced"), met een duidelijke omschrijving van wat er is veranderd. Dit is geen lokale git commit.
  • Gebruik mcp__claude-in-chrome__* (Bob's eigen, gedeelde Chrome), niet de in-app Browser pane — dat is de afgesproken tool voor builder-automation.
  • Resize de browser niet zelf (resize_window e.d.) — dit is Bob's eigen gedeelde vensters. Als iets buiten beeld valt, meld dat en vraag het aan Bob i.p.v. zelf te resizen.
  • Bob werkt vaak gelijktijdig zelf in dezelfde builder-sessie. Ga er niet van uit dat elke wijziging die je in de widget tree/git diff ziet van jezelf komt — check eerst of hij iets claimde ("dat regel ik zelf") voordat je het overschrijft.
  • Check bij een mislukte export/pull eerst het Issues-paneel (badge-icoon rechtsboven, naast de sync-checkmarks) voordat je een bug bij jezelf zoekt — een rode teller daar blokkeert elke export, ook voor niet-gerelateerde correcte wijzigingen, en is vaak Bob's eigen work-in-progress.

Lokale git-repo + pull/push-workflow

De projectdirectory zelf is een schone git-repo met origin op ssh://gogs.digitalforce.tv:2222/Uitgaanskrant.com/flutterflow.git, branch master. Dit is een eigen repo van de gebruiker, los van de grotere/rommelige repo die hoger in de mappenstructuur staat (die met AndroidStudioProjects, Flutter-SDK-installaties e.d.) — commits en pushes horen hier, in de projectdirectory zelf.

Vaste workflow na elke afgeronde taak (staand akkoord, geen aparte bevestiging per keer nodig): niet los flutterflow export-code draaien, maar het bestaande script gebruiken:

export PATH="/home/bob/fvm/bin:$HOME/.pub-cache/bin:$PATH" && /home/bob/Projects/ff-run-fvm.sh emulator-5554 uitgaanskrant-1qhvtd -s
  • De export PATH=... prefix is verplicht. De Bash-tool draait niet-interactief, dus ~/.bashrc (waar fvm/flutterflow normaal op PATH komen) wordt niet geladen. Zonder deze prefix faalt het script stil met "fvm: command not found" / "Prerequisites not met, skipping execution" — geen harde error, dus makkelijk te missen.
  • Dit script is interactief (device-run + hot-restart-loop) — moet via Bash met run_in_background: true. Volg de output tot minimaal "All done!" (export gelukt) én idealiter een succesvolle app-launch op de emulator (Launching lib/main.dart..., geen nieuwe EXCEPTION CAUGHT BY RENDERING LIBRARY t.o.v. bekende issues in TASKS.md) voordat je verder gaat.

Daarna automatisch:

  1. git status --short bekijken, dan git addniet blind -A. Voeg alleen daadwerkelijke FlutterFlow/app-wijzigingen toe (lib/, android/, ios/, pubspec*, .gitignore e.d.). Sluit .claude/ uit (staat in .gitignore) — dat is sessiestate van Claude Code, geen app-code. Wijzigingen die niet van jezelf zijn (Bob's eigen concurrent werk) horen gewoon mee in dezelfde commit — dit is één gedeeld project.
  2. git commit met een duidelijke boodschap.
  3. git push (of git push -u origin master als tracking nog niet staat).

Valkuil: flutterflow export-code overschrijft .gitignore bij elke export terug naar FlutterFlow's eigen standaardversie. Voeg dus na elke export, vóór je staged, deze regel weer toe aan .gitignore als hij ontbreekt:

# Claude Code session state (not app code)
.claude/

(CLAUDE.md zelf overleeft een export altijd — alleen bij een volledig verwijderde en opnieuw gepulde projectmap zou het verdwijnen. Check voor de zekerheid toch even of het bestand er nog is.)

Eerst research, dan bouwen

Voordat je aan een niet-triviale taak begint (een bugfix, een nieuw patroon, een integratie): zoek eerst kort op internet naar bestaande oplossingen/patronen voordat je het zelf helemaal uitvindt via trial-and-error in de builder. Geldt niet voor triviale/mechanische herhaling van een patroon dat al bevestigd werkt (zie hieronder).

FlutterFlow-builder: bekende problemen & patronen

Geneste "Set Variable"-dialoog kan lijken vast te zitten. Bij het instellen van een conditie (ConditionalBuilder, Visibility → Conditional) op een niet-triviaal type (JSON Path, API-response-veld) opent een tweede dialoog bovenop de eerste. Confirm/Cancel op die binnenste dialoog reageren soms niet zichtbaar. Twee bekende oorzaken, in volgorde van proberen:

  1. Viewport-clipping — de knoppen renderen buiten het zichtbare canvas, niet echt vast. Sleep de dialoog omhoog via het handvat bovenin (kleine grijze balk) naar een hogere y-positie; de Confirm/Cancel-knoppen worden dan zichtbaar en werken gewoon.
  2. Echt bevroren pagina (alle clicks, ook op onbetrokken plekken, doen niets) — de widget-wrap zelf is al server-side opgeslagen, maar de conditie-edit niet. Los op door de pagina te herladen (navigate naar dezelfde URL); de wrap blijft staan, de half-ingevulde conditie moet opnieuw.
  3. Kortere weg om deze dialoog te vermijden: gebruik als operator "Is Set" in plaats van "Not Equal To" + lege string — dan is er geen Second Value nodig en dus geen tweede geneste dialoog.
  4. Als dit na 1-2 pogingen (incl. de reload-poging) nog vastloopt: dit kost dan meer tijd dan Bob het zelf kan doen — meld het concreet (component, precieze stappen) en vraag het aan hem i.p.v. te blijven proberen.

Component Name kan per ongeluk overschreven worden. Vlak na paginanavigatie kan een klik bedoeld voor het "Search properties..." veld in plaats daarvan op het Component Name-veld landen (focus/ z-order race condition), en typen hernoemt dan stilletjes het hele component. Mitigatie: na een click-before-type altijd eerst een screenshot om focus te bevestigen, zeker vlak na navigatie. Herstel: rechtsklik component in de zoekresultatenlijst → "Rename Component".

Rechterpaneel kan te breed zijn voor de viewport. Sommige controls (Expansion segmented control, Visibility → Conditional expression builder) renderen soms deels buiten beeld — dit is geen resize_window-probleem (zie hierboven: niet zelf resizen). Na 1-2 bevestigde pogingen stoppen en aan Bob vragen.

Patroon: lege/ontbrekende afbeeldings-URL laten crashen. CachedNetworkImage gooit een synchrone ArgumentError bij het bouwen van de widget als imageUrl een lege string is — dit gebeurt vóórdat er ooit een netwerkverzoek wordt gedaan, dus errorWidget/ "Show Error Image on Failure" vangt dit niet (dat vangt alleen échte laadfouten zoals 404's). Werkende fix, toegepast op 9 van de ~10 bekende instanties (zie TASKS.md):

  1. Rechtsklik het Image-widget → Wrap Widget (Ctrl+B)ConditionalBuilder.
  2. IF-conditie: Conditions → Single Condition → First Value = de exacte expressie waar de Image's Path-property al aan gebonden was (component-parameter, JSON Path, of custom-function-resultaat via "Evenement Response" → API Response Options → JSON Body → JSON Path, bv. $[0].logo) → operator "Is Set".
  3. THEN-tak: de bestaande Image (blijft staan na de wrap).
  4. ELSE-tak: rechtsklik → Insert Widget → Icon → zoek "image not supported" → eerste Material-resultaat.
  5. Simpeler alternatief indien van toepassing: de "Default Variable Value"-toggle op een willekeurige "Set from Variable"-binding substitueert al bij zowel null als lege string (bevestigd via in-builder tooltip: "if the resulting value is null or empty") — géén ConditionalBuilder nodig. Werkt hier alleen niet voor Image-widgets met Image Type: Network, omdat er nog geen gehoste fallback-afbeeldings-URL bestaat in dit project. Mocht die er ooit komen (bv. een default-logo op de FlutterFlow-CDN of Drupal-server): gebruik dan Default Variable Value + "Show Error Image on Failure" i.p.v. ConditionalBuilder — sneller te bouwen.

Tooltip toevoegen aan een icon-only widget: rechtsklik → Wrap Widget (Ctrl+B) → 4e rij van de grid (kan geclipt lijken) → 1e icoon (spraakwolkje) = "Tooltip". Genereert een AlignedTooltip (zelfde stijl als de gedeelde header), geen losse styling nodig. Werkt niet op iconen die embedded zitten als suffixIcon van een TextFormField (bv. Login-pagina's wis-/toon-wachtwoord-iconen — geen losse wrapbare tree-node).

API-call headers: check op hardcoded literals i.p.v. [varname] templates. Als een API-call via curl wél werkt maar vanuit de app/ Response & Test-panel niet, controleer het Headers-tabblad van de call-configuratie — een handmatig ingeplakte testwaarde (bv. een Cookie-header) kan per ongeluk blijven staan i.p.v. de [sessionname]=[sessionid]-template. Check ook het per-variabele "Include"-vinkje in Response & Test — staat die uit, dan wordt de letterlijke [varname]-tekst meegestuurd i.p.v. de testwaarde.

Domein/architectuurcontext

  • Drupal 7 Services sessie-auth: sessid + session_name (uit LoginCall's response) vormen samen de sessie-cookie: Cookie: <session_name>=<sessid>. token is een los CSRF-token, alleen nodig bij schrijf-requests (POST/PUT/DELETE), nooit bij GET.
  • Drupal page cache bootstrapt vóór de sessie — een ooit anoniem gecachete response (bv. een 403) kan session-bootstrap, hooks én custom-module-logging volledig overslaan. Bij twijfel: Drupal-cache legen en opnieuw testen vóór je verder debugt.
  • Views numeric filter: $view->filter['uid']->value moet array('value' => $uid) zijn, niet array($uid) — de foute vorm faalt stil (geen filter toegepast) i.p.v. een error te geven.
  • Provincie/gemeente is en blijft het basismodel voor content-scoping (bevestigd door Bob: mensen zoeken primair lokaal). Home is de landelijke standaard-startpagina zónder verplichte gemeente-keuze vooraf (task #31, afgerond) — dat vervangt niet het provincie/ gemeente-model, het is een aanvullende ingang.
  • Favorieten/login zijn P0. Backend-endpoint bestaat al; Drupal 7 views die de respons voeden hebben soms nog aanpassing nodig.

Samenwerken met Bob

  • Bob is de enige developer/eigenaar en werkt vaak zelf gelijktijdig in dezelfde FlutterFlow-builder-sessie. Neem niet aan dat elke wijziging van jou komt.
  • Prioriteit: "eerst een werkende app live, daarna features" — P0 (blokkeert livegang) weegt zwaar boven P1 (polish/performance) en P2 (nieuwe features). Bob herprioriteert soms fors zelf (bv. login+ favorieten van P2 naar P0) — volg dat exact.
  • Als Bob een taak terugclaimt ("dat regel ik zelf") — stop daar direct mee en pak iets anders onafhankelijks op.
  • Als iets veel tijd kost door handmatige builder-acties (vastzittende dialogen, geclipte controls e.d.): na 1-2 serieuze pogingen stoppen en concreet aan Bob voorstellen dat hij het zelf doet (met exacte stappen) i.p.v. door te blijven proberen.
  • Browser niet zelf resizen (zie boven).
  • Rapporteer nieuw gevonden bugs (vooral op een pagina waar Bob net zelf op zit) direct en duidelijk, niet pas in een latere samenvatting.