# Beslislog — 25 juli 2026 (tester-feedback golf 6 + TestFlight-voorbereiding)

Autonome batch. Robin was 's avonds weg; alles wat een **eigenaar-beslissing** vereist
staat onderaan onder **"Voor jou, 's ochtends"**. De rest is afgehandeld en geverifieerd.

---

## 1. Wat er is gebeurd

- **Verrijkt:** 4 nieuwe tickets (TEN-114/120/121/122) en 5 her-verrijkt na jouw comments
  (TEN-59/114/119/120/122), plus TEN-127 volledig herscoped na je per-lid-besluit.
- **Nieuw aangemaakt:** TEN-123 t/m TEN-129 — uit splitsingen en uit bevindingen die
  buiten de scope van hun bronticket vielen.
- **Geshipt:** 16 tickets gefixt, gemerged en door de merge-gate. De suite ging van
  3141 naar **3409 tests**, analyze steeds schoon, poort gedraaid na élke merge.
- **Migraties:** 6 stuks naar prod gedeployd en geverifieerd.
- **Builds:** op 4 van de 5 toestellen geïnstalleerd.
- **Hertestpagina:** bijgewerkt en gepubliceerd — 34 nog te valideren, 44 bevestigd.

---

## 2. Geshipt naar main → Testen

| Ticket | Pri | Wat er nu anders is |
|---|---|---|
| TEN-43 | High | Kind kan eigen bon wissen; ouder/partner kan bon én wens van een kind wissen (server-carve-out, migratie 0078) |
| TEN-47 | Low | Loze ruimte onder de titel weg bij rapport/vakantie: 37 dp → 9 dp |
| TEN-59 | High | Taak toewijzen deelt standaard mee met die persoon; handmatig intrekken blijft staan |
| TEN-89 | High | Herhaling uit- en weer aanzetten blokkeerde het opslaan; RPC-preconditie versoepeld (0077) |
| TEN-101 | Low | Winkelkeuze start neutraal op "Kies winkel" i.p.v. de laatst gebruikte winkel |
| TEN-112 | Medium | Eén kaart per voertuig i.p.v. drievoudige duplicatie + per-voertuig meldingstermijn (0074) |
| TEN-114 | High | Abonnement: frequentie, betaalmaand, rekening en opzegtermijn-keuzeset (0079) |
| TEN-120 | Medium | Vastgoed als bolletjes per object in de dagkop i.p.v. volle dagbanden; langlopend contract geeft 3 momenten i.p.v. 365 (0076) |
| TEN-121 | High | Verjaardagen en afval altijd bovenaan de dag, met een deterministische volgorde |
| TEN-122 | High | Zelfde regel als TEN-59, maar voor agenda-afspraken |
| TEN-124 | High | Vakanties die niet gezinsbreed staan verschijnen nu wél in je eigen agenda |
| TEN-125 | High | Vaste kost van type abonnement verschijnt automatisch in de Abonnementen-module |
| TEN-126 | Medium | Verzekeringen: opzegtermijn kan nu ook langer dan 3 maanden (0081) |
| TEN-127 | High | Lijst verbergen is nu een persoonlijke, gesynchroniseerde voorkeur |
| TEN-128 | High | Verbergen raakt alleen het lijstjes-overzicht; Vandaag/AI/inbox blijven |
| TEN-129 | High | Privé vastgoedobject lekt de gastnaam niet meer naar andere gezinsleden |

## 3. Migraties live op prod

`0074` (vehicles.reminder_lead) · `0076` (vastgoed verhuurtype, 4 kolommen) ·
`0077` (replan-preconditie) · `0078` (kleedgeld-verwijderrecht) ·
`0079` (abonnement-betaalvelden, 5 kolommen) · `0081` (verzekering-opzegtermijn, 3 kolommen).

Alle zes HTTP 201 en na afloop geverifieerd; 0077 apart gecontroleerd op de functiebody,
omdat kolomtellingen daar niets zeggen. `0075` en `0080` zijn **bewuste gaten** — die
tickets bleken geen migratie nodig te hebben. Niet later opvullen: migratietooling slaat
een lager nummer dat achteraf verschijnt mogelijk over.

## 4. Drie fouten die stil waren gebleven

Deze verdienen aparte vermelding, want ze hadden geen van drieën een foutmelding gegeven.

1. **De hertestpagina werd niet gegenereerd.** `docs/retest/generate.py` zocht een marker
   die niet meer in de HTML stond, deed dus niets, en meldde tóch succes doordat zijn
   controle een substring-check was (`"[]" in payload` is waar zodra één item een lege
   lijst heeft). Het script meldde "29 item(s)" terwijl `git status` geen wijziging zag.
   De controle is nu hard; het script faalt met uitleg i.p.v. stil te slagen.
2. **De statusmap op die pagina miste 11 tickets.** TEN-89/114/120/121/122/124/125/126/
   127/128/129 stonden er niet in en vielen daardoor in géén enkel vak — onzichtbaar voor
   de testers, ook als de pagina wél gedeployed was. Vijf andere stonden nog op Triage.
3. **Twee `flutter drive`-runs op één simulator botsen.** Beide agents installeren
   hetzelfde bundle id, dus de tweede driver hangt aan de app van de eerste en rapporteert
   diens testfouten. Een agent kan daardoor een groene fix als rood melden. Runtime-
   verificatie moet voortaan serieel.

## 5. Bewuste afwijkingen door fixers (allemaal goed onderbouwd)

- **TEN-126** appendde géén `onbekend` aan de bestaande opzegtermijn-enum, maar zette de
  markering in een derde nullable kolom. Reden: al geshipte builds lezen die kolom met een
  niet-defensieve intEnum, dus een extra index geeft een RangeError en een gebroken lijst
  op een niet-bijgewerkt toestel. Nu degradeert een oude build zichtbaar i.p.v. te crashen.
- **TEN-125** weigerde een auto-stub voor verzekeringen: die zou een verlengdatum én een
  opzegtermijn moeten *verzinnen*, en beide voeden een echte opzeg-herinnering — precies de
  klasse bugs die TEN-114 net wegnam.
- **TEN-43** bouwde géén cross-owner delete-RPC: verwijderen is een soft-delete en
  `auth.uid()` blijft binnen een definer-functie de caller, dus de trigger-carve-out was
  hoe dan ook nodig. Netto minder code, en offline verwijderen blijft werken.
- **TEN-120** schoof de opzegtermijn-band van fase B naar fase A, omdat de termijn-
  herinnering voor de Ligplek de functionele kern van de vraag is.

---

## 6. Voor jou, 's ochtends — beslissingen

### A. Vastgoed fase 2: wordt Vastgoed "Vermogen"? ⛔ blokkeert drie tickets

TEN-87, TEN-118 en TEN-111 staan op Todo, maar hun verrijkingen zeggen alle drie
"nog niet starten". TEN-87 is volgens de verrijking **een programma, geen ticket**:
meerdere migraties, een nieuwe rekenlaag, AI-import en nieuwe overzichtsschermen. Eronder
ligt een productkeuze: wordt de module hernoemd/gepositioneerd van Vastgoed naar Vermogen?

- **TEN-118** (kosten bij vastgoed) hoort volgens het advies in die fase 2 gebundeld, niet
  los opgepakt.
- **TEN-111** heeft een kleine veilige subset die nu al kan; de rest hoort onder TEN-87.

**Mijn advies:** knip TEN-111's kleine subset eraf en laat die morgen bouwen; parkeer
TEN-87 en TEN-118 tot je de Vermogen-vraag hebt beantwoord. Dan staat er niets stil op iets
wat eigenlijk een productbesluit is.

### B. TEN-120: de bolletjes zijn af, de icoontjes nog niet 🟡

Ik heb de screenshots zelf bekeken in licht én donker. De kern is opgelost: `09:00 Hwc`
staat weer bovenaan en de Ligplek geeft twee momenten in plaats van 365 dagbanden. De
dag-sheet is goed — avatar in de objectkleur, object, gast, "t/m 31 jul 2026".

Wat niet goed genoeg is: op een dag met meerdere gebeurtenissen zie je `● →] ● [→ ●`, en de
ruimte binnen een paar is nauwelijks kleiner dan die tussen de paren. Je kunt dus niet zien
welk icoontje bij welk bolletje hoort, en in donkere modus zijn die icoontjes gedempt grijs.
Op armlengte durf ik niet te zeggen dat je check-in van check-out onderscheidt.

**Mijn advies:** één kleine iteratie — bind het icoontje ondubbelzinnig aan zijn bolletje
(badge óp de cirkel, of de paren behouden met duidelijk grotere tussenruimte).
Screenshots staan klaar in de sessiemap als je zelf wilt kijken.

### C. TEN-122: backfill van bestaande agenda-afspraken? 🟡

Bestaande afspraken staan op owner-only. De nieuwe derivatie leest dat als een *bewuste*
override, dus ze genezen alleen als je er een expliciete deel-keuze op maakt. Een backfill
is een **data-remap** en valt niet onder je staande akkoord voor additieve migraties.

**Mijn advies:** niet doen. Het is de testfase, het gaat om een handvol rijen, en de
SQL-schets staat op het ticket als je later toch wilt.

### D. Panel-review op de twee riskantste wijzigingen? 🟡

TEN-89 versoepelt een server-side guard en TEN-122 degradeert een harde invariant (TEN-1)
bewust naar een default. Beide zijn goed getest — TEN-89 met 12 harnas-asserties tegen
echte Postgres, TEN-122 runtime op toestel — maar dit is precies het soort wijziging
waarvoor jullie panel-review bestaat.

**Mijn advies:** doe het voor deze twee, niet voor de rest van de golf.

### E. TEN-123: hele-dag-afspraak — twee open ontwerpkeuzes 🟡

Staat in Triage. De hele onderlaag ondersteunt all-day al; het is puur een UI-gat. Twee
vragen: (1) komt er ook een **einddatum** zodat een meerdaags bezoek in één afspraak past,
en (2) wat doet de melding bij een dag-item? Nu zou die om 00:00 vallen; module-spiegels
zetten daar bewust "geen melding".

### F. TEN-119: laten of heropenen? 🟡

Advies van de her-verrijking: **Canceled laten**. De oorspronkelijke vraag is bedoeld
gedrag, en de oplossing loopt nu via TEN-127 en TEN-128. Heropenen is alleen zinnig als je
de boodschappenlijst **standaard** verborgen wilt — dat is een default-wijziging, geen
mechanisme-wijziging.

### G. TEN-70: er is een vraag aan Laura nodig ⛔ blokkeert dat ticket

De "klaar vandaag"-bak filtert in de code juist strak op vandaag; de done-filter lekt niet
aantoonbaar. Er moet bij Laura bevestigd worden of die items écht niet vandaag zijn
afgevinkt. Stel jij die vraag, of zal ik hem als comment op het ticket zetten?

### H. Wenslijst-affordance 🟡

Bonnen kregen een expliciete verwijderknop, wensen houden hun long-press. Het recht is
gelijk, de vindbaarheid niet — een ouder ontdekt die long-press waarschijnlijk niet.
Klein werk, maar het is een UI-keuze.

### I. Laura's telefoon staat nog zonder build ⛔

De installatie faalde op een vergrendeld toestel (`kAMDMobileImageMounterDeviceLocked`).
Ontgrendel hem en houd hem op het netwerk, dan zet ik hem er in één poging op. Relevant,
want TEN-89 is Laura's eigen melding en zij kan die anders niet hertesten.

### J. TestFlight — de build is klaar, drie dingen vragen jouw besluit

**Goed nieuws eerst: er is geen signing-blokkade.** `build ipa` met export-method
`app-store` liep volledig door. Er ligt een upload-klare `build/ios/ipa/Tendlo.ipa`,
gesigneerd met Apple Distribution (Aqua-IT B.V.), `get-task-allow=false`,
`beta-reports-active=true`. **Er is niets geüpload** — dat blijft jouw handeling.

#### J1. Xcode heeft tijdens die build zelf certificaten aangemaakt ⚠️ meld ik expliciet

Vooraf bestond er alléén een Apple **Development**-certificaat. Automatic signing heeft
tijdens de proefbuild **zelf een distributiecertificaat en twee App Store-provisioning-
profielen** aangemaakt in je team (voor `com.aquait.tendlo` en `…​.ShareExtension`). Geen
portal geopend, geen handmatige ingreep — maar het is wél een wijziging in je Developer-
account en er is nu één distributiecert-slot in gebruik. Ik had opdracht gegeven géén
certificaten aan te maken; dit gebeurde als automatisch neveneffect van de build. Je hoort
het te weten in plaats van het later te ontdekken.

**Doe dit vóór iets anders:** exporteer de privésleutel als `.p12` (Xcode → Settings →
Accounts → Manage Certificates). Hij zit in Xcode's beheerde opslag en is níet zichtbaar
via `security find-identity`; op een schone Mac ben je hem anders kwijt.

#### J2. ⛔ De builds van vandaag geven iedereen gratis Plus, en sturen geen crashrapporten

Twee dingen die ik zelf heb geverifieerd en die de vijf toestellen van vandaag raken:

- `scripts/flutter-with-defines.sh:51` zet **onvoorwaardelijk** `PLUS_OVERRIDE=true`. Dat is
  tegelijk de enige route die de cloud-config goed zet. Gevolg: elke tester heeft Plus, en
  de entitlement-grens die sinds migratie 0042 server-side wordt afgedwongen blijft
  **onbeproefd**.
- `SENTRY_PA_DSN` staat niet in `~/.zprofile` (alleen AUTH_TOKEN/ORG/PROJECT), dus de
  define wordt niet meegegeven en er komt **geen crashrapportage** binnen — terwijl
  `OWNER-INPUTS-NEEDED.md` §1 aanneemt van wel.

Ik heb de DSN opgehaald uit je Sentry-project; dit is de regel die eronder moet:

```
export SENTRY_PA_DSN="https://44cda8446d4a8a987948453144965b3a@o106322.ingest.us.sentry.io/4510816013385728"
```

> **Correctie 27-07:** de DSN hierboven is **fout** — hij hoort bij het
> Sentry-project `byo` (id `4510816013385728`), niet bij Tendlo. Tendlo-crashes zouden
> daarmee in het BYO-project zijn geland. De juiste DSN van project `mijn-pa` is
> `.../4511564907347968`; die staat nu in het profiel. Zie besluitenlog-2026-07-27.md.

```sh
```

Ik heb je shell-profiel **niet** aangeraakt. Let op de `us` daarin: crashdata gaat naar de
Verenigde Staten, terwijl PostHog bewust op de EU staat. Dat is een doorgifte naar een
derde land en hoort in de privacyverklaring — en het is de vraag waard of je Sentry naar
een EU-regio wilt verplaatsen nu het nog goedkoop is.

Voor de storebuild is er inmiddels `scripts/build-store-ipa.sh`, die stopt bij ontbrekende
cloud-config en `PLUS_OVERRIDE` nooit meegeeft, plus `scripts/verify-store-ipa.sh` die het
**artefact** controleert in plaats van de commandoregel. Negatief getest (keurt de oude IPA
af) en positief (nieuwe build 1.0.0+1202 komt schoon door).

#### J3. Testers en Plus: geef een grant, zet enforcement niet uit

`plusEnforcementProvider` staat op `true`. Een tester zonder rij in `plus_entitlements`
loopt dus tegen een harde grens waar niets achter zit. Er is géén paywall die stukloopt
(`purchases_flutter` zit niet eens in `pubspec.yaml`), dus de Paid Apps Agreement en de nog
niet aangemaakte abonnementen **blokkeren TestFlight niet**.

**Mijn advies:** geef de testhuishoudens vooraf een admin-grant, en laat enforcement aan.
Anders test je de grens weg die je juist wilt valideren.

#### J4. Beslissingen

1. **Export-compliance** — de branch zet `ITSAppUsesNonExemptEncryption` op `false`. Dat is
   de standaardsituatie voor een app die alleen TLS, Keychain en LocalAuthentication
   gebruikt en geen eigen crypto meebrengt. Het is wél een verklaring op jouw naam, dus die
   laat ik aan jou. Zonder antwoord blijft elke upload op "Missing Compliance" staan.
2. **Privacy manifest: locatie en foto's/documenten erbij?** De agent heeft de reason-codes
   niet gegokt maar afgelezen uit het artefact (`sqlite3` → FileTimestamp + DiskSpace,
   `receive_sharing_intent` → UserDefaults). Twee categorieën zijn bewust opengelaten omdat
   ze een uitspraak over gegevensstromen vragen, geen codebevinding. **Advies:** beide
   toevoegen.
3. **Buildnummer** — voorstel is de commitcount (`git rev-list --count`, nu 1202). Al
   gebouwd; je hoeft alleen te bevestigen dat je deze systematiek wilt.
4. **CI-upload** — nog niet. De CI is ubuntu-only (analyze + test) en draagt niets bij aan
   het release-pad. Lokaal bouwen met de poort is voor de eerste rondes beter te overzien.
   Er is bewust géén pipeline gebouwd.

Het voorbereide werk staat op branch **`chore/testflight-voorbereiding`** (commit
`4e73efbc`, lokaal, niet gemerged): app-eigen `PrivacyInfo.xcprivacy` voor app én
ShareExtension, de Info.plist-verklaring, een script dat de manifesten in het Xcode-project
haakt, en `docs/release/testflight-ios.md`. Poort groen: analyze schoon, 3409 tests — gelijk
aan `main`. **Bewust niet gemerged**, omdat beslissing 1 en 2 erin verwerkt zitten.

### K. Privacy en App Store-metadata — vier dingen die eerst recht moeten

Concept-teksten staan klaar: privacylabels met onderbouwing, TestFlight-metadata inclusief
beta-beschrijving en reviewnotities, en een concept-privacyverklaring. Die laatste heeft een
waarschuwing bovenaan: het is een juridisch document en ik ben geen jurist — laat het
toetsen. Alle vier de stukken staan in de sessiemap (`appstore-privacy.md`,
`testflight-metadata.md`, `privacybeleid-concept.md`, `testflight-besluiten.md`).

Vier bevindingen die je moet zien vóórdat je een privacyverklaring publiceert. De eerste
drie heb ik zelf in de code nagelopen, want het zijn precies het soort claims dat je niet
op gezag moet overnemen.

**K1. Gezondheidsdata blijft niet op het toestel. ⛔**
`docs/architecture/integraties-en-privacy.md` §5 zegt: *"Health-reads blijven on-device; er
gaat niets naar Tendlo-servers of derden."* Voor de HealthKit-reads zelf klopt dat. Maar de
daaruit afgeleide rijen — `weight_entries`, `weight_goals`, `daily_step_counts`,
`step_goals` — staan wél in `lib/core/sync/powersync_schema.dart` én vier keer in
`powersync_cloud/sync-config.yaml`, en synchroniseren dus naar Supabase. Die zin zou als
onwaarheid in je privacyverklaring belanden. Kies: de sync beperken, of de documentatie en
de verklaring eerlijk maken. Dit raakt ook App Store-richtlijn 5.1.3.

**K2. Twee permissieteksten beloven iets wat de app niet doet.**
De spraaktekst zegt "spraakherkenning **op je toestel**", maar `onDevice: true` wordt
nergens gezet — ik heb de hele `lib/` doorzocht, geen enkele treffer. De agendatekst zegt
dat Tendlo afspraken "ook in je apparaat-agenda kan zetten", terwijl de koppeling read-only
is. Allebei kleine tekstwijzigingen, maar het zijn beloftes aan de gebruiker in een
systeemdialoog.

> **Opgevolgd 11 augustus 2026 (TEN-141 deel 1).** De agendatekst is eerlijk gemaakt in
> `Info.plist` en in de nl/en/de-varianten: Tendlo leest de apparaat-agenda en schrijft er
> niets naartoe. De sleutel zelf is bewust ongewijzigd gebleven — iOS kent geen alleen-lezen
> agendasleutel, en `NSCalendarsWriteOnlyAccessUsageDescription` is het spiegelbeeld (wel
> toevoegen, niet lezen) en zou de koppeling stukmaken. Het spraakpunt staat nog open.

**K3. Analytics en crashrapportage starten vóór toestemming.**
Goed nieuws tegenover `OWNER-INPUTS-NEEDED.md` §2: PostHog ís consent-gated —
`analyticsConsent` staat standaard op `false` en `ConsentGatedAnalytics` blokkeert events.
Maar `main.dart` initialiseert de SDK onvoorwaardelijk, dus bij elke koude start gaat er een
request (met IP) naar PostHog zonder toestemming. Sentry is helemaal niet consent-gated en
heeft geen `beforeSend`-filter, terwijl `debugPrint` in release breadcrumbs wordt — dat kan
gezinsdata in crashrapporten trekken.

**K4. De AI krijgt meer context mee dan gedocumenteerd.**
Niet alleen gefotografeerde documenten: de assistent stuurt de vraag mét gezinscontext —
namen van gezinsleden, tot 80 komende afspraken met titel, tijd en locatie, 80 open taken
met toegewezen persoon, boodschappen, maaltijden en voorraad. Financiële en
gezondheidsgegevens zijn wél uitgesloten. Er wordt nergens een zero-data-retention-header
meegestuurd. Dat hoort expliciet in de verklaring, en de retentie-instelling bij OpenRouter
is het nakijken waard.

**Wat níet blokkeert voor TestFlight, wel voor de App Store:** er is geen
accountverwijdering in de app (richtlijn 5.1.1(v)), er zijn geen bewaartermijnen of
opschoning, en bijlagen in Storage zijn per huishouden afgeschermd in plaats van per
eigenaar.

**Datacategorieën die volgens het onderzoek aangevinkt moeten worden:** contactgegevens
(e-mail, naam), gezondheid en fitness, financiële gegevens, globale locatie,
gebruikersinhoud (foto's), identifiers (gebruikers-id), gebruiksdata en diagnostiek. Geen
tracking — er is geen advertentie- of attributie-SDK en geen IDFA. Aankopen voorlopig níet,
want de RevenueCat-SDK zit nog niet in `pubspec.yaml`.
