# Demo-testaccounts

> **Status:** ✅ Live (gebouwd, bereikbaar op genoemde route) · 🟡 Gedeeltelijk (werkt, met benoemd gat) · 🔧 In uitvoering · 📋 Gepland (met fase) · 💤 Bewust uitgesteld / non-goal

Zes accounts in vier huishoudens, bedoeld om de app scherm voor scherm te
beoordelen in de iOS-simulator. Aangemaakt en beheerd met
`scripts/seed-demo-accounts.sh`.

## Inloggen: hoe dat écht werkt

De app is **passwordless** — het inlogscherm vraagt alleen een e-mailadres en
stuurt een magic-link. Voor deze accounts kan die link nooit aankomen, want
`demo.tendlo.test` bestaat met opzet niet. Er is daarom een **test-only
wachtwoordpad** achter een dart-define:

```sh
scripts/flutter-with-defines.sh run -d <simulator-id>
```

Dat script zet `--dart-define=DEMO_LOGIN=true`. Alleen dán toont het
inlogscherm een wachtwoordveld en een knop **"Log in met wachtwoord"** onder de
gewone "Stuur inloglink". In de app: **Instellingen → Inloggen / account
aanmaken**, dan het e-mailadres uit de matrix hieronder plus:

**Wachtwoord voor alle accounts: `TendloDemo2026!`**

Een build die niet door dit script gaat — elke store- of TestFlight-build — zet
de define niet en is byte-voor-byte het gewone magic-link-scherm. Dat is
vastgelegd in `test/features/auth/login_screen_test.dart` (groep `DEMO_LOGIN`).

<details>
<summary>Terugvalroute zonder de dart-define</summary>

Werkt de define niet (oude build, ander startcommando), dan kun je de
PKCE-code met de hand ophalen:

1. Tik in de app op **"Stuur inloglink"**. Dit moet je écht doen: de app slaat
   de `code_verifier` lokaal op, dus een van buitenaf gegenereerde link werkt
   niet — de flow moet altijd in de app zelf beginnen.
2. Lees de zojuist aangemaakte code:
   ```sh
   scripts/prod-sql.sh "select auth_code from auth.flow_state order by created_at desc limit 1"
   ```
3. Open de callback in de simulator:
   ```sh
   xcrun simctl openurl booted "io.supabase.tendlo://login-callback?code=<auth_code>"
   ```

Omslachtig en foutgevoelig; gebruik het alleen als de define-route niet kan.
</details>

Het wachtwoord bestaat verder **alleen server-side**, gezet door
`seed-demo-accounts.sh`, en is daarnaast bruikbaar voor directe REST-/SQL-
toegang (`/auth/v1/token?grant_type=password`).

## Waarom deze accounts bestaan

Bij het beoordelen van schermen in Claude Desktop gaan schermafbeeldingen van de
simulator naar Anthropic. De documentatie zegt daar expliciet over: *"don't sign
in to real accounts on a device Claude uses."* Deze accounts bestaan zodat dat
niet hoeft — er staat uitsluitend verzonnen data in, en het domein
`demo.tendlo.test` bestaat niet, dus er kan nooit echte post naartoe.

## De matrix

| Account | Naam | Huishouden | Rol | Tier | Modules | Waarvoor |
|---|---|---|---|---|---|---|
| `vrij@demo.tendlo.test` | Vera | Demo Vrij | eigenaar | **gratis** | 5 | Hoe voelt de gratis versie, en waar botsen de Plus-grenzen? |
| `solo@demo.tendlo.test` | Sam | Demo Solo | eigenaar | Plus | **29 (alles)** | Het werkpaard: elk scherm bereikbaar en gevuld |
| `gezin-ouder@demo.tendlo.test` | Gijs | Demo Gezin | eigenaar | Plus | 11 | Delen, rechten, beheerdersacties |
| `gezin-partner@demo.tendlo.test` | Puck | Demo Gezin | partner | Plus | 11 | Wat ziet een tweede ouder wél en niet? |
| `gezin-kind@demo.tendlo.test` | Kim | Demo Gezin | kind | Plus | 11 | Rolgebonden schermen: zakgeld, kleedgeld, klusjes |
| `minimaal@demo.tendlo.test` | Mees | Demo Minimaal | eigenaar | Plus | 1 | Lege staten; blijft de app logisch zonder modules? |

De vier kern-oppervlakken — Vandaag, Taken, Agenda en Boodschappen — zijn geen
registry-modules en staan altijd aan, ook bij Mees.

### Waarom deze zes en niet meer

De drie assen die het gedrag echt sturen zijn **tier**, **gezin of alleen**, en
**hoeveel modules aan staan**. Alle combinaties zou 12+ accounts geven waarvan de
meeste hetzelfde laten zien. Deze zes dekken elke as minstens één keer, met het
gezin als enige plek waar rollen en delen samenkomen — precies waar de meeste
bugs zitten.

## Gebruik

```sh
scripts/seed-demo-accounts.sh              # aanmaken of bijwerken (idempotent)
scripts/seed-demo-accounts.sh --list       # tonen wat er staat
scripts/seed-demo-accounts.sh --teardown   # alles weghalen
```

De teardown filtert op `@demo.tendlo.test` en kan daarom nooit een echt account
raken. Demodata staat in een apart script (`seed-demo-data.sh`) zodat je de
inhoud kunt verversen zonder de accounts opnieuw te maken.

## Valkuilen die erin verwerkt zitten

Deze kostten eerder uren; ze staan in het script zelf toegelicht.

**`auth.users.confirmed_at` is een gegenereerde kolom.** Er direct in schrijven
laat de hele UPDATE falen. Bevestigen doe je uitsluitend via `email_confirmed_at`.

**Een lid is niet naar een ander huishouden te verplaatsen.** De trigger
`members_lock_identity` maakt `household_id` onveranderlijk. Een gezin bouw je
daarom via `pending_invites` vóórdat de gebruiker bestaat; de
`handle_new_user`-trigger pakt die invite op bij de INSERT.

**Het signup-endpoint werkt niet voor wegwerpadressen.** De standaard-SMTP van
Supabase mailt alleen teamleden, dus signup geeft een 500 en rolt de gebruiker
terug. Users worden daarom rechtstreeks via SQL aangemaakt.

**Laat je de varchar-tokenkolommen van `auth.users` op NULL staan, dan faalt
inloggen** met een misleidende `500 Database error querying schema`. Dat leest als
een schema- of rechtenprobleem, maar GoTrue leest die kolommen simpelweg als
niet-nullable strings. Echte, via de app aangemaakte accounts hebben daar lege
strings. Ontdekt op 29-07; het script zet ze nu zelf goed.

## Demodata

Geseed met `scripts/seed-demo-data.sh` (los van het accountscript, zodat je de
inhoud kunt verversen zonder opnieuw in te loggen). **290 rijen** verdeeld over de
modules die per huishouden aan staan: Demo Solo 165, Demo Gezin 71, Demo Vrij 37,
Demo Minimaal 17.

Per module 4-8 rijen met bewuste variatie — iets van vandaag, iets uit het
verleden, iets ver vooruit, korte én lange titels, items met en zonder optionele
velden, en bedragen in verschillende frequenties zodat de omrekening zichtbaar is.

```sh
scripts/seed-demo-data.sh              # vullen (idempotent)
scripts/seed-demo-data.sh --list       # rijtelling per huishouden en module
scripts/seed-demo-data.sh --check      # de vier controles
scripts/seed-demo-data.sh --teardown   # data weg, accounts blijven
```

### Bewuste randgevallen

- **Kim ziet één van haar eigen klusjes niet.** "Tafel afruimen" is aan haar
  toegewezen maar de deelkring is Gijs en Puck — het override-geval uit TEN-122.
  Drie andere taken van Kim zijn wél normaal zichtbaar, dus beide standen staan
  naast elkaar.
- **Boodschappen in Demo Gezin is gezinsbreed**, omdat er maar één actieve
  boodschappenlijst per huishouden mag bestaan.
- **Demo Solo staat volledig op privé** — er is niemand om mee te delen.

### Nog leeg

Stappenteller onder Beweging (het enige gat in een aangezette module),
verlanglijst, betaalpassen, en de betaald-markeringen van vaste kosten. Bijlagen
en bonnetjes zijn bewust overgeslagen: daar zijn echte bestanden voor nodig.

### Het echte huishouden is niet aangeraakt

Voor en na het seeden zijn alle 64 tabellen met een `household_id` geteld voor
huishouden "Duifje": **2613 rijen, ongewijzigd**, ook na een teardown en een
volledige reseed. Elke `DELETE` in het script is gefilterd op de vier
demo-huishoudens, en er staat een guard vooraan die afbreekt als die vier er niet
exact zijn.

---
*Bronnen: `scripts/seed-demo-accounts.sh`; `public.handle_new_user`-trigger; memory `e2e-throwaway-accounts`; https://code.claude.com/docs/en/desktop-ios-simulator. Geverifieerd op 2026-07-29: alle zes accounts geven HTTP 200 op `/auth/v1/token?grant_type=password` — dat is de REST-API, **niet** de app. De app-kant liep tot 2026-08-05 vast omdat er geen wachtwoordpad in het inlogscherm zat; dat is opgelost met `DEMO_LOGIN` (TEN-152).*
