Changelog
Elke release, met datum en tijd.
Gegenereerd uit onze eigen releaseaantekeningen, nieuwste eerst. Geen ClickUp-login nodig.
- v1.0.12
Pricing: one plan, two commissions
From 13 October 2026 an agency is either on a 30 day trial or on the one plan, Buyerbase, at € 99 a month or € 990 a year (ten months), excluding VAT. Every feature and every former add-on is included, members are unlimited, and the Starter, Agency and Enterprise tiers, the seat prices and the add-on prices are gone. Two commissions replace them: 2.9 percent of the agency's fee when it acted for the buyer on a completed sale, and 0.01 percent of the sale price, at most € 200 a house, when it acted for the seller (0.03 percent, at most € 500, from the eleventh subscribing agency on; the first ten keep their rate while they stay subscribed). The lifetime deal is € 7.500 once and is no longer sold on the website. The plan includes 300 credits a month.
- v1.0.7
Buyer onboarding: the five-step first-login flow
A buyer who signs in for the first time and holds no preference set lands on /portal/start, answers five steps (Type, Location, Requirements, Details, Let's go) and on the last one a single server action writes the first preference set, the buyer's name and the mobile number, then opens /portal/houses. Nothing is written before step 5.
- v1.0.4
Security page, responsible disclosure and the pentest
The page the first agency with a compliance officer asks for, behind security_page (0% rollout at merge, PostHog id 275118). /security prints where buyer data lives straight from src/lib/privacy/subsystems.ts, so only verified regions appear and Resend, iDIN and WhatsApp sit in a "Not confirmed yet" block rather than under an EU claim. Below it: the responsible disclosure policy with its response targets (acknowledgement in 72 hours, a triage verdict in 10 days), what is in and out of scope, safe harbour, the pentest state, and an explicit "What we do not claim" list naming the absence of ISO 27001 and SOC 2. Retention stays a policy, never a number. Off, the page 404s, the same fail-closed convention as the v1.0.3 pages.
- v1.0.3
Docs, changelog and a public roadmap
The roadmap becomes a published page rather than a ClickUp share link, behind public_roadmap. /changelog renders one card per RELEASE.md section, each dated and timed in Europe/Amsterdam from the commit that added the section (this environment's git proxy refuses real tag pushes, so RELEASE.md sections stand in for tags; see the comment atop src/lib/web/changelog.ts). /roadmap lists the upcoming build-plan items still open in docs/session-prompts/v0.6-to-v1.0.md, and /docs links out to the existing help centre and API docs rather than inventing new content. Off, all three routes 404. nl equivalents ship alongside the English routes. New e2e coverage in tests/e2e/web-changelog.spec.ts; no schema change. PR #167, merged 2026-09-13 01:00 CEST. Score ~90 (target 88).
- v1.0.2
Enterprise as a quote, not a plan
The Enterprise pricing column stops pretending to be a self-serve plan: behind enterprise_quote (0% rollout at merge), its CTA routes to a new /enterprise page instead of the generic /contact form. That page lists Enterprise's scope with each line tagged "Ships today", "In progress", or "Built to order" so the page stays honest about what exists, backed by a one-page audit at docs/cowork/product/enterprise-scope.md (Agency feature set and data export/API are real today; 2FA and SLA are partial; multi-office and a dedicated hosting region are aspirational). The quote form reuses the founding-waitlist spam guard (honeypot, dwell-time token, per-IP rate limit) and writes into the existing leads table rather than a new one, tagged kind = 'enterprise_quote' with office count and seat estimate captured. With the flag off, the pricing page's Enterprise CTA still points at /contact exactly as before. Score ~90 (target 88), PR #165.
- v0.9.9
National listing data, decided not built
ADR 005 (docs/decisions/adr-005-national-listing-data.md): brainbay through NVM, a licensed provider like Matrixian, or staying on the v0.6.1 link importer, compared on coverage and integration shape (no cost figures exist anywhere in this repo for brainbay's or Matrixian's actual pricing, so none are invented). Decision: stay on the link importer through 2027; budget a brainbay/NVM integration evaluation for late 2027 as a Q1 2027 discovery spike, not a committed build. Matrixian-style licensed data stays out of scope regardless of timing, priced for a larger operation than exists yet. No code changes, no flag; this subtask is the ADR itself. Score n/a (documentation only), PR #163.
- v0.9.8
Mortgage advisor connection
A connection, not a marketplace: no ranking, no directory, no fee taken. A buyer invites an external mortgage advisor from /portal/advisor and picks which document types they may see; the advisor signs in with the same magic-link mechanism the buyer portal already uses (/advisor/sign-in, /advisor/confirm) and reads exactly the approved documents plus the buyer's afronding timeline at /advisor, nothing else. RLS backs every read: current_advisor_buyer_id() (mirroring current_buyer_id()) resolves the signed-in advisor's one active connection; new policies on documents and afronding_milestones key off it. A revoked connection loses access immediately. The 4.6 document_access.test.sql suite gained an advisor-role case (approved type visible, unapproved type invisible, revoked connection sees nothing), its RLS logic verified directly against staging before being committed. Behind advisor_connection (PostHog id 275022, 0% rollout at merge). Score 90 (target 90), PR #161.
- v0.9.7
Dutch native read and the mobile performance budget
The two genuinely manual pieces (a native speaker reading 3225 rows, a browser hitting a live deployment) are still manual, but everything that can be built ahead of them now is: the export tool, the four already-flagged corrections, and a tightened signed-in axe bar. Score 90 (target 90), PR #170.
- v0.9.6
Audit log export
A card on Settings > Privacy ("Export the audit log", owners and admins only, behind audit_export) with a from/to date form that GETs /settings/audit-export, a route handler mirroring the v0.8.7 reports-export pattern: it requires an admin viewer, checks the flag, queries agency_audit_log scoped to the agency and the date range, resolves each actor_id to a member name, and streams RFC 4180 CSV. The export itself writes a row to the same log (audit_log_exported), so handing the log to a compliance officer is itself in the log. Behind audit_export (PostHog id 275015, 0% rollout at merge). Score 88 (target 88), PR #159.
- v0.9.5
Buyer-side deletion and AVG requests
A portal page (/portal/account) with two AVG (GDPR) actions, both behind buyer_deletion: "Request my data", a buyer-scoped export reusing the v0.5.1 export_jobs pipeline (src/lib/export/buyer.ts, now with a nullable buyer_id), run inline and returned as a signed download URL; and "Delete my account", which opens a buyer_deletion_requests row with a 30 day clock (src/lib/deletion/server.ts). On Settings > Privacy, a new "Deletion requests" card lists pending requests with their due date; an owner or admin processes a request (anonymises the buyer's personal fields, removes their documents from Storage and the documents rows, keeps agency_audit_log) or refuses it with one of four fixed legal reasons (ongoing transaction, Wwft retention, legal dispute, other). Every step is audited (buyer_data_export_requested, buyer_deletion_requested, buyer_deletion_refused, buyer_deleted). The DPA body now names the mechanism and the 30 day window so the legal text matches the product. Behind buyer_deletion (PostHog id 275014, 0% rollout at merge). Score 90 (target 90), PR #157.
- v0.8.9
Public Wat kan ik kopen page
A public, white-labelled mortgage-estimate page per agency at /wat-kan-ik-kopen/[slug], resolving the agency by slug the same way the v0.9.3 embed widget does and failing closed (404) when the agency is unknown or the wat_kan_ik_kopen flag is off. Reuses the v0.8.8 computeMaxMortgage module directly (no duplicated NIBUD/NHG logic): a visitor enters gross annual income and existing monthly debt, sees the estimated maximum mortgage, and can continue into an intake form that creates a buyer, tagged lead_source = wat_kan_ik_kopen, with the calculated estimate saved unconfirmed against it through saveMortgageCalculation. The intake step carries the same honeypot, signed dwell-time token and per-IP rate limiting as the embed widget's public form, writes an agency_audit_log row and an activity_events row on success, and fires PostHog funnel events (wat_kan_ik_kopen_calculated, wat_kan_ik_kopen_lead_created, wat_kan_ik_kopen_lead_rejected). Behind wat_kan_ik_kopen (PostHog id 275001, 0% rollout at merge). Score 90 (target 88), PR #155. This closes v0.8.0 The assistant and the app: all nine subtasks (v0.8.1-v0.8.9) are now tagged.
- v0.8.8
Mortgage calculator in the portal
NIBUD 2024 financieringslastpercentage table and the NHG cost limit as a plain data module (src/lib/mortgage/nibud-2024.ts, ten bracket boundaries sampled from the published table with linear interpolation between them), feeding a pure computeMaxMortgage function (src/lib/mortgage/calculator.ts) that returns the maximum mortgage, the NHG cap state, and the assumptions it was computed under (year, interest rate, term, financieringslastpercentage, NHG eligibility). Saved as a mortgage_estimates row per calculation on the buyer record, kept as history rather than overwritten so a recalculation never loses the previous number; a new MortgageCalculatorCard on the buyer overview tab lets the advisor enter income and existing monthly debt, see the estimate, and confirm it (confirmed_at/confirmed_by), distinct from the pre-existing manual mortgage_max_estimate field on the buyer. Behind mortgage_calculator (PostHog id 274985, 0% rollout at merge). Score 92 (target 88), PR #153.
- v1.0.1
Public pricing live and self-serve
The self-serve checkout path (pricing CTA → appBillingUrl → /settings/billing?plan=... → createPlanCheckoutSession → Stripe Checkout) predates this subtask and was already fully wired end to end: automatic_tax and tax_id_collection are already enabled on the Checkout Session for EU VAT and reverse charge, and dunning already runs on Stripe's own retry schedule (past_due while retrying, unpaid or canceled flips the agency to suspended in agencyStatusFor, with billingPaymentFailedEmail sent from the webhook). The live/test key switch is a pure STRIPE_SECRET_KEY env var change (stripeConfigured, stripeTestMode), no code path differs.
- v0.8.7
Reporting exports
Four owner reports, computed from source tables rather than a single rollup: closings from bids (outcome = won, dated by koopakte_date), revenue per employee from paid fee_invoices, time in stage paired from consecutive activity_events stage transitions per buyer, and conversion by source from buyers.lead_source against whether the buyer ever won a bid. Each report has a date range filter, an in-app table, and a CSV export; export writes an agency_audit_log row (report_exported). Behind reporting_exports, off for every agency at merge. Score 88 (target 88), PR #149.
- v0.9.4
Help centre and in-app support
Public /help (English and Dutch) with one article per app screen (HELP_ARTICLES in src/lib/help/articles.ts), the published first-response commitment ("we reply to every support message within one working day"), also added to /terms section 7, and a support form that emails hello@ with the agency, page and message (no database write). The app shell's help button (mobile drawer and desktop sidebar) deep-links to the article matching the current screen. Behind help_centre (PostHog id 274927, 0% rollout at merge); off keeps the old plain mailto: link. Score 91/100 (target 88), PR #147.
- v0.8.6
Smart matching
Every new house is scored against every active buyer in the agency, and a buyer's houses are rescored whenever their like/pass history changes. Formula: base 60 if buyerMatchesProperty's hard constraints (budget, type, room counts, area) fit, 15 if not, then ±5 per shared dimension (property_type, neighbourhood) with each past house_signals row, capped at ±30 total, clamped to 0-100 (full reasoning in docs/decisions.md). Scores land in a new buyer_property_match_scores table (distinct from the advisor-curated buyer_property_matches pipeline) and drive: the portal house order, the property record's matching-buyers list (score-ordered, match badge), the buyer record's new Top matches card, and a new agency-wide /matches inbox (score >= 60) linked from the Properties list header. Behind smart_matching (PostHog id 274919, 0% rollout at merge). Score 90/100 (target 90), PR #145.
- v0.8.5
Weekly what-changed digest
A Monday 06:00 UTC cron (/api/cron/weekly-digest) sends one email per advisor per agency with the flag on: buyers they own who moved stage, got a portal signal, have a viewing or a task due in the surrounding 7 days. Reuses the same tables as the v0.8.4 AI buyer summary context, aggregated per advisor rather than per buyer. Opt out per advisor through the existing notification centre preferences (new weekly_digest event, email-only default). Behind weekly_digest (PostHog id 274910, 0% rollout at merge). Score 90/100 (target 88), PR #143.
- v0.8.4
AI buyer summary
One AI-written paragraph per buyer record on where they stand and what is blocking them, built server-side from stage, portal signals, viewings, tasks and recent activity (never document contents), with a regenerate control and a clear AI label. Cached in a new ai_buyer_summaries table keyed by a hash of that context, so an unchanged record never re-calls the model. Behind ai_buyer_summary (PostHog id 274902, 0% rollout at merge). Calls are metered per agency through the existing request-metering counters (ai:buyer_summary:<agencyId>). Score 92/100 (target 88), PR #141.
- v0.9.3
Embeddable Find your home with us widget
A script tag and an iframe variant, white-labelled, opening buyer intake on the agency's own site. Behind embed_widget, score 89/100 (target 88, docs/scorecards/v0.9.3-1.md), PR #139.
- v0.9.2
First-run checklist and product tour
A new agency's first minutes: five setup steps ticked from real data, and a dismissable five step tour. Behind first_run_tour.
- v0.9.1
Signup, agency creation and trial
No super admin in the path: a stranger reaches a working trial agency and the existing Stripe checkout (v0.4.3) without a platform member ever touching it. Behind self_serve_signup.
- v0.8.3
Tasks and next actions
- buyer_tasks: one task per buyer with an owner, a due date, a done state and a source (manual or automation). Carries agency_id, RLS, the platform-admin read and the suspended-agency restrictive policy every other table carries. The member policy additionally requires current_member_role() is not null, so a buyer session inside the same agency never reads advisor tasks: this is internal work and the portal has no policy on the table at all. - The partial unique index on (buyer_id, template_id) is what makes the automation idempotent. Entering the same stage twice never produces a second copy of a standard task. Manual tasks carry a null template_id and are therefore never deduplicated. - src/lib/tasks/templates.ts is the single catalogue: the standard task list per pipeline stage, pure and unit-tested. Ten pipeline stages carry tasks; the terminal and side stages deliberately carry none, so archiving a buyer creates nothing. Template ids are stable dedup keys and must never be renamed; titles can change freely. - src/lib/tasks/server.ts: syncStageTasks creates the standard tasks for the stage a buyer has just entered and closes the still-open automation tasks belonging to a stage they have left. Manual tasks are never touched, and a task whose template has left the catalogue is left for the advisor rather than silently closed. It never throws, so it cannot break the stage change that called it. - Wired into both paths that move a buyer: the manual stage menu (setBuyerStage) and the stage automation, in the latter case only after the guarded SQL move actually took. - Surfaces: My day lists every open task the member owns plus what they closed today, with overdue and due-today counts and an optimistic tick; the buyer record gets a card that adds, ticks and deletes. task was added to the activity_events kinds so a task event is never mistaken for a note somebody typed. - Tasks join the agency data export. - With advisor_tasks off nothing is fetched, nothing is rendered, and syncStageTasks returns immediately, so a stage change behaves exactly as it did before this subtask. The server actions re-check the flag, so a stale page cannot write past a switched-off flag. - Migration supabase/migrations/20260912080000_advisor_tasks.sql, applied to staging and production this session; src/lib/supabase/types.ts regenerated from staging. Supabase security advisors report no new findings. - Behind flag advisor_tasks (fallback false, 0% rollout at merge, PostHog id 274819). - Score: 91 (docs/scorecards/v0.8.3-1.md). PR #133, merged as d23eae3c41f9a1084e336527b4e04e186262d1f7. No env vars added or changed.
- v0.8.2
Notification centre
- Two tables. notifications is the in-app inbox: one row per event per person, with a read state. notification_preferences is the matrix the senders consult: one row per person, per event type, per channel. Both carry agency_id and RLS that scopes a row to the single person it belongs to (member_id = auth.uid() or buyer_id = current_buyer_id()), plus the platform-admin read and the suspended-agency restrictive policy every other table carries. notifications grants update (read_at) only, so a reader can mark something read and cannot rewrite what they were told. - src/lib/notifications/events.ts is the single catalogue: each event's audience, its possible channels and its defaults. A stored preference row wins over the default in both directions, an absent row falls back to the default, a channel the event does not support is off whatever a stale row says, and an unknown event is off. Saving writes every cell, defaults included, so changing a default later cannot rewrite a choice somebody already made. The catalogue lists only events a sender actually emits. - src/lib/notifications/server.ts: channelAllowed (the gate every sender calls), notify (the inbox row, never throwing so a failed row cannot fail the email carrying the same news), plus the list, count, mark-read and preference-save helpers. - Surfaces: a bell with an unread count in the agency shell and in the buyer portal; /notifications and /portal/notifications with mark one read and mark all read as plain forms; the grid on a new Settings > Notifications tab and on portal preferences (where it replaces the single reminder toggle while the flag is on). - Routed through the grid: viewing confirmations, viewing reminders (buyer and advisor), afronding deadlines (buyer and advisor), early-access match emails, review requests, viewing requests, document uploads and portal signals. resolveBuyerChannel also asks the grid at scheduling time, so a reminder is never scheduled onto a channel the buyer switched off. With the flag off, channelAllowed returns true and notify writes nothing, so every sender behaves exactly as before. - Migration supabase/migrations/20260912060000_notification_centre.sql, applied to staging and production this session; src/lib/supabase/types.ts regenerated from staging. - Behind flag notification_centre (fallback false, 0% rollout at merge, PostHog id 274813). - Score: 91 (docs/scorecards/v0.8.2-1.md). PR #131, merged as bbba978efa1177bb30522367842082b5bd09e105. No env vars added or changed.
- v0.8.1
Portal as an installable PWA with push
- Buyer portal installs as a white-label PWA: /portal/manifest.webmanifest (route handler, not the manifest.ts file convention, which only resolves at the app root) reads the agency's name, colour and logo from getPortalContext(); the shell layout's generateMetadata points at it only when portal_pwa is on for the buyer's agency, keeping the flag off behaviour identical to before (manifest: null). - public/sw.js: caches /portal/offline as a minimal offline shell for navigation requests, and turns a Web Push message into a notification (push/notificationclick handlers) that opens or focuses the portal on the reminder's URL. - PwaInstallPrompt (src/components/app/pwa-install-prompt.tsx), shown in the portal shell only behind portal_pwa: listens for beforeinstallprompt to offer Install, and separately (already installed, or a platform that never fires that event) offers to enable push notifications; both dismiss permanently to localStorage once answered. - New push_subscriptions table (agency_id + buyer_id + RLS), one row per device; src/lib/push/server.ts (save/remove/send, VAPID via web-push) and src/lib/push/client.ts (browser subscribe helper) mirror the shape of whatsapp_connections/src/lib/whatsapp/* from v0.7.7. API route src/app/api/portal/push/subscribe/route.ts (POST/DELETE), buyer-authenticated through getPortalContext(). - viewing_reminders.channel widens to ('email', 'whatsapp', 'push'); resolveBuyerChannel in src/lib/viewings/reminders.ts now checks push first (VAPID configured, portal_pwa on, an active subscription), then whatsapp, else email. src/lib/viewings/reminder-server.ts sends a push notification through sendPushToBuyer for channel: "push" rows, marking the row failed if every device is gone. - Migration supabase/migrations/20260912040000_portal_pwa.sql. Applied to staging and production this session, along with the still-unapplied v0.7.7 migration 20260912030000_whatsapp_channel.sql found in both databases (see docs/decisions.md); src/lib/supabase/types.ts regenerated from staging afterwards. - Behind flag portal_pwa (fallback false, 0% rollout at merge, PostHog id 274801). - Score: 91 (pnpm check green: typecheck, lint, lint:strings, 739 unit tests including tests/unit/push-client.test.ts; pnpm build green, confirming /portal/manifest.webmanifest and /portal/offline both resolve).
- v0.7.7
WhatsApp for reminders and confirmations
- viewing_reminders gains a channel column (email or whatsapp), decided per row at scheduling time (resolveBuyerChannel in src/lib/viewings/reminders.ts): whatsapp only when whatsapp_channel is on, the buyer has a verified phone (buyers.whatsapp_verified_at), and the agency has a live connection; email otherwise. Advisor rows stay email in this subtask; the shared inbox is out of scope. - Settings > Integrations gains a WhatsApp Business API card (owner/admin only): phone number ID, access token, business account id and display number, sealed with the existing INTEGRATIONS_ENCRYPTION_KEY AES-256-GCM helper (src/lib/imports/crypto.ts, reused rather than duplicated) and never granted to authenticated. - Portal preferences gains a WhatsApp opt-in card, shown only when the agency has a live connection: the buyer enters a phone number, a 6-digit code is sent over WhatsApp (10 minute expiry, 5 attempts), and on success buyers.phone/whatsapp_verified_at are set. - The cron sender (src/lib/viewings/reminder-server.ts) branches on row.channel: whatsapp sends through the WhatsApp Cloud API (src/lib/whatsapp/client.ts), email keeps the existing Resend path; both mark sent/failed the same way. - New tables whatsapp_connections (one per agency) and phone_verifications (service-role only), migration supabase/migrations/20260912030000_whatsapp_channel.sql. Not yet applied to staging or production, left for the migration-applier routine. - Decision recorded in docs/decisions.md: the WhatsApp Cloud API (Meta) direct, not a BSP, so each agency uses its own Business Manager number with no per-message markup. - Meta added to src/lib/privacy/subsystems.ts (region not yet verified, left null). - Behind flag whatsapp_channel (fallback false, 0% rollout at merge). - Score: 89 (pnpm check green: typecheck, lint, lint:strings, 737 unit tests including tests/unit/whatsapp-server.test.ts; tests/e2e/whatsapp-channel.spec.ts added, env-gated like the other Playwright specs). - PR #127, merged as 42649b537c0b88d516db81576ba4d4f003b21c70. Closes v0.7.0 (Bid to keys): all seven subtasks (v0.7.1 to v0.7.7) are now tagged.
- v0.7.6
Review request at Contract signing
- Agency settings gained a Google review link and a Funda profile link field, with a live preview of the email a buyer receives (ReviewRequestCard at /settings). - Reaching contract_signing (a manual stage change or an automated one via apply_stage_rule) queues one review request email, sent 3 days later by a new cron pass at /api/cron/review-requests (vercel.json, every 15 minutes, mirroring afronding-reminders). unique(buyer_id, channel) keeps it to once per buyer even if the stage is re-entered. - A token-signed one-click opt out at /portal/unsubscribe/review-requests (purpose-scoped, so it cannot be confused with the existing viewing_reminders unsubscribe link) sets buyers.review_request_opt_out; the cron pass checks it, and the buyer's current stage, before every send, cancelling rather than sending when either has changed since the row was queued. - Logs an activity_events row (type: "email") on every send. - New review_requests table (agency_id + RLS), plus agencies.google_review_url, agencies.funda_profile_url and buyers.review_request_opt_out, migration supabase/migrations/20260912020000_review_requests.sql. Applied to staging and production; src/lib/supabase/types.ts regenerated from production. - Also applied the previously-unapplied v0.7.5 fee_invoice migration to production (staging already had it) to fix a schema-drift blocker found while regenerating types, and regenerated types again afterwards. - WhatsApp is left out: whatsapp_channel (v0.7.7) does not exist in this codebase yet. queueReviewRequest only ever inserts the email channel row; a WhatsApp insert alongside it is called out in a comment for whoever builds v0.7.7. - Behind flag review_requests (fallback false, 0% rollout at merge, PostHog id 274797). - Score: 92 (pnpm check green: typecheck, lint, lint:strings, 730 unit tests, including mocked-admin-client coverage of queueing, the opt-out suppression, and the stage-changed-since-queued suppression; tests/e2e/review-requests.spec.ts added, env-gated like the other Playwright specs in this repo). - PR #125, merged as c24d750918b07c635a4f08f7ab14822c16e6e16c.
- v0.7.5
Fee invoice at Bid won
- New fee_invoices table (agency_id + RLS): one row per Stripe Invoice drafted from a won bid, status moves draft -> sent -> paid/void and never back, migration supabase/migrations/20260911150000_fee_invoice.sql. - On Bid won: an invoice draft from the fee agreement amount, the buyer as Stripe customer, the closing employee recorded; the owner reviews and sends the hosted invoice from the buyer record. - Stripe webhook marks an invoice paid; the admin dashboard's revenue per employee reads from paid invoices when fee_invoice is on, falling back to the manual field when it is off. - Audit rows on draft, send and paid. - Behind flag fee_invoice (fallback false, 0% rollout at merge). - Score: 90+ (pnpm check green: typecheck, lint, lint:strings, 717 unit tests; tests/e2e/fee-invoice.spec.ts covers draft and send; CI's SQL suite caught a missing fee_invoices entry in tenant_isolation.test.sql's census, fixed on the run branch before merge). - PR #123, merged as db8bf5010a9efed5d0435259c507ad2b09411066.
- v0.7.4
ID verification
- iDIN chosen over Vidua (ADR docs/decisions/adr-004-idin-vs-vidua.md): buyers already bank with participating NL banks, iDIN never returns a raw ID image by construction, cheaper and simpler integration than Vidua's document-scan flow. - New id_verifications and id_verification_audit_log tables (agency_id + RLS, mirrors the koperspakket/bid-record patterns), migration supabase/migrations/20260911130000_id_verification.sql. - Portal sandbox verification flow, agency-side buyer record status card with re-verify and manual override (reason required, audited), koperspakket now shows verified ID state. - iDIN added to the processor table (src/lib/privacy/subsystems.ts) and named on the public privacy page. - Behind flag id_verification (fallback false, 0% rollout at merge). - Score: 92 (checks: typecheck, lint, unit tests, build all green; SQL migration test suite green after fixing a hardcoded document-type count in supabase/tests/portal_isolation.test.sql; tests/e2e/id-verification.spec.ts not runnable against staging from this sandbox). - PR #113, merged as 560bfe40bc5695190e8804f3d519f5b46315e29b.
- v0.7.3
The afronding timeline
Run autonomously on 2026-09-11, PR #111. Flag afronding_timeline, created in PostHog at 0% rollout at merge (id 273855). pnpm check (typecheck, lint, lint:strings, 710 unit tests) and pnpm build both green on the merged tree. Migration 20260911060000_afronding_timeline.sql not applied to any database from this sandbox (no Supabase CLI, no reachable staging or local Postgres) — see manual steps.
- v0.7.2
Koperspakket PDF
Run autonomously on 2026-09-11. Flag koperspakket, created in PostHog at 0% rollout at merge (id 273850). pnpm check (typecheck, lint, lint:strings, 700 unit tests) green on the merged tree. Migration applied to buyerbase-staging; src/lib/supabase/types.ts regenerated from that project. PR #109.
- v0.7.1
Bid record per house
Run autonomously on 2026-09-10/11. Flag bid_records, created in PostHog at 0% rollout at merge (id 273792). pnpm check (typecheck, lint, lint:strings, 689 unit tests) green on the merged tree. Migration applied to staging and production; src/lib/supabase/types.ts regenerated from production.
- v0.6.7
Pricing around cost per closing
Run autonomously on 2026-09-10/11. Flag pricing_per_closing, created in PostHog at 0% rollout at merge (id 273783). pnpm check (typecheck, lint, 684 unit tests) and pnpm run build are both green on the merged tree. No Supabase CLI or live staging project is reachable in this sandbox, but this subtask touches no database: it is a website-only variant read straight from src/lib/web/pricing.ts.
- v0.6.6
The switching kit
Run autonomously on 2026-09-10. Flag switching_kit, created in PostHog at 0% rollout at merge (id 273776). pnpm check (typecheck, lint, lint:strings, 682 unit tests) is green on the merged tree. No Supabase CLI or live staging Supabase project is reachable in this sandbox (supabase is not installed here), so the migration and the fixture imports were verified at the pure-engine level with unit tests rather than against a running database; see manual steps.
- v0.6.5
Fee agreement signed in the portal
Run autonomously on 2026-09-10. Flag fee_agreement_esign, created in PostHog at 0% rollout at merge (id 273763). pnpm check (typecheck, lint, lint:strings, unit tests) and pnpm build both green; migration applied to staging and production; types regenerated for real from production.
- v0.6.4
Viewing pack and viewing report
Run autonomously on 2026-09-10. Flag viewing_pack, created in PostHog at 0% rollout at merge. Self-score 91/100 (target 90): pnpm check (typecheck, lint, lint:strings, unit tests) and pnpm build both green; migration applied to staging and production.
- v0.6.3
Viewing reminders
Run autonomously on 2026-09-10. Flag viewing_reminders, off (0% rollout) in PostHog at merge. Score: pnpm check (typecheck, lint, lint:strings, 663 unit tests including the new scheduling-maths suite) green, migration applied to staging and production; treated as meeting the 90 target on that basis (no self-score rubric was run this session).
- v0.6.2
Like, pass and a reason
Run autonomously on 2026-09-10, PR #93 (squash merge ae658d1f6dfd849f7dcd9ed2680a3ce87460df8e). Flag portal_signals, off (0% rollout) in PostHog at merge. Score: pnpm check and pnpm build green, migration applied to staging and production, unit tests for the formula pass; treated as meeting the 90 target on that basis (no self-score rubric was run this session).
- v0.6.1
Link importer
Run autonomously on 2026-09-10. Flag link_importer, created off (0% rollout, agency group) in PostHog at merge. Score 90 (target 92).
- v0.4.2
Product improvements
Session 26, 2026-09-05. Seven items, seven scorecards (docs/scorecards/v0.4.2-1.md to -7.md, summary in v0.4.2-summary.md). Items 1 to 3 shipped through PRs #38, #39 and #40; items 4 to 7 were finished in a second window on the session branch claude/v0.4.2-product-improvements-1t2g0g, which carries one PR into main. The session ran in a cloud container: SQL tests on a local Postgres 16 through scripts/db-test-ci.sh, Playwright against the staging project through a local dev server (the service role key and the Mapbox token were not available, so the checks that need them are listed as manual steps rather than claimed).